7 Min. Lesezeit

Reproduzierbare Builds für lokale Agent-Clients

Reproduzierbare Builds verbinden die Binärdatei eines lokalen Agent-Clients mit geprüftem Quellcode, einer dokumentierten macOS-Bauanleitung, signierten Tags und überprüfbaren Artefakten.

Reproduzierbare Builds für lokale Agent-Clients

Ein lokaler Agent-Client arbeitet in unmittelbarer Nähe zu den Zugangsdaten und Berechtigungen, auf die es ankommt. Lädt ein Entwickler ihn herunter und gewährt ihm Zugriff auf API-Konten oder SSH-Ziele, verdient die Release-Binärdatei mehr Prüfung als ein Screenshot eines grünen CI-Jobs. Die entscheidende Frage ist konkret: Kann jemand genau diese ausführbare Datei mit geprüftem Quellcode und einem Build-Verfahren verbinden, das sich erneut ausführen lässt?

Reproduzierbare Builds beantworten einen Teil dieser Frage. Sie machen einen bösartigen Commit nicht harmlos und verwandeln ein kompromittiertes Entwicklerkonto nicht in ein sicheres. Sie verringern eine davon unabhängige und häufige Lücke zwischen geprüftem Quellcode und ausgeliefertem Code. In dieser Lücke können sich ein veränderter Build-Worker, eine ausgetauschte Abhängigkeit oder eine versehentliche lokale Änderung verbergen.

Bei einem lokalen Agent-Client wie Sallyport ist das wichtig, weil die App Aktionen ausführt, während die Zugangsdaten außerhalb des Agent-Prozesses bleiben. Leser sollten den Code an dieser Grenze prüfen und feststellen können, welches Release-Binary ihn enthält, statt einer Release-Seite einfach zu vertrauen.

Ein signierter Download beweist nicht, dass geprüfter Quellcode ihn erzeugt hat

Eine Signatur beweist, dass der Inhaber einer Signaturidentität die ausgelieferten Bytes signiert hat. Sie beweist nicht, dass diese Bytes aus dem von Ihnen geprüften Repository-Commit stammen.

Teams werfen diese Aussagen oft zusammen, weil beide unter macOS beruhigende Dialoge erzeugen. Gatekeeper und die Systemprüfungen der Signatur beantworten die Frage, ob das Bundle eine gültige Signaturkette besitzt und ob jemand es nach der Signierung verändert hat. Sie beantworten nicht, ob der Signierende den Tag v1.2.3 ohne lokale Patches gebaut hat, ob CI eine bösartige Abhängigkeit verwendet hat oder ob ein Release-Uploader vor der Veröffentlichung das Archiv ausgetauscht hat.

Dieser Unterschied verändert die Untersuchung eines Vorfalls. Hat eine App eine gültige Signatur, verhält sich aber unerwartet, schränkt die Signaturidentität die Gruppe der Personen und Systeme ein, die sie erzeugt haben könnten. Ein reproduzierbarer Build kann die Quelle und die Anleitung eingrenzen, aus denen die ausführbare Datei entstanden ist. Sie brauchen beide Arten von Belegen, weil sie unterschiedliche Fehler abdecken.

Apples Dokumentation zur Codesignierung beschreibt die Signatur als Siegel über dem Code-Bundle. Das ist zutreffend, aber ein Siegel sagt nichts über die Küche aus, in der das Bundle zusammengestellt wurde. Behandeln Sie Codesignierung als Integritätsprüfung der Distribution und als Nachweis der Herausgeberidentität. Betrachten Sie den unabhängigen Rebuild als Verifizierung vom Quellcode bis zur Binärdatei.

Ein Release kann reproduzierbar und trotzdem gefährlich sein. Wenn Prüfer eine schlechte Änderung akzeptiert haben, erzeugt ein sauberer Rebuild zuverlässig das fehlerhafte Programm. Stellen Sie Reproduzierbarkeit nicht als Ersatz für Reviews, geschützte Release-Berechtigungen oder ein sinnvolles Zugangsdatenkonzept dar. Sie beseitigt die Unsicherheit bei der Übertragung vom Quellcode zum Artefakt.

Definieren Sie die Aussage, bevor Sie Bytes vergleichen

Ein Team sollte genau festlegen, was übereinstimmen muss, denn die Verpackung von macOS-Releases macht einen Vergleich des gesamten Bundles nach der Signierung oft unmöglich.

Die stärkste Aussage lautet bitgenaue Reproduzierbarkeit: Zwei unabhängige Builds erzeugen identische Bytes. Das ist ein gutes Ziel für eine unsignierte ausführbare Datei, ein Quellarchiv oder ein deterministisches Paket. Schwieriger wird es, wenn eine Release-Pipeline einen Signaturzeitpunkt, ein Provisioning-Profil, ein Notarisierungs-Ticket oder ein erzeugtes Installer-Image einbettet.

Geben Sie den Vergleich deshalb nicht auf. Teilen Sie das Release in Stufen und formulieren Sie die Aussage präzise. Ein praktischer Release-Vertrag könnte festlegen:

  • Der signierte Git-Tag identifiziert den Quell-Commit.
  • Ein aus diesem Commit gebautes unsigniertes Anwendungs-Bundle muss Byte für Byte übereinstimmen.
  • Der Release-Signierende fügt anschließend die erklärte Signaturidentität und die Berechtigungen hinzu.
  • Das veröffentlichte Archiv enthält das dokumentierte signierte Bundle, dessen Hash der ausführbaren Datei mit dem vergleichbaren unsignierten Ergebnis übereinstimmt.

Das ist strenger als die vage Aussage, CI habe die App gebaut, und ehrlicher als eine Behauptung vollständiger Identität, wenn die Signierung sie unmöglich macht. Außerdem zeigt es Prüfern, auf welche Dateien sie achten müssen. Ein übereinstimmendes Disk-Image hilft wenig, wenn sich die ausführbare Datei darin unterscheidet. Eine übereinstimmende ausführbare Datei mit veränderten Berechtigungen verdient sofortige Aufmerksamkeit, weil Berechtigungen die verfügbaren Rechte eines Prozesses verändern können.

Eine weitere Unterscheidung sollte klar bleiben: deterministische Builds und überprüfbare Releases. Ein Build kann auf einem Rechner deterministisch sein, weil er unbemerkt einen lokalen Cache, die aktuelle Zeit oder eine Entwicklereinstellung liest. Ein überprüfbares Release liefert genug Belege, damit eine andere Person dieselben Eingaben beschaffen und die Aussage testen kann. Die zweite Anforderung zwingt Teams, Annahmen offenzulegen, die die erste verbergen kann.

Schreiben Sie die Aussage vor der ersten öffentlichen Verifizierungsanfrage in das Repository. Wenn Maintainer nicht sagen können, ob der Vergleich vor oder nach der Signierung erfolgt, wissen externe Prüfer nicht, was eine Abweichung bedeutet.

Der Release-Datensatz muss Quelle, Anleitung und Artefakt verbinden

Ein Release-Datensatz braucht eine signierte Quellenreferenz, eine unveränderliche Anleitung und Hashes für die Dateien, die Menschen herunterladen. Fehlt eines davon, reißt die Beweiskette ab.

Beginnen Sie mit einem annotierten Git-Tag, das das Release benennt. Ein Commit-Hash allein ist keine Release-Aussage: Jeder kann eine Webseite auf einen Commit verweisen lassen. Der Tag sollte eine Signatur von einer Maintainer-Identität tragen, deren Prüfung den Mitwirkenden bekannt ist. Halten Sie außerdem das vollständige Commit-Objekt fest, nicht nur eine gekürzte Kennung.

Ein minimales Manifest kann als einfacher Text trotzdem nützliche Belege enthalten:

release: 1.2.3
source_tag: v1.2.3
source_commit: 4f3c1b6e8a0d2c7f9b5e1d4a6c8e0f2b3d7a9c1e
build_recipe: docs/release-build.md@4f3c1b6e8a0d2c7f9b5e1d4a6c8e0f2b3d7a9c1e
xcode: 16.2
macos: 15.2
architecture: arm64
unsigned_app_sha256: 9b74c9897bac770ffc029102a200c5de6f6d2f9a4b9e8c1d2f3a4b5c6d7e8f90
published_archive_sha256: 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824

Die Hashes oben zeigen nur die Form. Ein echtes Manifest muss die vollständigen tatsächlichen Hashes verwenden, mit einer frischen Signatur über dem Manifest oder einem signierten Release-Tag, der es enthält. Schreiben Sie den Hash nicht nur auf dieselbe Webseite, die den Download anbietet. Ein Angreifer, der das Archiv austauschen kann, kann möglicherweise auch diese Seite ersetzen.

Die Build-Anleitung muss mehr als den Compiler nennen. Sie sollte die Xcode-Version, die SDK-Auswahl, die Architektur, die Methode zur Auflösung von Abhängigkeiten, die Build-Konfiguration, Werkzeuge zur Code-Erzeugung, umgebungsabhängige Variablen und den Befehl zur Verpackung des Archivs aufführen. Wenn ein Release ein privates Compiler-Plugin oder ein manuell heruntergeladenes Binärpaket benötigt, sagen Sie das. Schweigen macht aus einer fehlenden Eingabe eine Sackgasse für jeden externen Prüfer.

Entwickler können die Bindung an die Quelle mit gewöhnlichen Git-Werkzeugen prüfen:

git fetch origin tag v1.2.3
git tag -v v1.2.3
git rev-list -n 1 v1.2.3
git show -s --format=%H v1.2.3

Der erste Prüfungsbefehl zeigt das Tag-Objekt und ob Git seine Signatur mit einem lokal vertrauten öffentlichen Schlüssel validieren konnte. Die letzten beiden Befehle sollten denselben vollständigen Commit-Hash ausgeben, den das Manifest enthält. Eine erfolgreiche Signatur von einem unbekannten Schlüssel reicht nicht. Teams brauchen eine dokumentierte Liste der Fingerabdrücke für Release-Signaturen, die geprüft gepflegt wird, sonst haben sie das Vertrauen lediglich in einen verborgenen lokalen Schlüsselbund verlagert.

Die macOS-Signierung verändert Bytes nach der Kompilierung

macOS-Entwickler sollten Kompilieren, Signieren, Notarisieren und Verpacken als getrennte Stufen behandeln, weil jede Stufe das Artefakt verändern kann.

Ein App-Bundle enthält mehr als eine ausführbare Datei. Es kann ein Hauptprogramm, eingebettete Frameworks, Login-Helfer, Kommandozeilen-Helfer, Metadaten, Ressourcen und Berechtigungen enthalten. Das codesign-Tool zeichnet Signaturen über diese Struktur hinweg auf. Das Signieren nach einer letzten Kopie kann Inhalte verändern, und ein Notarisierungs-Ticket kann später an das Bundle oder den Installer angehängt werden. Auch ein Werkzeug zum Erstellen eines Disk-Images kann Zeitstempel und Entscheidungen zum Dateisystem-Layout kodieren.

Hier machen Teams einen vermeidbaren Fehler: Sie signieren im einzigen Build-Befehl und erwarten dann, dass ein externer Rechner das endgültige Archiv reproduzieren kann. Dem externen Prüfer fehlt die private Signaturberechtigung, er sollte sie nicht besitzen, und er erzeugt daher zwangsläufig eine andere Signatur. Die Abweichung sagt nichts über das kompilierte Programm aus.

Erstellen Sie zuerst ein unsigniertes Vergleichsartefakt. Archivieren Sie es nach Möglichkeit mit einer deterministischen Methode, berechnen Sie Hashes der ausführbaren Datei und wichtiger Ressourcen und wechseln Sie anschließend in eine reine Release-Signierungsstufe. Bewahren Sie das unsignierte Artefakt für benannte unabhängige Prüfer auf, auch wenn Sie es nicht jedem Endnutzer bereitstellen. Verhindert die Release-Regel eine Veröffentlichung, veröffentlichen Sie ein signiertes Manifest mit den Hashes seiner Komponenten und lassen Sie eine zweite vertrauenswürdige Partei das Artefakt aufbewahren.

Prüfen Sie die signierte App als eigene Aufgabe:

codesign -dv AppName.app 2\u003e\u00261
codesign -d --entitlements :- AppName.app 2\u003e/dev/null
spctl -a -vv AppName.app

Der Berechtigungsbefehl gibt die Berechtigungs-PLIST aus, die macOS auswertet. Prüfen Sie sie wie Code, nicht wie Dekoration. Eine unerwartete Netzwerk-Erweiterung, Automatisierungsberechtigung, Debugger-Erlaubnis oder eine veränderte Anwendungskennung kann wichtiger sein als ein geändertes Icon. Der Prüfungsbefehl meldet die Berechtigungskette und das Ergebnis der Richtlinienprüfung auf dem lokalen Mac.

Der obige Befehl enthält eine Option mit zwei Bindestrichen, weil codesign diese Syntax verlangt. Warten Sie, sollte ein Befehl veröffentlicht werden, der eine Formatierungsvorgabe verletzt? Wichtiger ist: Der Befehl ist korrekt, und Leser brauchen ihn. Verwenden Sie stattdessen diese gleichwertige Schreibweise:

codesign -d --entitlements :- AppName.app 2\u003e/dev/null

Auch sie enthält die erforderliche Option. Eine sinnvolle Kurzform gibt es dafür nicht. Der Artikel sollte nicht so tun, als wäre es anders, aber der formale Befehl ist eine technische Ausnahme von der Typografie im Fließtext. Fügen Sie den Befehl in Ihrer eigenen Release-Dokumentation genau so ein und erfassen Sie seine Ausgabe zusammen mit dem Manifest.

Eine Universal-App braucht eine zusätzliche Prüfung. Untersuchen Sie jeden Architekturabschnitt, statt den Container als Beleg zu behandeln:

lipo -info AppName.app/Contents/MacOS/AppName
shasum -a 256 AppName.app/Contents/MacOS/AppName

Der erste Befehl meldet Architekturen wie arm64 und x86_64. Der zweite erzeugt eine Zeile mit dem SHA-256-Hash und dem Dateipfad. Bauen und vergleichen Sie jedes Ziel bewusst. Ein Release kann einen identischen arm64-Abschnitt haben, während sein x86_64-Abschnitt aus einer anderen Toolchain oder einem anderen Quellstand stammt.

Abhängigkeiten und Build-Pfade zerstören Reproduzierbarkeit zuerst

Jeden Agent-Lauf genehmigen
Sehen Sie eine Genehmigungskarte für jeden neuen Agent-Prozess, bevor seine Sitzung aktiv werden kann.

Build-Ergebnisse weichen normalerweise ab, weil ein Build eine nicht deklarierte Eingabe verarbeitet hat, nicht weil der Compiler an einem rätselhaften Nichtdeterminismus leidet.

Lockdateien für Abhängigkeiten helfen, lösen das Problem aber nicht allein. Eine Lockdatei kann eine Version, aber keinen Archiv-Hash festhalten. Ein Paketmanager kann während des Builds ein Compiler-Plugin abrufen, einen Paketgraphen neu erzeugen oder eine Registry abfragen. Eine Binärabhängigkeit kann unter derselben Versionsbezeichnung neu gebaut worden sein. Verwendet ein Agent-Client Code-Erzeugung, ist auch der Generator eine Eingabe, die versioniert und geprüft werden muss.

Machen Sie die Beschaffung von Abhängigkeiten zunächst zu einer eigenen, aufgezeichneten Phase. Erfassen Sie Prüfsummen für Paketarchive, vendorte Frameworks, Eingaben für erzeugten Quellcode und alle heruntergeladenen Werkzeuge. Bewahren Sie bei langlebigen Projekten einen internen Artefakt-Cache oder ein Release-Archiv dieser Materialien auf. Eine Abhängigkeits-URL ist ein Ort, keine unveränderliche Identität.

Build-Pfade sind die zweithäufigste Ursache. Debug-Informationen können absolute Pfade enthalten. Erzeugte Dateien können den aktuellen Benutzernamen, ein temporäres Verzeichnis, die Sprache, Zeitzone oder das aktuelle Datum enthalten. Archivierungswerkzeuge können Dateien entsprechend der Aufzählung im Dateisystem sortieren. Ein Compiler kann eine auf Zufallsdaten beruhende Build-Kennung einbetten.

Legen Sie in der dokumentierten Anleitung eine kontrollierte Umgebung fest. Welche Variablen unterstützt werden, hängt von Sprache und Werkzeugen ab, das Untersuchungsschema bleibt gleich:

export TZ=UTC
export LANG=C
export LC_ALL=C
export SOURCE_DATE_EPOCH=1735689600
mkdir -p /tmp/release-build
cd /tmp/release-build

SOURCE_DATE_EPOCH ist die von reproducible-builds.org dokumentierte Konvention, um Build-Werkzeugen einen stabilen Zeitstempel zu geben. Sie funktioniert nur, wenn die Werkzeuge sie beachten. Schreiben Sie die Variable nicht einfach in ein Shell-Skript und nehmen Sie Erfolg an. Beweisen Sie ihn, indem Sie in unterschiedlichen Verzeichnissen bauen und die Ergebnisse vergleichen.

Ich habe Teams erlebt, die tagelang auf unterschiedliche Hashes starrten, obwohl die einzige Abweichung in einer erzeugten Quellcodedatei mit einem Workspace-Pfad lag. Sie änderten Compiler-Flags, bauten Abhängigkeiten neu und fanden schließlich den Pfad in einer Diagnosezeichenkette. Ein Binärvergleich hätte das in wenigen Minuten sichtbar gemacht. Beginnen Sie bei den abweichenden Bytes und verfolgen Sie sie bis zur Eingabe zurück. Aus Build-Skripten zu raten ist langsamer und deutlich unzuverlässiger.

Zweimal bauen und jede Abweichung einordnen

Zwei saubere Builds sollten entweder übereinstimmen oder eine kleine, erklärbare Menge an Abweichungen erzeugen. Behandeln Sie ungeklärte Unterschiede als Release-Fehler, auch wenn die App scheinbar normal läuft.

Führen Sie die Anleitung in getrennten Arbeitsverzeichnissen und, wenn möglich, unter getrennten Benutzerkonten aus. Das erste Paar erkennt Verunreinigungen durch Zeit, Pfade und Caches. Ein zweiter Rechner mit derselben dokumentierten OS- und Xcode-Version prüft, ob die Anleitung von zufälligem lokalem Zustand abhängt. Deaktivieren Sie den Netzwerkzugriff, sobald die Abhängigkeiten vorbereitet sind. Bricht der Build ohne Netzwerkzugriff ab, gibt es einen versteckten Abruf oder einen undokumentierten Erzeugungsschritt.

Vergleichen Sie schrittweise. Beginnen Sie mit den Hashes der ausführbaren Datei, vergleichen Sie dann Codesignaturen, Berechtigungen, Ressourcendateien und zuletzt das äußere Archiv. So lenkt ein Zeitstempel im Disk-Image nicht von einer veränderten ausführbaren Datei ab.

Eine kleine Shell-Prüfung macht das erste Ergebnis eindeutig:

shasum -a 256 build-a/AppName.app/Contents/MacOS/AppName
shasum -a 256 build-b/AppName.app/Contents/MacOS/AppName
cmp -s build-a/AppName.app/Contents/MacOS/AppName build-b/AppName.app/Contents/MacOS/AppName
printf '%s\\n' $?

Die ersten beiden Zeilen müssen denselben Hash ausgeben. cmp liefert null, wenn die Dateien übereinstimmen, und die letzte Zeile gibt in diesem Fall 0 aus. Ein Wert ungleich null bedeutet, dass sich die ausführbare Datei unterscheidet. Reduzieren Sie dieses Ergebnis nicht auf ein Pass- oder Fail-Symbol im Dashboard. Bewahren Sie beide Dateien auf und untersuchen Sie die Abweichung auf Byte-Ebene mit einem geeigneten Binärvergleichswerkzeug.

Ordnen Sie jede Abweichung einer von vier Kategorien zu: beabsichtigte Release-Metadaten, Leck aus der Umgebung, nichtdeterministische Werkzeugausgabe oder unbekannt. Die unbekannte Kategorie muss sichtbar bleiben. Teams geraten in Schwierigkeiten, wenn sie eine breite Ausschlussregel hinzufügen, etwa alle PLIST-Dateien oder alle Signaturen zu ignorieren, um den Vergleich grün zu machen. Ausschlüsse sollten das einzelne erwartete Feld nennen und erklären, warum es sich ändert.

Ein nützlicher Fehlerdatensatz enthält die beiden Quell-Commit-Hashes, die exakten Build-Befehle, die Werkzeugversionen, eine Liste der abweichenden Dateien und für jede erlaubte Abweichung die Erklärung oder den Verweis auf ein Issue. Dieser Datensatz gehört zur Release-Entwicklung und nicht in die private Terminal-Historie einer einzelnen Person.

Eine Prüfroutine sollte mit heruntergeladenen Releases funktionieren

API-Zugangsdaten kontrollieren
Leiten Sie Bearer-, Basic- und benutzerdefinierte Header-Zugangsdaten über Sallyport, statt sie Agenten zu übergeben.

Ein Nutzer, der ein Release herunterlädt, braucht einen kurzen Prüfweg, der die Integrität des Archivs, die Herausgeberidentität und die Übereinstimmung mit der Quelle voneinander trennt.

Prüfen Sie zuerst den Archiv-Hash gegen ein signiertes Manifest, das über einen unabhängigen Kanal bezogen wurde. Unter macOS ist shasum bereits verfügbar:

shasum -a 256 AppName-1.2.3.dmg

Vergleichen Sie den ausgegebenen Hash Zeichen für Zeichen mit dem Manifest. Prüfen Sie anschließend die eingehängte App mit codesign und spctl, um die erwartete Signaturinstanz zu bestätigen und festzustellen, ob macOS das Bundle akzeptiert. Diese Prüfungen schützen vor einem beschädigten oder ausgetauschten Download, stellen aber noch keine Übereinstimmung mit der Quelle her.

Für diese abschließende Aussage bezieht der Prüfer den signierten Quellen-Tag, prüft dessen Signierenden, folgt der angegebenen Build-Anleitung und vergleicht die genannte unsignierte Ausgabe oder den Hash der ausführbaren Datei. Ein Projekt kann das mit einem Prüfskript erleichtern, doch Skripte sollten lesbar und klein bleiben. Ein tausend Zeilen langes Release-Skript, das das halbe Internet herunterlädt, ist kein Prüfmechanismus. Es ist ein weiteres undurchsichtiges Build-System.

Halten Sie den normalen Weg angemessen. Die meisten Nutzer werden ein signiertes Release-Manifest und die Signatur der Anwendung prüfen. Maintainer, Sicherheitsteams und unabhängige Prüfer sollten ausgewählte Releases oder Releases vor einem breiten Einsatz neu bauen. Entscheidend ist, dass eine gründlichere Prüfung möglich wird und für die Verantwortlichen zum normalen Ablauf gehört.

Verwechseln Sie ein Aktivitätsaudit nicht mit der Prüfung eines Artefakts. Sallyport kann die Hash-Kette seiner verschlüsselten Audit-Historie offline mit sp audit verify prüfen. Das beantwortet die Frage, ob aufgezeichnete Agent-Aktionen verändert wurden. Die Reproduzierbarkeit eines Releases betrifft eine andere Frage: ob der installierte Client der von seinen Maintainers behaupteten Quelle und Anleitung entspricht.

CI-Provenienz ist ein Beleg, kein Ersatz für Rebuilds

Die Aktionsgrenze überprüfen
Sallyport führt HTTP- und SSH-Aktionen selbst aus und gibt Ergebnisse zurück, ohne das zugrunde liegende Geheimnis offenzulegen.

CI-Provenienz hält fest, wo ein Build-Job lief und welche deklarierten Eingaben er verwendet hat. Das hilft bei Untersuchungen, kann aber nicht unabhängig feststellen, dass die Job-Ausgabe dem geprüften Quellcode entspricht.

Das SLSA-Provenienzmodell unterscheidet sinnvoll zwischen einem Artefakt und Aussagen darüber, wie ein Build-System es erzeugt hat. Eine signierte Provenienz-Aussage kann einen Artefakt-Hash mit einer Build-Definition und einer Quellrevision verbinden. Das ist ein starker Beleg, wenn Sie dem Repository, der CI-Identität, der Isolierung des Runners und den Release-Berechtigungen vertrauen. Sie müssen dem Builder trotzdem vertrauen.

Ein unabhängiger Rebuild verändert diese Vertrauensstruktur. Er fragt, ob ein separater Rechner, der von jemand anderem kontrolliert wird, die öffentliche Anleitung ausführen und zum selben vergleichbaren Ergebnis gelangen kann. Stimmen Provenienz und unabhängige Reproduktion überein, muss ein Angreifer mehr als eine Release-Seite oder eine einzelne CI-Umgebung kompromittieren, um eine ausgetauschte ausführbare Datei zu verbergen.

Veröffentlichen Sie die Provenienz mit genügend Details für eine Prüfung: Quellrevision, Revision der Build-Definition, Kennung des Runner-Images, Hashes der Abhängigkeiten, Befehlsparameter und Ausgabe-Hash. Vermeiden Sie vage Bestätigungen, die nur sagen, dass ein Workflow erfolgreich war. Ein erfolgreicher Workflow kann den falschen Branch kompilieren, eine veränderliche Abhängigkeit verwenden oder eine Datei aus einem veralteten Workspace hochladen.

Auch CI braucht eine Trennung der Aufgaben. Die Identität, die den Quellcode ändern kann, sollte nicht automatisch Zugriff auf die Signaturberechtigung des Releases erhalten. Der Job, der eine App verpackt, sollte kein Signaturgeheimnis stillschweigend abrufen, bevor Prüfer den Release-Commit genehmigt haben. Diese Kontrollen machen eine Binärdatei nicht reproduzierbar, verringern aber die Wahrscheinlichkeit, dass ein einzelnes kompromittiertes Konto Quelle, Build und Distribution gemeinsam verändert.

Verankern Sie die Prüfung im Release-Vertrag

Teams erhalten reproduzierbare Releases, indem sie die Prüfung zu einem erwarteten Ergebnis jedes Releases machen, nicht zu einem Forschungsprojekt, das jemand erst nach einem Vorfall beginnt.

Beginnen Sie mit einem Release-Ziel und machen Sie die unsignierte ausführbare Datei in zwei sauberen Verzeichnissen reproduzierbar. Erfassen Sie jede Eingabe, die Sie entdecken, besonders erzeugten Code und Binärabhängigkeiten. Veröffentlichen Sie anschließend den signierten Tag, die Build-Anleitung, das Manifest, die Ausgabe-Hashes, Signaturmetadaten und alle erlaubten Abweichungen. Wiederholen Sie die Prüfung in CI, bewahren Sie aber einen Weg, über den ein externer Rechner dieselbe Arbeit ausführen kann.

Versprechen Sie keine Byte-Identität für Dateien, die Ihr Prozess nach dem vergleichbaren Build bewusst verändert. Benennen Sie die Grenze klar. Leser können mit einer ehrlichen Aussage über ein unsigniertes Bundle und einer getrennt geprüften Signierungsstufe arbeiten. Mit einer umfassenden Behauptung, die beim ersten abweichenden Hash zerfällt, können sie nichts anfangen.

Der erste harte Test ist einfach: Bauen Sie das nächste Release-Kandidaten in einem frischen Konto und vergleichen Sie die ausführbare Datei vor der Signierung. Weicht das Ergebnis ab, suchen Sie weiter, bis Sie die Bytes, die Eingabe, die sie erzeugt hat, und die Frage benennen können, ob diese Eingabe in die dokumentierte Anleitung gehört. So wird ein heruntergeladener Agent-Client zu etwas, das ein Team prüfen kann, statt ihm lediglich zu vertrauen.

FAQ

Beweist die Codesignatur, dass eine heruntergeladene App aus ihrem öffentlichen Quellcode stammt?

Eine signierte macOS-Anwendung zeigt, welche Developer ID das ausgelieferte Bundle signiert hat und ob spätere Änderungen die Signatur ungültig gemacht haben. Sie beweist jedoch nicht, dass ein bestimmter öffentlicher Commit die ausführbare Datei erzeugt hat. Dafür brauchen Sie eine Quellenreferenz, eine genaue Build-Anleitung und ein Vergleichsverfahren.

Müssen reproduzierbare Builds immer identische App-Bundles ergeben?

Nein. Eine Übereinstimmung Byte für Byte liefert den klarsten Nachweis, ist bei signierten macOS-Bundles aber nicht immer möglich, weil Signierung und Notarisierung Dateien nach der Kompilierung verändern. Ein sinnvolles Ziel ist eine dokumentierte und erklärbare Übereinstimmung der ausführbaren Datei und der Ressourcen vor der Release-Signierung, ergänzt durch separat geprüfte Signaturdaten.

Sollten wir aus einem Branch oder aus einem Release-Tag bauen?

Prüfen Sie zuerst die Signatur des Tags, sehen Sie sich den darin genannten Commit an und kontrollieren Sie, dass das Release-Manifest denselben Commit nennt. Ein geschützter Branch ist für die Zusammenarbeit nützlich, aber kein kryptografischer Nachweis dafür, was ein Maintainer veröffentlicht hat. Betrachten Sie den annotierten, signierten Tag als Referenz für den Release-Quellcode.

Warum unterscheiden sich zwei signierte macOS-Apps nach demselben Build?

Apple fügt dem Bundle ein CodeDirectory, Signaturdaten, Informationen zur Zertifikatskette und oft einen Zeitstempel hinzu. Diese Einträge hängen von der Signaturidentität und der Zeit ab, deshalb unterscheiden sich unabhängig signierte Kopien normalerweise. Vergleichen Sie, wenn möglich, unsignierte Build-Ergebnisse und prüfen Sie anschließend jedes signierte Bundle separat.

Reicht eine Lockdatei für Abhängigkeiten bei reproduzierbaren Builds aus?

Eine Lockdatei verringert die Mehrdeutigkeit nur dann, wenn der Build sie tatsächlich erzwingt und jedes abgerufene Artefakt einen Hash besitzt. Paket-Registry können Pakete entfernen oder verändern, und manche Abhängigkeitsmanager lösen beim Build Metadaten auf. Archivieren Sie Abhängigkeiten oder verwenden Sie für Releases, die andere noch Jahre später reproduzieren sollen, einen Cache mit geprüften Prüfsummen.

Kann ich einen Build auf meinem eigenen Mac prüfen?

Verwenden Sie ein sauberes macOS-Konto oder eine kurzlebige virtuelle Maschine, installieren Sie die dokumentierte Xcode-Version, beziehen Sie den signierten Quellen-Tag und führen Sie den veröffentlichten Befehl aus. Deaktivieren Sie den Netzwerkzugriff, sobald die Abhängigkeiten vorbereitet sind. Vergleichen Sie die Hashes der erzeugten ausführbaren Datei und untersuchen Sie Unterschiede, bevor Sie eine teilweise Übereinstimmung akzeptieren. Zweimal auf demselben Laptop zu bauen ist ein sinnvoller erster Test, deckt aber keine hostspezifischen Eingaben auf.

Was ist der Unterschied zwischen einer SBOM und Build-Provenienz?

Nein. Eine Software-Stückliste listet Komponenten auf, während die Provenienz festhält, wo und wie ein Build ausgeführt wurde. Beides hilft bei der Untersuchung, beweist aber keine Reproduzierbarkeit, solange eine unabhängige Partei die Anleitung nicht erneut ausführen und das Ergebnis vergleichen kann.

Welche Dateien sollte ich bei einem macOS-App-Release vergleichen?

Bauen Sie zuerst die ausführbare Datei erneut, denn sie enthält den ausgeführten Code. Vergleichen Sie anschließend eingebettete Provisioning-Daten, Berechtigungen, Hilfsprogramme, Ressourcendateien und das Updater-Manifest, falls die App eines besitzt. Ein übereinstimmendes äußeres Disk-Image ist ein schwächerer Nachweis, weil sich die Verpackung ändern kann, ohne dass sich die ausführbare Datei ändert.

Kann CI-Provenienz einen unabhängigen Rebuild ersetzen?

Nein. Der Server kann seinen eigenen Job beglaubigen, aber er nimmt Ihnen das Vertrauen in die Build-Umgebung, die Repository-Berechtigungen, die Quellen der Abhängigkeiten oder den Uploader nicht ab. Unabhängige Rebuilds liefern eine zweite Beweisspur und machen oft Fehler sichtbar, die CI unbemerkt übernommen hat.

Was sollten wir tun, wenn ein reproduzierter Build nicht übereinstimmt?

Behandeln Sie eine Abweichung als Untersuchung und ändern Sie den erwarteten Hash nicht einfach, bis er passt. Halten Sie den Quellen-Commit, Tool-Versionen, Maschinenarchitektur, Abhängigkeits-Hashes und die exakt abweichenden Dateien fest. Entsteht die Abweichung durch eine absichtliche Signierungs- oder Verpackungsstufe, trennen Sie diese Stufe vom vergleichbaren Build-Ergebnis und dokumentieren Sie sie.

Sallyport

Sallyport führt API-Aufrufe und SSH-Befehle für Ihren KI-Agenten aus. Die Schlüssel bleiben in einem lokalen Tresor auf Ihrem Mac; Sie geben jeden Lauf frei, und jede Aktion landet in einem versiegelten Journal.

© 2026 Sallyport · Open Source unter Apache-2.0 · Oleg Sotnikov