Signierte Git-Commits überprüfen?

93

Mit neueren Versionen von git es möglich, einzelne Commits (zusätzlich zu Tags) mit einem PGP-Schlüssel zu signieren:

git commit -m "some message" -S

Und Sie können diese Signaturen in der Ausgabe von git logmit der --show-signatureOption anzeigen:

$ git log --show-signature
commit 93bd0a7529ef347f8dbca7efde43f7e99ab89515
gpg: Signature made Fri 28 Jun 2013 02:28:41 PM EDT using RSA key ID AC1964A8
gpg: Good signature from "Lars Kellogg-Stedman <[email protected]>"
Author: Lars Kellogg-Stedman <[email protected]>
Date:   Fri Jun 28 14:28:41 2013 -0400

    this is a test

Aber gibt es eine andere Möglichkeit, die Signatur eines bestimmten Commits programmgesteuert zu überprüfen, als die Ausgabe von zu überprüfen git log? Ich suche nach dem Commit-Äquivalent von git tag -v- etwas, das einen Exit-Code bereitstellt, der angibt, ob für ein bestimmtes Commit eine gültige Signatur vorhanden war oder nicht.

Larsks
quelle
1
Ich denke das sollte git commit ...und sein git log .... Soweit ich weiß, wurden gpgkeine Unterbefehle hinzugefügt, die gittransparent übergeben werden ... Ich habe keine Repos zum Testen, aber funktioniert das git show --show-signature <commitish>?
Twalberg
show_signaturefügt der Ausgabe nur Dinge hinzu (siehe github.com/git/git/blob/master/log-tree.c#L370 ).
Emil Sit
Hinweis: Sie werden bald --rawfür git verify-tag/ haben git verify-commit. Siehe meine Antwort unten
VonC
1
Hinweis: Mit git 2,11 (Q4 2016), git logwird zusätzliche Statuscodes E, X, Y, Rfür ERRSIG, EXPSIG, EXPKEYSIG, und REVKEYSIG, dass so ein Benutzer %G?mehr Informationen bekommen. Siehe meine bearbeitete Antwort unten
VonC
1
Mit Git 2.26 (Q1 2020) kann die neue Konfiguration gpg.minTrustLevelbei der Verwendung von git verify-tag/ helfen verify -commit. Siehe meine bearbeitete Antwort unten .
VonC

Antworten:

111

Nur für den Fall, dass jemand über eine Suchmaschine auf diese Seite kommt, wie ich es getan habe: In den zwei Jahren seit der Veröffentlichung der Frage wurden neue Tools zur Verfügung gestellt: Es gibt jetzt Git-Befehle für diese Aufgabe: git verify-commitund git verify-tagkönnen zum Überprüfen von Commits und verwendet werden Tags.

Tarleb
quelle
34

Hinweis: Bis Git 2.5 git verify-commitund git verify-tagnur eine vom Menschen lesbare Nachricht angezeigt.
Wenn Sie die Prüfung automatisieren möchten, fügt git 2.6+ (Q3 2015) eine weitere Ausgabe hinzu.

Siehe Commit e18443e , Commit aeff29d , Commit ca194d5 , Commit 434060e , Commit 8e98e5f , Commit a4cc18f , Commit d66aeff (21. Juni 2015) von brian m. Carlson ( bk2204) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in commit ba12cb2 , 03. August 2015)

verify-tag/ verify-commit: Option zum Drucken von Roh-GPG-Statusinformationen hinzufügen

verify-tag/ verify-commitzeigt standardmäßig eine lesbare Ausgabe bei Standardfehlern an.
Es kann jedoch auch nützlich sein, auf die maschinenlesbaren Roh-GPG-Statusinformationen zuzugreifen, um eine automatisierte Implementierung der Signaturrichtlinie zu ermöglichen .

Fügen Sie eine --rawOption hinzu , verify-tagmit der die GPG-Statusinformationen für Standardfehler anstelle des für Menschen lesbaren Formats erstellt werden.

Plus:

verify-tagWird erfolgreich beendet, wenn die Signatur gut ist, der Schlüssel jedoch nicht vertrauenswürdig ist. verify-commitwird erfolglos beendet.
Diese Verhaltensunterschiede sind unerwartet und unerwünscht.
Seit verify-tagfrüher existierte, fügen Sie eine versagende Test haben verify-commitAnteil verify-tagdes Verhaltens.


git 2.9 (Juni 2016) aktualisiert das git merge doc :

Siehe Commit 05a5869 (13. Mai 2016) von Keller Fuchs (``) .
Mit freundlichergitster Unterstützung von Junio ​​C Hamano ( ) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit be6ec17 , 17. Mai 2016)

--verify-signatures:
--no-verify-signatures:

Stellen Sie sicher, dass das Tip Commit des zusammengeführten Seitenzweigs mit einem gültigen Schlüssel signiert ist, dh einem Schlüssel mit einer gültigen UID: Im Standard-Vertrauensmodell bedeutet dies, dass der Signaturschlüssel von einem vertrauenswürdigen Schlüssel signiert wurde.
Wenn das Tip-Commit des Seitenzweigs nicht mit einem gültigen Schlüssel signiert ist, wird die Zusammenführung abgebrochen
.


Update Git 2.10 (Q3 2016)

Siehe Commit b624a3e (16. August 2016) von Linus Torvalds ( torvalds) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit 83d9eb0 , 19. August 2016)

gpg-interface: Bevorzugen Sie die Ausgabe des "langen" Schlüsselformats, wenn Sie pgp-Signaturen überprüfen

" git log --show-signature" und andere Befehle, die den Überprüfungsstatus der PGP-Signatur anzeigen, zeigen jetzt die längere Schlüssel-ID an, wie es die 32-Bit-Schlüssel-ID im letzten Jahrhundert war.

Das Original von Linus wurde neu basiert, um auf den Wartungspfad angewendet zu werden, nur für den Fall, dass Binärverteiler, die in der Vergangenheit stecken geblieben sind, es in ihre ältere Codebasis übernehmen möchten.


Git 2.11+ (Q4 2016) wird noch präziser.

Siehe Commit 661a180 (12. Oktober 2016) von Michael J Gruber ( mjg) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit 56d268b , 26. Oktober 2016)

Der GPG-Überprüfungsstatus, der im %G?hübschen Formatbezeichner " " angezeigt wird, war nicht umfangreich genug, um eine Signatur eines abgelaufenen Schlüssels, eine Signatur eines widerrufenen Schlüssels usw. zu unterscheiden. Es wurden
neue Ausgabebuchstaben zugewiesen, um sie auszudrücken .

Laut gpg2doc/DETAILS :

Für jede Signatur nur einer der Codes GOODSIG, BADSIG, EXPSIG, EXPKEYSIG, REVKEYSIGoder ERRSIGwird ausgesendet werden.

Die git pretty-formatDokumentation enthält jetzt:

  • ' %G?': zeigen
    • " G" für eine gute (gültige) Unterschrift,
    • " B" für eine schlechte Unterschrift,
    • " U" für eine gute Unterschrift mit unbekannter Gültigkeit,
    • " X" für eine gute Unterschrift, die abgelaufen ist,
    • " Y" für eine gute Signatur eines abgelaufenen Schlüssels,
    • " R" für eine gute Signatur eines widerrufenen Schlüssels,
    • " E" wenn die Signatur nicht überprüft werden kann (zB fehlender Schlüssel) und "N" für keine Signatur

Git 2.12 (Q1 2017) " git tag" und " git verify-tag" haben gelernt, den GPG-Überprüfungsstatus in ihr " --format=<placeholders>" Ausgabeformat zu setzen .

Siehe Commit 4fea72f , Commit 02c5433 , Commit ff3c8c8 (17. Januar 2017) von Santiago Torres ( SantiagoTorres) .
Siehe Commit 07d347c , Commit 2111aa7 , Commit 94240b9 (17. Januar 2017) von Lukas Puehringer (``) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit 237bdd9 , 31. Januar 2017)

Hinzufügen --formatzu git tag -vStumme die Standard - Ausgabe des GPG Verifikation und stattdessen druckt das formatiert Tag - Objekt.
Auf diese Weise können Aufrufer den Tagnamen von refs / tags mit dem Tagnamen aus dem Tag-Objekt-Header bei der GPG-Überprüfung abgleichen.


Mit Git 2.16 (Q1 2018) kann die Überprüfung der Commit-Signatur mit der merge.verifySignaturesKonfigurationsvariablen noch automatisiert werden.

Siehe Commit 7f8ca20 , Commit ca779e8 (10. Dezember 2017) von Hans Jerry Illikainen (``) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit 0433d53 , 28. Dezember 2017)

merge: Konfigurationsoption für hinzufügen verifySignatures

git merge --verify-signatures kann verwendet werden, um zu überprüfen, ob das Tip-Commit des Zweigs, in dem zusammengeführt wird, ordnungsgemäß signiert ist. Es ist jedoch umständlich, dies jedes Mal angeben zu müssen.

Fügen Sie eine Konfigurationsoption hinzu, die dieses Verhalten standardmäßig aktiviert und von überschrieben werden kann --no-verify-signatures.

Die git mergeKonfigurations-Manpage lautet nun:

merge.verifySignatures:

Wenn true, entspricht dies der --verify-signaturesBefehlszeilenoption.


Git 2.19 (Q3 2018) ist noch hilfreicher, da " git verify-tag" und " git verify-commit" gelernt haben, den Exit-Status des zugrunde liegenden " gpg --verify" zu verwenden, um eine schlechte oder nicht vertrauenswürdige Signatur zu signalisieren, die sie gefunden haben.

Hinweis: Mit Git 2.19 gpg.formatkann dies auf " openpgp" oder " x509" gesetzt werden und gpg.<format>.programwird verwendet, um anzugeben, welches Programm für den Umgang mit dem Format verwendet werden soll), damit x.509-Zertifikate mit CMS über " gpgsm" anstelle von "verwendet openpgpwerden können " gnupg".

Siehe Commit 4e5dc9c (09. August 2018) von Junio ​​C Hamano ( gitster) .
Unterstützt von: Vojtech Myslivec ( VojtechMyslivec) , brian m. Carlson ( bk2204) und Jeff King ( peff) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit 4d34122 , 20. August 2018)

gpg-interface: Weitergabe des Exit-Status von gpgzurück an die Anrufer

Als die gpg-interface API Mitte 2015 um etwa v2.6.0-rc0 ~ 114 die einheitliche Unterstützung für Codepfade zur Signaturüberprüfung für signierte Tags und signierte Commits unterstützte, haben wir die GPG-Signaturüberprüfung versehentlich gelockert.

Vor dieser Änderung wurden signierte Commits überprüft, indem nach einer Gguten Signatur von GPG gesucht wurde, während der Exit-Status des gpg --verifyProzesses ignoriert wurde , während signierte Tags überprüft wurden, indem einfach der Exit-Status von "gpg --verify"Through" übergeben wurde.

Der einheitliche Code, den wir derzeit haben, ignoriert den Exit-Status von " gpg --verify" und gibt eine erfolgreiche Überprüfung zurück, wenn die Signatur mit einem nicht abgelaufenen Schlüssel übereinstimmt, unabhängig von der auf den Schlüssel gesetzten Vertrauenswürdigkeit (dh zusätzlich zu G"guten" akzeptieren wir " U" nicht vertrauenswürdige).

Lassen Sie diese Befehle einen Fehler mit ihrem Exit-Status signalisieren, wenn das zugrunde liegende " gpg --verify" (oder der durch die gpg.programKonfigurationsvariable " " angegebene benutzerdefinierte Befehl ) dies tut.
Dies ändert ihr Verhalten im Wesentlichen rückwärts inkompatibel, um Signaturen abzulehnen, die mit nicht vertrauenswürdigen Schlüsseln erstellt wurden, auch wenn sie korrekt überprüft wurden, da sich " gpg --verify" so verhält.

Beachten Sie, dass der Code immer noch einen von " gpg" (oder gpg.program) erhaltenen Exit-Status von Null überschreibt, wenn die Ausgabe nicht besagt, dass die Signatur gut ist oder korrekt berechnet wird, sondern mit nicht vertrauenswürdigen Schlüsseln erstellt wurde, um einen schlecht geschriebenen Wrapper abzufangen, den gpgder Benutzer uns möglicherweise gibt .

Wir könnten " U" nicht vertrauenswürdige Unterstützung von diesem Fallback-Code ausschließen, aber das würde zwei rückwärts inkompatible Änderungen in einem einzigen Commit bewirken. Vermeiden wir dies also vorerst .
Eine Folgeänderung könnte dies auf Wunsch tun.


Der Schlüssel muss vertrauenswürdig / signiert sein, bevor eine Verschlüsselung durchgeführt wird

Auf der Vertrauensseite gibt es Fortschritte:
Mit Git 2.26 (Q1 2020) wurde eine gpg.minTrustLevelKonfigurationsvariable eingeführt, um verschiedenen Codepfaden zur Signaturüberprüfung die erforderliche Mindestvertrauensstufe mitzuteilen.

Siehe Commit 54887b4 (27. Dezember 2019) von Hans Jerry Illikainen ( illikainen) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit 11ad30b , 30. Januar 2020)

gpg-interface: Fügen Sie minTrustLevel als Konfigurationsoption hinzu

Unterzeichnet von: Hans Jerry Illikainen

Zuvor wurde bei der Signaturüberprüfung für Zusammenführungs- und Pull-Vorgänge überprüft, ob der Schlüssel eine Vertrauensstufe von entweder TRUST_NEVERoder TRUST_UNDEFINEDin hatte verify_merge_signature().

Wenn dies der Fall wäre, würde der Prozess die()'d.

Die anderen Codepfade, die die Signaturüberprüfung durchgeführt haben, stützten sich vollständig auf den Rückkehrcode von check_commit_signature().

Und Signaturen, die mit einem guten Schlüssel erstellt wurden, unabhängig von seinem Vertrauensniveau, wurden von als gültig angesehen check_commit_signature().

Dieser Unterschied im Verhalten kann dazu führen, dass Benutzer fälschlicherweise annehmen, dass die Vertrauensstufe eines Schlüssels in ihrem Schlüsselbund von Git immer berücksichtigt wird, selbst bei Vorgängen, bei denen dies nicht der Fall ist (z. B. während eines verify-commitoder verify-tag) .

Die Funktionsweise bestand darin, gpg-interface.cdas Ergebnis aus dem Schlüssel- / Signaturstatus und den untersten zwei Vertrauensstufen im resultMitglied der signature_checkStruktur zu speichern (die letzte dieser Statuszeilen, auf die gestoßen wurde, wurde geschrieben result).

Diese sind in GPG unter dem Unterabschnitt General status codesbzw. dokumentiert Key related.

In der GPG-Dokumentation steht Folgendes zu den TRUST_ statusCodes :


Dies sind mehrere ähnliche Statuscodes:

- TRUST_UNDEFINED <error_token>
- TRUST_NEVER     <error_token>
- TRUST_MARGINAL  [0  [<validation_model>]]
- TRUST_FULLY     [0  [<validation_model>]]
- TRUST_ULTIMATE  [0  [<validation_model>]]

Bei guten Signaturen wird eine dieser Statuszeilen ausgegeben, um die Gültigkeit des Schlüssels anzuzeigen, der zum Erstellen der Signatur verwendet wurde.
Die Fehlertokenwerte werden derzeit nur von gpgsm ausgegeben.


Meine Interpretation ist, dass sich die Vertrauensstufe konzeptionell von der Gültigkeit des Schlüssels und / oder der Signatur unterscheidet.

check_signature()Dies scheint auch die Annahme des alten Codes gewesen zu sein, in dem ein Ergebnis von ' G' (wie in GOODSIG) und ' U' (wie in TRUST_NEVERoder TRUST_UNDEFINED)beide als Erfolg angesehen wurden).

Die beiden Fälle , in denen das Ergebnis von ‚ U‘ hatte eine besondere Bedeutung waren in verify_merge_signature()(wo dies verursacht gitzu die()) und in format_commit_one()(wo er den Ausgang des betroffenen %G?Formatbezeichner).

Ich halte es für sinnvoll, die Verarbeitung von TRUST_ statusZeilen so umzugestalten, dass Benutzer eine Mindestvertrauensstufe konfigurieren können, die global erzwungen wird, anstatt einzelne Teile git(z. B. Zusammenführen) selbst ausführen zu lassen (mit Ausnahme einer Kulanzfrist mit Abwärtskompatibilität).

Ich denke auch, dass es sinnvoll ist, die Vertrauensstufe nicht im selben Strukturelement wie den Schlüssel- / Signaturstatus zu speichern.

Das Vorhandensein eines TRUST_ statusCodes bedeutet zwar, dass die Signatur gut ist (siehe den ersten Absatz im obigen Snippet oben), aber soweit ich das beurteilen kann, ist die Reihenfolge der Statuszeilen von GPG nicht genau definiert. Daher erscheint es plausibel, dass die Vertrauensstufe mit dem Schlüssel- / Signaturstatus überschrieben werden könnte, wenn sie im selben Mitglied der signature_checkStruktur gespeichert würden .

Dieser Patch führt eine neue Konfigurationsoption ein : gpg.minTrustLevel.

Es konsolidiert die Überprüfung auf Vertrauensebene gpg-interface.cund fügt trust_levelder signature_checkStruktur ein neues Mitglied hinzu .

Die Abwärtskompatibilität wird durch die Einführung eines Sonderfalls aufrechterhalten, bei dem verify_merge_signature(), wenn keine vom Benutzer konfigurierbare gpg.minTrustLevelEinstellung festgelegt ist, das alte Ablehnungsverhalten festgelegt TRUST_UNDEFINEDund TRUST_NEVERerzwungen wird.

Wenn andererseits gpg.minTrustLevelfestgelegt ist, überschreibt dieser Wert das alte Verhalten.

In ähnlicher Weise zeigt der %G?Formatbezeichner weiterhin ' U' für Signaturen an, die mit einem Schlüssel erstellt wurden, der eine Vertrauensstufe von TRUST_UNDEFINEDoder hat TRUST_NEVER,, obwohl das UZeichen ' ' im resultMitglied der signature_checkStruktur nicht mehr vorhanden ist .

Ein neuer Formatbezeichner %GTwird auch für Benutzer eingeführt, die alle möglichen Vertrauensstufen für eine Signatur anzeigen möchten.

Ein anderer Ansatz wäre gewesen, einfach die Anforderung auf Vertrauensebene fallen zu lassen verify_merge_signature().

Dies hätte das Verhalten auch mit anderen Teilen von Git konsistent gemacht, die eine Signaturüberprüfung durchführen.

Das Erfordernis eines Mindestvertrauensniveaus für das Signieren von Schlüsseln scheint jedoch einen realen Anwendungsfall zu haben.

Beispielsweise analysiert das vom Qubes OS-Projekt verwendete Build-System derzeit die Rohausgabe von verify-tag, um eine Mindestvertrauensstufe für Schlüssel festzulegen, die zum Signieren von Git-Tags verwendet werden .

Die git config gpgManpage enthält jetzt:

gpg.minTrustLevel:

Gibt eine Mindestvertrauensstufe für die Signaturüberprüfung an.
Wenn diese Option nicht aktiviert ist, erfordert die Signaturüberprüfung für Zusammenführungsvorgänge einen Schlüssel mit mindestens marginalVertrauen.
Andere Vorgänge, die eine Signaturüberprüfung durchführen, erfordern einen Schlüssel mit mindestens undefinedVertrauen.
Durch Festlegen dieser Option wird die erforderliche Vertrauensstufe für alle Vorgänge überschrieben. Unterstützte Werte in aufsteigender Reihenfolge der Bedeutung:

  • undefined
  • never
  • marginal
  • fully
  • ultimate

Mit Git 2.26 (Q1 2020)git show gaben " " und andere in ihrer Fehlerausgabe einen Objektnamen im Rohformat an, der korrigiert wurde, um ihn in hexadezimaler Form anzugeben.

show_one_mergetag: Nicht übergeordnetes Element in hexadezimaler Form drucken.

Wenn ein Mergetag einen Nicht-Elternteil benennt, der nach einem flachen Klon auftreten kann, wurde sein Hash zuvor als Rohdaten gedruckt.
Drucken Sie es stattdessen in hexadezimaler Form.

Getestet mit git -C shallow log --graph --show-signature -n1 plain-shallownach agit clone --depth 1 --no-local . shallow


Mit Git 2.27 (Q2 2020) wurde der Code für die Schnittstelle mit GnuPG überarbeitet.

Siehe Commit 6794898 , Commit f1e3df3 (04. März 2020) von Hans Jerry Illikainen ( illikainen) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit fa82be9 , 27. März 2020)

gpg-interface: Bevorzugen Sie die check_signature()GPG-Überprüfung

Unterzeichnet von: Hans Jerry Illikainen

Diese verpflichten refactors die Verwendung von verify_signed_buffer()außerhalb gpg-interface.czu verwenden , check_signature()statt.

Es wird auch verify_signed_buffer()zu einer dateilokalen Funktion, da es jetzt nur noch intern von aufgerufen wird check_signature().

Bisher wurden in verschiedenen Teilen von Git zwei Funktionen mit globalem Gültigkeitsbereich verwendet, um die Überprüfung der GPG-Signatur durchzuführen: verify_signed_buffer()und check_signature().

Jetzt wird nur check_signature()noch verwendet.

Die verify_signed_buffer()Funktion schützt nicht vor doppelten Signaturen, wie von Michał Górny beschrieben .

Stattdessen wird nur ein fehlerfreier Exit-Code von GPG und das Vorhandensein mindestens eines GOODSIGStatusfelds sichergestellt .

Dies steht im Gegensatz dazu, check_signature()dass ein Fehler zurückgegeben wird, wenn mehr als eine Signatur angetroffen wird.

Der niedrigere Überprüfungsgrad macht die Verwendung verify_signed_buffer()problematisch, wenn Anrufer die verschiedenen Teile der GPG-Statusnachricht nicht selbst analysieren und validieren.

Die Verarbeitung dieser Nachrichten scheint eine Aufgabe zu sein, der gpg-interface.cdie Funktion vorbehalten sein sollte check_signature().

Darüber hinaus verify_signed_buffer()macht es die Verwendung von schwierig, neue Funktionen einzuführen, die auf dem Inhalt der GPG-Statuszeilen beruhen.

Jetzt teilen sich alle Vorgänge, die die Signaturüberprüfung durchführen, einen einzigen Einstiegspunkt gpg-interface.c.

Dies erleichtert die Weitergabe geänderter oder zusätzlicher Funktionen bei der GPG-Signaturüberprüfung an alle Teile von Git, ohne dass seltsame Randfälle auftreten, die nicht den gleichen Überprüfungsgrad aufweisen .

VonC
quelle
4

Eine flüchtige Überprüfung des Codes legt nahe, dass es keine solche direkte Methode gibt.

Alle Tests in der Git-Quelle basieren auf dem grepPing der Ausgabe von git show(siehe t / t7510-signed-commit.sh für die Tests).

Sie können die Ausgabe anpassen, indem --pretty "%H %G?%"Sie das Parsen vereinfachen.

Es scheint, dass Sie git mergenach einer Signatur fragen können, aber auch hier stützen sich die Tests darauf grep(siehe t / t7612-merge-verify-signatures.sh ). Es sieht so aus, als würde eine ungültige Signatur dazu führen git merge, dass sie mit einer schlechten Signatur beendet wird. Sie könnten dies also möglicherweise heute umgehen, indem Sie irgendwo eine Testzusammenführung durchführen und diese Zusammenführung verwerfen, aber das scheint schlimmer zu sein, als nur grep aufzurufen.

Emil Setz dich
quelle