fix(P15): file-mode audit expansion (REQ-130, F13)

---ci---
project: orca
phase: 15
milestone: v0.12
status: execute
---/ci---

EnforceFileModes now checks SSH key, known_hosts, master.key,
master.key.sealed, server.key (0600) + orca_ssh_key.pub, server.crt
(0644). Missing files skipped (may not exist before init or after
step-ca migration). All security tests pass. Build green.
This commit is contained in:
Jon Chery
2026-08-07 11:25:02 +00:00
parent 50c4e910ed
commit 8ba0723700
+43 -18
View File
@@ -227,28 +227,53 @@ func LoadCA(dir string) (*CA, error) {
// Deprecated: v0.9 re-architecture replaces the internal CA with step-ca
// (D-101/REQ-076). EnforceFileModes is retained for the dual-write window
// and scheduled for deletion in v0.10-P14. See .ciagent/PRD_v0.9.md.
// EnforceFileModes checks that all security-sensitive files in dir have
// the correct permissions (REQ-033 + REQ-130, F13). Checks: ca.crt
// (0644), ca.key (0600), orca_ssh_key (0600), orca_ssh_key.pub (0644),
// known_hosts (0600), master.key (0600), master.key.sealed (0600),
// server.crt (0644), server.key (0600). Missing files are skipped (they
// may not exist yet — e.g. before init or after migration to step-ca).
func EnforceFileModes(dir string) error {
certPath := filepath.Join(dir, CACertFile)
keyPath := filepath.Join(dir, CAKeyFile)
certInfo, err := os.Stat(certPath)
if err != nil {
return fmt.Errorf("EnforceFileModes: stat %s: %w", certPath, err)
// Files that must be 0600 (secrets/keys).
secretFiles := []string{
CAKeyFile,
"orca_ssh_key",
"known_hosts",
"master.key",
"master.key.sealed",
"server.key",
}
keyInfo, err := os.Stat(keyPath)
if err != nil {
return fmt.Errorf("EnforceFileModes: stat %s: %w", keyPath, err)
// Files that must be 0644 (certs/public keys).
publicFiles := []string{
CACertFile,
"orca_ssh_key.pub",
"server.crt",
}
if certInfo.Mode().Perm() != CACPEMMode {
return fmt.Errorf(
"REQ-033 violation: %s has mode %04o, want %04o — fix with `chmod %04o %s`",
certPath, certInfo.Mode().Perm(), CACPEMMode, CACPEMMode, certPath,
)
for _, name := range secretFiles {
path := filepath.Join(dir, name)
info, err := os.Stat(path)
if err != nil {
continue // skip missing
}
if info.Mode().Perm() != 0o600 {
return fmt.Errorf(
"REQ-033/130 violation: %s has mode %04o, want 0600 — fix with `chmod 0600 %s`",
path, info.Mode().Perm(), path,
)
}
}
if keyInfo.Mode().Perm() != CAMode {
return fmt.Errorf(
"REQ-033 violation: %s has mode %04o, want %04o — fix with `chmod %04o %s`",
keyPath, keyInfo.Mode().Perm(), CAMode, CAMode, keyPath,
)
for _, name := range publicFiles {
path := filepath.Join(dir, name)
info, err := os.Stat(path)
if err != nil {
continue // skip missing
}
if info.Mode().Perm() != 0o644 {
return fmt.Errorf(
"REQ-033/130 violation: %s has mode %04o, want 0644 — fix with `chmod 0644 %s`",
path, info.Mode().Perm(), path,
)
}
}
return nil
}