Mein Lab-Repo ist öffentlich — abrufbar über meinen
Radicle
-Seed-Node seed.this-is-fine.io. Genau deshalb darf da nie ein Klartext-Secret hineinrutschen. Nicht „sollte nicht", sondern kann nicht: Drei
pre-commit
-Hooks fangen den Fehler ab, bevor er ein Commit-Objekt wird. Das ist die billigste Versicherung, die ich kenne — ein paar Zeilen YAML gegen einen Leak, den man nie ganz zurückholt.
Drei Schichten gegen denselben Fehler
Die Hooks greifen ineinander, jeder deckt eine andere Lücke ab:
git commit
│
├─ gitleaks ............ findet Secret-MUSTER (Keys, Tokens) per Entropie/Regex
├─ sops-pre-commit ..... verweigert KLARTEXT in .sops-Pfaden (.sops.yaml-Regeln)
└─ check-sops .......... validiert: entschlüsselt sauber? alle Empfänger drin?
1. gitleaks — der Mustererkenner. gitleaks scannt den Diff auf alles, was wie ein Secret aussieht: AWS-Keys, JWTs, hochentropische Strings. Standardregeln plus eine kleine Erweiterung, denn nicht alles, was wie ein Key aussieht, ist eins:
# .gitleaks.toml
[extend]
useDefault = true
# Public Radicle / W3C DID identifiers — keine Secrets.
[[allowlists]]
description = "did:key public DIDs (Radicle assignees, delegates)"
regexTarget = "line"
regexes = ['''did:key:z[1-9A-HJ-NP-Za-km-z]+''']
Öffentliche did:key-Identifier aus meinem Radicle-Workflow sehen für einen Entropie-Scanner verdächtig aus, sind aber per Definition öffentlich. Pattern-Allowlists wandern in die .gitleaks.toml; einzelne, bewusst geduldete Fundstellen in eine .gitleaksignore mit <pfad>:<rule-id>:<zeile>-Fingerprints. Die Trennung ist wichtig: Muster-Ausnahmen sind generell, Fingerprints sind chirurgisch.
2. sops-pre-commit — der Klartext-Wächter. gitleaks erkennt bekannte Muster. Aber ein DB-Passwort ohne Struktur? Das rutscht durch. Hier greift die zweite Schicht: Sie verweigert, dass eine Datei, die laut .sops.yaml-creation_rules verschlüsselt sein müsste, im Klartext eingecheckt wird.
- repo: https://github.com/red-lichtie/sops-pre-commit-hook
hooks:
- id: sops-pre-commit-hook
name: ensure sops-encrypted secrets
files: \.sops
exclude: ^(\.sops\.yaml|.*\.sops-k8s\.yaml\.tmpl|.*\.sops\.yaml\.tmpl)$
Der Hook kennt meine Verschlüsselungsregeln und prüft Pfad gegen Inhalt. Templates (.tmpl) und die Regeldatei selbst sind ausgenommen — sie sollen Klartext sein.
3. check-sops — der Empfänger-Check. Die dritte Schicht ist eigener Code und fängt einen subtileren Fehler: Eine Datei ist verschlüsselt, aber gegen die falsche Empfängerliste. Das passiert nach einem schlechten updatekeys oder wenn ich einen neuen Contributor vergessen habe. Der lokale Hook entschlüsselt jede geänderte SOPS-Datei (prüft den MAC) und verifiziert, dass alle Pflicht-Empfänger drin sind:
- repo: local
hooks:
- id: sops-decrypt-check
name: validate SOPS decrypt + recipients
entry: .taskfiles/core/scripts/check-sops.sh
language: script
files: ^.*\.sops(?:(\.|-).*)?$
exclude: \.tmpl$
Damit ist die Kette geschlossen: gitleaks fängt das Muster, sops-pre-commit den fehlenden Schutz, check-sops den kaputten Schutz.
Der Rest ist Hygiene
Die übrigen Hooks sind unspektakulär, aber sie halten Diffs sauber und Skripte korrekt: check-yaml (mit --allow-multiple-documents für Kustomize-Streams), end-of-file-fixer, trailing-whitespace, remove-crlf/remove-tabs, und
shellcheck
für die Task-Skripte. fail_fast: false — ich will alle Befunde auf einmal, nicht beim ersten abbrechen.
--no-verify tippt, umgeht alles. Der Schutz lebt davon, dass pre-commit install einmal lief und ich die Hooks nicht aushebele. Eine echte Mauer wäre serverseitig — ein Push-Hook am Seed-Node oder ein CI-Gate. Hier vertraue ich der lokalen Disziplin, weil ich der einzige Committer bin.Warum das reicht
Es ist nicht perfekt, und das ist okay. Der teuerste Fehler — ein Klartext-Secret in der History eines öffentlichen Repos — braucht genau eine Sekunde Unaufmerksamkeit und ist danach nie wieder ganz weg. Diese drei Hooks kosten mich nichts im Alltag und stehen genau an der Stelle, an der der Fehler passiert: zwischen git add und einem unwiderruflichen Commit-Objekt. Wie die Secrets danach in den Cluster fließen — über SOPS, Vault und den External Secrets Operator —, ist eine eigene Geschichte.
