Schlüsselloses Signieren von Commits mit gitsign
Signiere Git-Commits mit deiner IdP-Identität statt mit einem langlebigen SSH-Schlüssel. Das schlüssellose Signieren von fremforge nutzt die Bausteine von Sigstore – aber jede Komponente läuft in T Cloud Public eu-de, die Root-CA liegt in einem HSM nach FIPS 140-2 Level 3 in Deutschland, und deine Signaturen sind an den IdP gebunden, dem deine fremforge-Organisation ohnehin vertraut (Microsoft Entra, Okta, Google Workspace, Authentik oder alles andere, was OIDC spricht).
Jeder signierte Commit erscheint in Forgejo als Verified by sigstore: <your-email> via <your-IdP>. Kein Keyserver. Keine Überraschungen durch abgelaufene Schlüssel. Kein „Habe ich den Schlüssel auf dem neuen Laptop importiert?“.
Warum sich das lohnt (im Vergleich zum Signieren mit SSH)
| Signieren mit SSH-Schlüssel | Schlüssellos (sigstore) | |
|---|---|---|
| Bindung an die Identität | „Passt zu einem hinterlegten SSH-Schlüssel“ | „Signiert von you@org.com, bestätigt durch Microsoft Entra“ |
| Lebensdauer des Schlüssels | Dauerhaft (Rotation = Handarbeit) | 10-Minuten-Zertifikat, über OIDC erneuert |
| Laptop verloren = Schlüssel verloren | Du fügst den neuen Schlüssel überall erneut hinzu | Du meldest dich bei deinem IdP an – fertig |
| MFA beim Signieren | Was immer du für den Schlüssel eingestellt hast | Was immer dein IdP verlangt (meist MFA bei jeder Erneuerung) |
| Prüfbar in fremder CI | Schwierig (eigener Aufruf der Forgejo-API) | cosign verify funktioniert mit der CA von fremforge |
Beide Signaturverfahren bestehen nebeneinander in fremforge – der Admin deines Mandanten wählt, wie streng es zugeht (siehe Organisationsrichtlinien unten). Neue Entwicklerinnen und Entwickler sollten standardmäßig gitsign verwenden; bestehende Einrichtungen mit SSH-Signaturen funktionieren weiter.
Einrichtung (einmalig)
1. gitsign installieren
# macOS (Homebrew)
brew install sigstore/tap/gitsign
# Linux (binary)
curl -sLO https://github.com/sigstore/gitsign/releases/latest/download/gitsign-linux-amd64
chmod +x gitsign-linux-amd64 && sudo mv gitsign-linux-amd64 /usr/local/bin/gitsign
# Verify
gitsign --version2. Git konfigurieren
git config --global gpg.x509.program gitsign
git config --global gpg.format x509
git config --global commit.gpgsign true
# Point gitsign at fremforge's private Fulcio + TSA (not the public Sigstore).
git config --global gitsign.fulcio https://sign.frem.sh
git config --global gitsign.timestamp-server-url https://tsa.frem.sh
git config --global gitsign.rekor "" # explicitly empty — fremforge uses TSA-only, no transparency log
git config --global gitsign.connectorID https://your-idp-issuer-urlErsetze https://your-idp-issuer-url durch die OIDC-Issuer-URL, die deine Organisation konfiguriert hat (siehe die Einstellung Erlaubte OIDC-Aussteller-URLs im Verwaltungsbereich von fremforge, oder frage den Admin deiner Organisation).
3. Deinen ersten Commit signieren
cd your-repo
git commit -S -m "First signed commit"Ein Browser-Tab öffnet sich, du authentifizierst dich bei deinem IdP (ein Tippen, wenn du schon angemeldet bist), und der Commit wird signiert. Der Browser-Cache hält das Zertifikat etwa 10 Minuten, sodass weitere Commits in derselben Sitzung ohne erneute Abfrage auskommen.
4. Prüfen, ob es geklappt hat
git verify-commit HEAD
# tlog index: <n>
# Good signature from [you@yourorg.com](you@yourorg.com)
# Validated Git signature: true
# Validated Rekor entry: true (or "skipped" — fremforge doesn't run Rekor)In der Weboberfläche von fremforge zeigt dein Commit neben dem Commit-Hash das Abzeichen Verified by sigstore.
Lieber einen GPG-Schlüssel?
fremforge verifiziert auch Commits, die mit deinem eigenen GPG-Schlüssel signiert sind. Erzeuge einen, weise Git an, ihn zu verwenden, und exportiere seinen öffentlichen Teil:
gpg --full-generate-key
gpg --list-secret-keys --keyid-format=long # the key ID follows "sec ed25519/"
git config --global user.signingkey <key-id>
git config --global commit.gpgsign true
gpg --armor --export <key-id>Füge den Block, der mit -----BEGIN PGP PUBLIC KEY BLOCK----- beginnt, unter Einstellungen → SSH- / GPG-Schlüssel → GPG-Schlüssel verwalten → Schlüssel hinzufügen ein. Die E-Mail-Adresse des Schlüssels muss eine verifizierte Adresse deines Kontos sein; deine signierten Commits erscheinen dann als Verified. Das schlüssellose Signieren oben leistet dasselbe, ohne dass du einen Schlüssel pflegen musst – deshalb ist es der empfohlene Weg. Kombiniere die beiden nicht: Die Einstellung gpg.format x509 aus Schritt 2 gilt nur für gitsign.
Signieren in CI und ohne Browser
Wenn du aus CI oder auf einem Rechner ohne Browser signierst:
# Get an OIDC token from your IdP (different per IdP — see below)
export SIGSTORE_ID_TOKEN="<bearer-token-from-your-idp>"
# Then sign as usual
git commit -S -m "Signed from CI"Rezepte zum Abrufen des Tokens je IdP:
- GitHub Actions → eingebaute Berechtigung
id-token: write - Microsoft Entra Workload Identity →
az account get-access-token --resource <fulcio-audience> - Authentik → OAuth-Device-Code-Ablauf über den Headless-Modus von
gitsign - Alles andere → jedes OIDC-konforme Token funktioniert, solange der Issuer auf der Allowlist deiner Organisation steht
Organisationsrichtlinien
Zwei Einstellungen unter /<your-org>/_admin/auth-policy steuern das Verhalten des schlüssellosen Signierens:
- Erlaubte OIDC-Aussteller-URLs – muss die Issuer-URL deines IdP enthalten. Der Pre-Receive-Hook liest die Erweiterung
OIDCIssuerdes Zertifikats und lehnt jeden Commit ab, dessen Issuer nicht in dieser Liste steht. Leere Liste = Sigstore-Signaturen für diese Organisation deaktiviert. - Strenger Modus – ist er an, werden auf Branches mit aktiviertem Require signed commits nur Commits mit Sigstore-Signatur akzeptiert. Mit SSH signierte Commits werden abgelehnt. Schalte ihn erst ein, wenn alle deine Entwicklerinnen und Entwickler umgestiegen sind.
Fehlerbehebung
gitsign: oidc issuer not allowed by Fulcio: Die Issuer-URL deines IdP ist korrekt, aber das Fulcio der Plattform vertraut ihr noch nicht. Mandanten-Admin: Öffne /<slug>/_admin/auth-policy und füge die Issuer-URL zu Erlaubte OIDC-Aussteller-URLs hinzu. fremforge übernimmt die Änderung automatisch (~30 s); ein Ticket beim Betrieb ist nicht nötig.
gitsign: cert expired before push completed: Zwischen dem Ausstellen des Zertifikats und dem Push lief ein langes Skript. Das Zertifikat gilt 10 Minuten. Führe vor dem Push git commit --amend --no-edit -S aus, um mit einem frischen Zertifikat neu zu signieren.
Abzeichen „Verified“ fehlt bei einem signierten Commit: Prüfe /<your-org>/<repo>/commit/<sha>/status – der Status fremforge-sigstore-verified erscheint als Check neben den anderen CI-Checks. Steht er auf „failure“, klicke darauf, um den Grund zu sehen. Fehlt er ganz, wurde der Pre-Receive-Hook nicht ausgelöst – entweder ist der Commit zu alt (gepusht, bevor die Richtlinie aktiviert wurde), oder dein Push lief über einen unerwarteten Weg. Wende dich mit dem Commit-SHA an den Support.
Das Abzeichen „Verified“ von Forgejo zeigt „unsigned“, aber git verify-commit funktioniert: Der eingebaute Verifizierer von Forgejo versteht Sigstore-Signaturen noch nicht – deshalb verwenden wir stattdessen das Commit-Status-Abzeichen. Der Status fremforge-sigstore-verified ist maßgeblich. (Upstream-Unterstützung wird in gitea#34361 verfolgt.)
So funktioniert es unter der Haube
gitsignstartet den OIDC-Anmeldeablauf deines IdP.- Dein IdP stellt ein kurzlebiges ID-Token aus, das deine Identität bestätigt.
gitsignsendet das Token an die Fulcio-CA von fremforge (sign.frem.sh).- Fulcio prüft das Token und stellt ein X.509-Zertifikat mit 10 Minuten Laufzeit aus, mit deiner E-Mail-Adresse im SAN und der Issuer-URL des IdP in der Erweiterung
OIDCIssuer. Der Signaturschlüssel liegt in einem DEW-HSM von T Cloud Public (FIPS 140-2 Level 3, deutsche Rechtsordnung). gitsignsigniert den Commit-Inhalt mit dem privaten Schlüssel des Zertifikats (der nur für die Lebensdauer des Zertifikats im Speicher deines lokalen Browsers gehalten wird).- Die TSA von fremforge (
tsa.frem.sh) signiert einen vertrauenswürdigen Zeitstempel nach RFC 3161, der festhält: „Das Zertifikat war genau in dieser Sekunde gültig“ – deshalb funktioniert die Prüfung auch noch nach Ablauf des Zertifikats. - Signatur, Zertifikat und Zeitstempel werden in den
gpgsig-Header des Commits eingebettet (Format CMS/PKCS7). - Du pushst. Der Pre-Receive-Hook von fremforge prüft die Zertifikatskette → unsere Fulcio-Root, den Zeitstempel → unsere TSA-Root und den OIDC-Issuer gegen die Liste der erlaubten Issuer deiner Organisation.
- Die Commit-Status-API von Forgejo erfasst das Ergebnis.
Für die Prüfung braucht es nur das Fulcio-Root-Zertifikat und die TSA-Root – während der Prüfung gibt es keine Netzwerkaufrufe. Ein Ausfall der CA betrifft bereits signierte Commits nicht, nur neue Signaturversuche.
Unabhängige Prüfung außerhalb von fremforge
Soll eine fremde CI (oder einfach dein Laptop) mit fremforge signierte Commits prüfen, ohne fremforge zu vertrauen:
# Fetch fremforge's CA root + TSA root (publicly served, integrity via TLS)
curl -sf https://docs.frem.sh/_pki/fulcio-root.pem > /tmp/fremforge-fulcio.pem
curl -sf https://docs.frem.sh/_pki/tsa-root.pem > /tmp/fremforge-tsa.pem
# Use cosign to verify a specific commit
cosign verify-blob \
--certificate-chain /tmp/fremforge-fulcio.pem \
--timestamp-certificate-chain /tmp/fremforge-tsa.pem \
--certificate-identity "you@yourorg.com" \
--certificate-oidc-issuer "https://your-idp-issuer" \
<commit-bytes>Dasselbe Signaturformat wie jede andere Signatur aus dem Sigstore-Ökosystem – es funktioniert mit cosign verify-blob und den übrigen Werkzeugen der OpenSSF zum Signieren von Commits und Blobs. (Hinweis: slsa-verifier prüft die Build-Provenienz von Artefakten, nicht Signaturen von Git-Commits, und ist hier daher nicht anwendbar. Die Build-Provenienz von fremforge ist ein eigener, mit Ed25519 signierter Weg – siehe SLSA-Provenienz.)