generated from coulomb/repo-seed
feat(local-identity): Stage 4 — security hardening (NK-WP-0002-T04)
Permission enforcement on startup: enforce_permissions() checks store dir (700), user files (600), signing key, TLS key, audit.log, revoked.json. CLI and run_server() call it before any sensitive operation. New modules: security.py check_store(), enforce_permissions(), print_security_check() audit.py log_event() — append-only TSV audit log (mode 600) revoke.py revoke(jti), is_revoked(jti) — revocation list (mode 600) New CLI commands: security-check Print per-check pass/warn/fail report; exit 1 on failure revoke-token <jti|jwt> Add JTI to revocation list; accepts raw JTI or full JWT Serve integration: Audit log written for auth request, token issuance, and userinfo calls Revocation checked at /userinfo; revoked tokens return 401 Docs: security model section in LocalIdentity.md — threat model, assumptions, non-guarantees, SELinux/AppArmor guidance, revocation usage. 138 tests passing (34 new for Stage 4). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
40
local-identity/src/local_identity/audit.py
Normal file
40
local-identity/src/local_identity/audit.py
Normal file
@@ -0,0 +1,40 @@
|
||||
"""
|
||||
Append-only audit log for local-identity.
|
||||
|
||||
All CLI commands and OIDC server events write one entry per operation to
|
||||
~/.local-identity/audit.log (mode 600).
|
||||
|
||||
Format (TSV): ISO-timestamp command username outcome
|
||||
Example:
|
||||
2026-03-02T00:00:00Z init alice ok
|
||||
2026-03-02T00:01:00Z serve/auth alice auth_request
|
||||
2026-03-02T00:01:01Z serve/token alice token_issued
|
||||
|
||||
Failures (e.g. file permission errors) are silently swallowed — the audit
|
||||
log must never interrupt the primary operation.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from .store import _store_dir
|
||||
|
||||
|
||||
def _audit_log_path() -> Path:
|
||||
return _store_dir() / "audit.log"
|
||||
|
||||
|
||||
def log_event(command: str, username: str | None, outcome: str) -> None:
|
||||
"""Append one audit entry. Silent on any I/O failure."""
|
||||
timestamp = datetime.datetime.now(datetime.timezone.utc).strftime(
|
||||
"%Y-%m-%dT%H:%M:%SZ"
|
||||
)
|
||||
entry = f"{timestamp}\t{command}\t{username or '-'}\t{outcome}\n"
|
||||
path = _audit_log_path()
|
||||
try:
|
||||
with open(path, "a", encoding="utf-8") as fh:
|
||||
fh.write(entry)
|
||||
os.chmod(path, 0o600)
|
||||
except OSError:
|
||||
pass
|
||||
Reference in New Issue
Block a user