slice 7: PBS recovery-code escrow creation (agent, Phase B) (v0.9.0 wip)
internal/escrow: zero-knowledge escrow creation. R = 10 EFF-wordlist words (crypto/rand, ~129 bits); wrap K under R via PBS-native key change-passphrase driven over a stdlib pty (x/sys/unix; output discarded so R can't leak, F-A2); self-verify the blob recovers K (fingerprint match) before shipping. Opt-in (b) R-wrapped offline copy + (a) raw paperkey. Live K is byte-unchanged (operates on a copy). --selftest=escrow-create (-storage/-paperkey/-offline/-upload). Posture config field (zero_knowledge default). PBSEncKeyPath helper. Grounded by the escrow spike findings. Tests: R entropy>=128/format/uniqueness; integration round-trip (wrap->unwrap fingerprint match, wrong-R fails, K byte-unchanged) guarded to linux+pbc. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -28,9 +28,20 @@ type Config struct {
|
||||
Hub HubConfig `json:"hub"`
|
||||
Storage StorageConfig `json:"storage"`
|
||||
Backup BackupConfig `json:"backup"`
|
||||
Escrow EscrowConfig `json:"escrow"`
|
||||
LogLevel string `json:"log_level"` // debug|info|warn|error (default info)
|
||||
}
|
||||
|
||||
// EscrowConfig configures PBS recovery-code escrow creation (slice 7, doc 03 §8a). Enrollment-time
|
||||
// only (not the steady-state daemon). The default posture is zero-knowledge (Felhom holds the
|
||||
// opaque blob, the customer holds the recovery code).
|
||||
type EscrowConfig struct {
|
||||
// Posture is the key-custody posture; "" → zero_knowledge (the only one implemented this slice).
|
||||
Posture string `json:"posture"`
|
||||
// PBSStorageID is the pbs storage whose client encryption key is escrowed (e.g. "felhom-pbs").
|
||||
PBSStorageID string `json:"pbs_storage_id"`
|
||||
}
|
||||
|
||||
// BackupConfig tunes the slice-6 backup + self-restore-test layer. The restore-test runs on
|
||||
// an agent-internal cadence (no hub policy needed — it's self-validation); the backup
|
||||
// schedule/retention/target-selection policy is hub-manifest-owned and unfed until slice 10.
|
||||
@@ -91,11 +102,20 @@ func (b BackupConfig) PBSVerifyCadence() time.Duration {
|
||||
|
||||
// PBSSecretPath returns the path to a pbs storage's token-secret file.
|
||||
func (b BackupConfig) PBSSecretPath(storageID string) string {
|
||||
dir := b.PBSSecretDir
|
||||
if dir == "" {
|
||||
dir = "/etc/pve/priv/storage"
|
||||
return b.pbsSecretDir() + "/" + storageID + ".pw"
|
||||
}
|
||||
|
||||
// PBSEncKeyPath returns the path to a pbs storage's CLIENT ENCRYPTION KEY (K) file — the key the
|
||||
// escrow wraps (slice 7). PVE stores it alongside the token secret as <id>.enc, 0600 root.
|
||||
func (b BackupConfig) PBSEncKeyPath(storageID string) string {
|
||||
return b.pbsSecretDir() + "/" + storageID + ".enc"
|
||||
}
|
||||
|
||||
func (b BackupConfig) pbsSecretDir() string {
|
||||
if b.PBSSecretDir == "" {
|
||||
return "/etc/pve/priv/storage"
|
||||
}
|
||||
return dir + "/" + storageID + ".pw"
|
||||
return b.PBSSecretDir
|
||||
}
|
||||
|
||||
// ScratchBand returns the effective [min,max] scratch VMID band (defaults applied).
|
||||
|
||||
Reference in New Issue
Block a user