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:
+43
-18
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user