R-879: seal box API keys, owner passphrases, controller keys and PBS-DR tokens at rest
hosts.api_key, customer_configs.api_key / retrieval_password and host_pbs_secrets.value now hold the R-821/R-133 seal (enc:v1:, OFFSITE_SECRET_KEY). The two API keys get an api_key_hash lookup twin (SHA-256, backfilled keyless in migrate()), so box authentication never needs the sealing key; a row with no hash is matched on its plaintext only while it is plaintext. SealLegacyBoxSecrets seals legacy rows at start-up (idempotent, non-fatal). A sealed value that does not open sets SecretsUnreadable: serve/compare paths answer 500, saves refuse the record, the PBS token is not burned. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -208,6 +208,13 @@ func main() {
|
||||
} else {
|
||||
logger.Printf("[INFO] console passwords sealed at rest (%d legacy plaintext row(s) sealed now)", n)
|
||||
}
|
||||
// R-879: the box API keys, owner passphrases, controller API keys and PBS-DR tokens — same key,
|
||||
// same seal. A failure part-way is logged, never fatal: boxes still authenticate (hash lookup).
|
||||
if n, serr := dataStore.SealLegacyBoxSecrets(); serr != nil {
|
||||
logger.Printf("[ERROR] sealing legacy box secrets failed after %d value(s): %v", n, serr)
|
||||
} else {
|
||||
logger.Printf("[INFO] box secrets sealed at rest (%d legacy plaintext value(s) sealed now)", n)
|
||||
}
|
||||
}
|
||||
logger.Printf("[INFO] Database opened at %s", dbPath)
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// R-879 seam wiring: main() must CALL SealLegacyBoxSecrets, or every box key, owner passphrase and PBS
|
||||
// token written before the change stays in plaintext in hub.db. RED-PROOF: comment the call out → FAIL.
|
||||
func TestR879_MainSealsLegacyBoxSecrets(t *testing.T) {
|
||||
f, err := parser.ParseFile(token.NewFileSet(), "main.go", nil, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
found := false
|
||||
ast.Inspect(f, func(n ast.Node) bool {
|
||||
if c, ok := n.(*ast.CallExpr); ok {
|
||||
if sel, ok := c.Fun.(*ast.SelectorExpr); ok && sel.Sel.Name == "SealLegacyBoxSecrets" {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
if !found {
|
||||
t.Fatal("cmd/hub/main.go never calls SealLegacyBoxSecrets — legacy box secrets stay in plaintext")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user