7 Commits

Author SHA1 Message Date
a9e51fbc8c MikroTik-Zertifikat erneuern und automatisieren
Das API-SSL-Zertifikat von mt-az.famfi.home (CN=mt-az.famfi.home, ausgestellt
von famfi.home Intermediate CA, 90 Tage) lief am 2026-09-26 ab. Folge: der
Container crowdsec-mikrotik-bouncer starb im Minutentakt mit
"x509: certificate has expired" - 967 Neustarts, letzter erfolgreicher
API-Pull 2026-09-27 02:29. Zehn Tage lang wurden CrowdSec-Sperren also nur
noch von Traefik durchgesetzt und nicht mehr in die Router-Firewall
geschoben.

Die eigentliche Ursache war nicht das Zertifikat, sondern dass niemand
hinsah: fuer openbao und gitea gibt es Erneuerungsskripte samt Timer (daher
sind die mit 58 bzw. 73 Tagen in Ordnung), fuer den Router gab es keins. Und
die Regeln TLSCertificateExpiringSoon/-Critical haengen am blackbox_exporter,
der nur openbao.famfi.home und www_mahle_com prueft - mt-az:8729 steht in
keiner Probe.

renew-mikrotik-cert.sh ist die Kopie von renew-gitea-cert.sh mit dem
Router-spezifischen Installationsschritt: Zertifikat und Schluessel per scp
hochladen, zweimal /certificate import mit leerem passphrase (RouterOS
protokolliert Kommandos mit, deshalb darf dort nichts Geheimes stehen),
api-ssl umschalten, hochgeladene Dateien entfernen und das alte Objekt erst
danach loeschen. Der Name traegt einen Datumsstempel, weil RouterOS den
Zertifikatsnamen vom Dateinamen ableitet und es sonst Kollisionen gibt.

Das Router-Passwort holt das Skript zur Laufzeit aus OpenBAO, damit es
nirgends auf Platte und in keiner Kommandozeile liegt. Die Policy cert-renew
bekommt dafuer read auf genau secret/data/mikrotik/MT-AZ/apics, nicht auf
secret/mikrotik/*.

Zwei Fehler des ersten Laufs sind behoben: die Namensauslese benutzte einen
gierigen Regex ".*name=", der in der terse-Ausgabe das spaetere
subject-alt-name= traf und "DNS:mt-az.famfi.home" zurueckgab; jetzt wird
feldweise zerlegt. Und /ip service set spricht den Dienst direkt beim Namen
an statt ueber [find ...], das auch den dynamischen Verbindungseintrag
erfasste und eine irritierende Meldung ueber telnet quittierte. Am Ende
prueft das Skript, dass api-ssl wirklich ein gueltiges Zertifikat ausliefert.

Ergebnis: neues Zertifikat gueltig bis 2027-01-04, der Bouncer laeuft und
arbeitet den Rueckstand ab ("Address ... blocked in mikrotik"). Die
IP-Dienste des Routers sind unveraendert, telnet/ftp/www-ssl bleiben
deaktiviert. Timer taeglich mit 21-Tage-Fenster, wie bei den anderen beiden.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-06 20:45:38 +02:00
175c0bc2d1 Policy claude-rw einchecken und SSH-Rechte eingrenzen
Die Policy der Agentenidentitaet claude existierte nur live in OpenBAO und
fehlte als Datei - deshalb war sie bei der Fehlersuche nicht gegenzulesen.

Inhaltlich umgebaut: statt einem pauschalen path "claude/*" nun getrennte
Pfade je Mount. Grund: claude/* deckt auch claude/ssh/config/ca und
claude/ssh/roles/* mit Schreibrecht ab - das war mit einem Testtoken belegt.
Damit haette die Identitaet die SSH-CA ueberschreiben und eigene Rollen mit
beliebigen Principals anlegen koennen, was auf jedem Host, der die CA kennt,
faktisch Root bedeutet. Jetzt gilt: Rollen definiert der Admin, signieren
darf claude.

Dazu die beiden Punkte, die vorher gefehlt hatten: fuer KV v2 sind data/ und
metadata/ getrennte Pfadbaeume und "list" ist eine eigene Faehigkeit; und
sys/mounts mit read, damit `bao secrets list` auf der Kommandozeile nicht
leer bleibt.

Mit Testtoken nachgeprueft: alle erwarteten Rechte vorhanden,
claude/ssh/config/ca und secret/* verweigert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FXUoNtUh1qEdZXSzQeSos4
2026-10-03 10:51:53 +02:00
471692c283 Automate demo-app AppRole SecretID rotation on a weekly timer
A SecretID cannot be renewed the way a token can -- it must be re-issued by a
privileged caller -- so renew-app-tokens.sh structurally could not cover it.
The original expired on ~2026-07-28 and broke app-get-secret.sh silently until
it was noticed on 2026-08-22. This closes the last credential in the stack
that had no automated lifecycle.

Weekly against a 30d secret_id_ttl gives 4x margin: three consecutive failed
rotations can occur before anything actually breaks, and the first failure
already mails an alert via OnFailure=openbao-alert@%n.service.

Order of operations is the safety property: mint -> verify the new SecretID
actually authenticates -> only then overwrite the file -> only then prune old
accessors. Any failure leaves the previous working credential in place and
exits non-zero. Verified by pointing BAO_ADDR at a dead port: exit 1, file
byte-identical, consumer unaffected. A SecretID that fails its verification
login is destroyed rather than installed.

Accessors are kept one cycle deep (KEEP=2: current + previous) so a consumer
that read the file just before rotation can still log in. Verified across
three consecutive runs: steady state stays at 2, oldest pruned each cycle.

The rotator skips its verification login when secret_id_num_uses would be
consumed by it, which is why the policy grants read on the role config. That
read exposes TTLs and bound policies but NOT the RoleID, which lives at the
separate .../role-id path and stays denied -- verified 403, along with 403 on
the demo secret itself and on other AppRoles.

Token at /etc/openbao-approle-rotate.token matches the /etc/openbao-*.token
glob, so the renew loop picks it up automatically (verified: renewed=10
failed=0) and it cannot lapse the way the backup tokens did.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NFtVLA7VVqXL5G2S18c4Jk
2026-08-22 09:33:16 +02:00
700e4ce15a Add smtp-healthcheck-ro policy (scoped read for ClaudeAdmin SMTP creds)
Read-only access to secret/data/smtp/healthcheck, consumed by the ClaudeAdmin
healthcheck email notifier via a periodic token.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-01 10:00:07 +02:00
5e5e7f779f Add governance blueprint: policy-as-code, operator/auditor, identity setup
Reviewable governance scaffolding — NOTHING applied to live OpenBAO yet:

- policies/: materialize all existing policies as code (faithfully fetched
  from live) + new `operator` (use engines, no admin) and `auditor`
  (read-only governance visibility, no secret material)
- scripts/apply-policies.sh: idempotent policy-as-code apply, with a
  read-only --dry-run that diffs files vs live (ignores comments)
- scripts/setup-identity.sh: identity-as-code — policy-bound groups
  (g-admins/operators/auditors/personal) + a human entity/alias; DEFAULT
  DRY-RUN, --apply to execute
- GOVERNANCE.md: the layered model, policy catalog, naming, apply order,
  and cross-cutting controls (audit device, root offline, AppRole migration)

Dry-runs verified read-only: apply-policies shows operator/auditor as NEW,
all others unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 21:05:38 +02:00
14f46e1082 Add unsealer, internal CA/TLS, auto-unseal, and automated cert+backup
Build out the home-lab OpenBAO deployment beyond the basic node:

- docker-compose: add openbao-unsealer sidecar; main node now transit
  auto-unseals against it (seal config kept in gitignored config/seal.hcl)
- policies/admin.hcl: non-root admin policy; per-engine rules for
  ssh/pki/pki_int/totp/transit
- Internal two-tier CA (pki/ root + pki_int/ intermediate) issues the
  openbao.famfi.home leaf Traefik serves; root CA published under ca/
- scripts/ + systemd/: daily cert renewal and Raft snapshot backups
  (both instances), with scoped tokens stored outside the repo
- README: full runbook (auto-unseal, PKI, renewal, backups, DR/restore)

Secrets (init/unsealer keys, tokens, seal stanza) stay gitignored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 14:28:49 +02:00
95e1b676e2 Initial Commit 2026-06-28 13:41:17 +02:00