fix(check): Log-Verzeichnis darf beim Start angelegt werden
Der Audit-Logger legt sein Verzeichnis per MkdirAll selbst an, check brach aber ab, wenn es noch nicht existierte — der Pruefer war strenger als die Laufzeit und blockierte die Erstinbetriebnahme. Fehlt zusaetzlich das uebergeordnete Verzeichnis, bleibt es ein Fehler: dann steckt fast immer ein Tippfehler im Pfad. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NBHF4R9EAejDJUMdwr6C68
This commit is contained in:
parent
6694aa5d2e
commit
a2bce25424
2 changed files with 80 additions and 10 deletions
|
|
@ -103,8 +103,11 @@ func (c *Checker) Run(ctx context.Context, testAuthUser string) *Report {
|
||||||
add("Konfiguration", c.Cfg.Validate(), "gelesen und plausibel")
|
add("Konfiguration", c.Cfg.Validate(), "gelesen und plausibel")
|
||||||
|
|
||||||
// 2. Dateirechte
|
// 2. Dateirechte
|
||||||
add("Dateirechte", c.checkPermissions(),
|
permsDetail := "Konfiguration, Secrets und Log-Verzeichnis sind ausreichend geschützt"
|
||||||
"Konfiguration, Secrets und Log-Verzeichnis sind ausreichend geschützt")
|
if note := logDirNote(c.Cfg.Logging.AuditLog); note != "" {
|
||||||
|
permsDetail += "\n" + note
|
||||||
|
}
|
||||||
|
add("Dateirechte", c.checkPermissions(), permsDetail)
|
||||||
|
|
||||||
// 3. OPNsense
|
// 3. OPNsense
|
||||||
serverTime, fwErr := c.checkFirewall(ctx)
|
serverTime, fwErr := c.checkFirewall(ctx)
|
||||||
|
|
@ -152,18 +155,52 @@ func (c *Checker) checkPermissions() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.Cfg.Logging.AuditLog != "" {
|
if c.Cfg.Logging.AuditLog != "" {
|
||||||
dir := filepath.Dir(c.Cfg.Logging.AuditLog)
|
if err := checkLogDir(filepath.Dir(c.Cfg.Logging.AuditLog)); err != nil {
|
||||||
if _, err := os.Stat(dir); err == nil {
|
errs = append(errs, err)
|
||||||
if err := config.CheckDirMode(dir, 0o750); err != nil {
|
|
||||||
errs = append(errs, err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
errs = append(errs, fmt.Errorf("Log-Verzeichnis %s existiert nicht: %w", dir, err))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return errors.Join(errs...)
|
return errors.Join(errs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkLogDir prüft das Verzeichnis des Audit-Logs.
|
||||||
|
//
|
||||||
|
// Der Audit-Logger legt sein Verzeichnis beim Start selbst an. Der Check darf
|
||||||
|
// deshalb nicht strenger sein als die Laufzeit: fehlt nur das letzte
|
||||||
|
// Pfadelement, ist das in Ordnung. Fehlt auch das übergeordnete Verzeichnis,
|
||||||
|
// steckt fast immer ein Tippfehler im Pfad — das bleibt ein Fehler.
|
||||||
|
func checkLogDir(dir string) error {
|
||||||
|
info, err := os.Stat(dir)
|
||||||
|
if err == nil {
|
||||||
|
if !info.IsDir() {
|
||||||
|
return fmt.Errorf("Log-Pfad %s ist kein Verzeichnis", dir)
|
||||||
|
}
|
||||||
|
return config.CheckDirMode(dir, 0o750)
|
||||||
|
}
|
||||||
|
if !os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("Log-Verzeichnis %s nicht lesbar: %w", dir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
parent := filepath.Dir(dir)
|
||||||
|
parentInfo, perr := os.Stat(parent)
|
||||||
|
if perr != nil || !parentInfo.IsDir() {
|
||||||
|
return fmt.Errorf("Log-Verzeichnis %s existiert nicht und kann nicht angelegt werden, "+
|
||||||
|
"weil auch %s fehlt — bitte logging.audit_log prüfen", dir, parent)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// logDirNote beschreibt den Zustand des Log-Verzeichnisses für die Ausgabe.
|
||||||
|
func logDirNote(auditLog string) string {
|
||||||
|
if auditLog == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
dir := filepath.Dir(auditLog)
|
||||||
|
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||||
|
return fmt.Sprintf("Log-Verzeichnis %s wird beim Start angelegt", dir)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// checkFirewall ruft den Export-Endpunkt auf und übersetzt die Fehler in
|
// checkFirewall ruft den Export-Endpunkt auf und übersetzt die Fehler in
|
||||||
// handlungsleitende Meldungen.
|
// handlungsleitende Meldungen.
|
||||||
func (c *Checker) checkFirewall(ctx context.Context) (time.Time, error) {
|
func (c *Checker) checkFirewall(ctx context.Context) (time.Time, error) {
|
||||||
|
|
|
||||||
|
|
@ -152,10 +152,43 @@ func TestLooseTLSKeyIsDetected(t *testing.T) {
|
||||||
|
|
||||||
func TestMissingLogDirectoryIsDetected(t *testing.T) {
|
func TestMissingLogDirectoryIsDetected(t *testing.T) {
|
||||||
c := newChecker(t, &fakePinger{at: checkNow}, &fakeDir{dn: "CN=x"})
|
c := newChecker(t, &fakePinger{at: checkNow}, &fakeDir{dn: "CN=x"})
|
||||||
|
// Auch das übergeordnete Verzeichnis existiert nicht — das ist ein
|
||||||
|
// echter Konfigurationsfehler, meist ein Tippfehler im Pfad.
|
||||||
c.Cfg.Logging.AuditLog = "/gibt/es/nicht/audit.log"
|
c.Cfg.Logging.AuditLog = "/gibt/es/nicht/audit.log"
|
||||||
rep := c.Run(context.Background(), "")
|
rep := c.Run(context.Background(), "")
|
||||||
if rep.OK() {
|
if rep.OK() {
|
||||||
t.Fatal("fehlendes Log-Verzeichnis muss auffallen")
|
t.Fatal("fehlendes Log-Verzeichnis mit fehlendem Elternverzeichnis muss auffallen")
|
||||||
|
}
|
||||||
|
if out := report(t, rep); !strings.Contains(out, "/gibt/es/nicht") {
|
||||||
|
t.Errorf("der beanstandete Pfad muss genannt werden:\n%s", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogDirectoryIsAcceptedIfItCanBeCreated(t *testing.T) {
|
||||||
|
// Der Audit-Logger legt sein Verzeichnis beim Start selbst an. Der Check
|
||||||
|
// darf deshalb nicht strenger sein als die Laufzeit: existiert das
|
||||||
|
// Elternverzeichnis, ist alles in Ordnung.
|
||||||
|
c := newChecker(t, &fakePinger{at: checkNow},
|
||||||
|
&fakeDir{dn: "CN=VPN-Users,DC=firma,DC=local"})
|
||||||
|
parent := filepath.Dir(filepath.Dir(c.Cfg.Logging.AuditLog))
|
||||||
|
c.Cfg.Logging.AuditLog = filepath.Join(parent, "nochnichtda", "audit.log")
|
||||||
|
|
||||||
|
rep := c.Run(context.Background(), "")
|
||||||
|
if !rep.OK() {
|
||||||
|
t.Fatalf("noch nicht angelegtes Log-Verzeichnis darf nicht scheitern:\n%s", report(t, rep))
|
||||||
|
}
|
||||||
|
if out := report(t, rep); !strings.Contains(out, "wird beim Start angelegt") {
|
||||||
|
t.Errorf("der Hinweis auf die spätere Anlage fehlt:\n%s", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogDirectoryWithLoosePermissionsStillFails(t *testing.T) {
|
||||||
|
// Ein existierendes, zu offenes Verzeichnis bleibt ein Fehler.
|
||||||
|
c := newChecker(t, &fakePinger{at: checkNow}, &fakeDir{dn: "CN=x"})
|
||||||
|
os.Chmod(filepath.Dir(c.Cfg.Logging.AuditLog), 0o777)
|
||||||
|
rep := c.Run(context.Background(), "")
|
||||||
|
if rep.OK() {
|
||||||
|
t.Fatal("0777-Log-Verzeichnis muss weiterhin auffallen")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue