Ein sauberer TLS-Aufbau ist für API-Tests, Deployments und Automatisierung wichtiger als viele denken. Die Meldung curl unable to get local issuer certificate weist fast immer darauf hin, dass curl den ausstellenden CA-Eintrag nicht bis zum eigenen Trust Store auflösen kann. In diesem Artikel zeige ich, wie ich den Fehler eingrenze, sauber behebe und in Skripten dauerhaft vermeide.
Die wichtigsten Punkte auf einen Blick
- curl prüft Zertifikate standardmäßig; Fehler 60 bedeutet fast immer ein Problem mit der Vertrauenskette.
- Wenn der Browser funktioniert, curl aber nicht, fehlt oft ein Zwischenzertifikat auf dem Server oder der Client nutzt einen anderen CA-Store.
- Die sauberen Lösungen sind `--ca-native`, `--cacert` oder `--capath` - nicht `-k`.
- Auf Windows hängt viel davon ab, ob curl mit Schannel oder mit einem anderen TLS-Backend läuft.
- In Firmenumgebungen muss der Proxy getrennt vom Zielserver vertrauenswürdig konfiguriert werden.
Was die Meldung technisch bedeutet
Hinter der Fehlermeldung steckt kein kryptischer „curl-Defekt“, sondern eine gescheiterte Zertifikatsprüfung im TLS-Handshake. curl vergleicht das Serverzertifikat nicht nur mit dem Hostnamen, sondern baut auch die Zertifikatskette bis zu einer vertrauenswürdigen CA auf. „Local issuer“ meint dabei nicht den Server vor Ort, sondern die ausstellende Zertifizierungsstelle, die in deinem lokalen Trust Store bekannt sein muss.
Wenn diese Kette nicht vollständig oder nicht vertrauenswürdig ist, bricht curl mit Fehler 60 ab. Das ist gewollt, denn genau an dieser Stelle schützt dich der Client vor einer Verbindung zu einem falschen oder manipulierten Ziel. Ich behandle das deshalb immer als Sicherheitsproblem, nicht als lästige Warnung, die man „wegklicken“ sollte. Genau deshalb schaue ich als Nächstes zuerst auf die typischen Ursachen, nicht auf ein zufälliges Flag.Die häufigsten Ursachen in der Praxis
In der Praxis wiederholen sich vier bis fünf Muster ständig. Wenn ich sie auseinanderhalte, spare ich mir viel Trial-and-Error.
| Ursache | Typisches Muster | Was ich daraus ableite |
|---|---|---|
| Unvollständige Zertifikatskette auf dem Server | Browser funktioniert, curl scheitert auf einer frischen Maschine | Dem Server fehlen Zwischenzertifikate; die Kette wird nicht vollständig ausgeliefert |
| Private CA oder selbstsigniertes Zertifikat | Nur interne Systeme betroffen, öffentliches Web nicht | Die interne CA ist nicht im Trust Store des Clients enthalten |
| Veralteter oder falscher CA-Store | Neue Zertifikate schlagen fehl, alte funktionieren noch | Das lokale CA-Bundle ist zu alt, zu minimal oder zeigt auf die falsche Quelle |
| TLS-Inspection oder HTTPS-Proxy | Im Firmennetz Fehler, außerhalb des Netzes nicht | Der Proxy hat ein eigenes Vertrauensproblem und muss getrennt betrachtet werden |
| Anderes TLS-Backend als erwartet | Unter Windows unterschiedliche Ergebnisse je nach Build | curl nutzt nicht unbedingt denselben Store wie der Browser |
So prüfe ich die Ursache mit curl und Logs
Ich starte mit einer sauberen Minimalprüfung und erhöhe erst dann die Komplexität. Das Ziel ist nicht, sofort eine Lösung zu erzwingen, sondern zuerst zu sehen, welche Vertrauensquelle curl wirklich benutzt und an welcher Stelle die Kette abreißt.
curl -v https://ziel.tld
curl --ca-native -v https://ziel.tld
curl -w '%{certs}' https://ziel.tld > chain.pem
- Ich schaue zuerst in `curl -v` nach `CAfile` und `CApath`. Dort sehe ich, ob curl ein Bundle, ein Verzeichnis oder den nativen Store verwendet.
- Dann prüfe ich `issuer` und `subject` im Verbose-Output. Wenn der ausgestellte Zwischenschritt fehlt, ist die Zertifikatskette auf Serverseite meist unvollständig.
- Mit `--ca-native` teste ich, ob das Problem nur am eingebetteten Bundle liegt. Wenn der Aufruf dann funktioniert, ist die Ursache meist ein Trust-Store-Mismatch.
- Mit `-w '%{certs}'` exportiere ich die vom Server gelieferte Kette, um zu sehen, ob Zwischenzertifikate fehlen oder ob ein falsches Zertifikat ausgeliefert wird.
- Wenn ein Proxy im Spiel ist, teste ich den Zielserver und den Proxy getrennt. Sonst repariere ich am Ende die falsche Seite der Verbindung.
Ich bewerte das Ergebnis nie isoliert am Arbeitsplatzrechner. Ein lokaler Test kann gut aussehen, während dieselbe Anfrage in einem Container, auf einem Build-Agenten oder hinter einem Proxy scheitert. Sobald ich den Befund kenne, trenne ich sauber zwischen Serverfehler, Client-Trust und Proxy-Trust.
Wie ich Zertifikatskette und Trust Store sauber repariere
Die beste Lösung ist fast immer die, die die Ursache behebt. Wenn ich den Server kontrolliere, liefere ich die komplette Zertifikatskette aus, also Leaf-Zertifikat plus Zwischenzertifikate. Das ist der Punkt, an dem viele Setups scheitern: Das Serverzertifikat ist korrekt, aber der Webserver, Reverse Proxy oder Ingress liefert die Kette unvollständig aus.
Wenn ich den Client kontrolliere, ergänze ich den passenden CA-Eintrag im Trust Store oder zeige curl auf ein konkretes Bundle. Für interne PKIs ist das normal. Für öffentliche Dienste sollte das hingegen gar nicht nötig sein, weil die Root- und Zwischenzertifikate in den üblichen Stores vorhanden sein müssen.
| Option | Wann ich sie nutze | Worauf ich achte |
|---|---|---|
--ca-native |
Wenn ich den Betriebssystem-Trust-Store nutzen will, etwa auf Windows oder macOS | Das ist meist die sauberste Lösung auf Systemen mit gepflegtem nativen Store |
--cacert /pfad/zur/ca.pem |
Wenn ich ein bestimmtes PEM-Bundle oder eine private CA fest vorgeben will | Die Datei muss im PEM-Format vorliegen und muss die richtigen Trust Anchors enthalten |
--capath /pfad/zum/verzeichnis |
Wenn ich ein CA-Verzeichnis pflege, besonders bei OpenSSL-basierten Builds | Das Verzeichnis muss korrekt vorbereitet sein, bei OpenSSL typischerweise mit `c_rehash` |
CURL_CA_BUNDLE, SSL_CERT_FILE, SSL_CERT_DIR
|
Wenn ich in CI, Containern oder Shell-Umgebungen einen festen Trust-Pfad setzen will | Praktisch für Automatisierung, aber nur sinnvoll, wenn die Datei oder das Verzeichnis sauber versioniert ist |
-k / --insecure
|
Nur für kurzfristiges Debugging in isolierten Tests | Verifikation wird abgeschaltet; das ist kein Fix, sondern ein Sicherheitsrisiko |
Auf Windows ist der Unterschied zwischen Schannel und anderen Backends besonders wichtig. Mit Schannel nutzt curl standardmäßig den nativen Windows-Store. Mit anderen Backends braucht es häufiger ein eigenes Bundle oder einen expliziten Pfad. Wenn ich auf einem nicht-Schannel-Build unter Windows arbeite, prüfe ich zusätzlich, ob `curl-ca-bundle.crt` überhaupt an der Stelle liegt, die der Build erwartet. Damit ist die Ursache oft schon beseitigt, bevor ich überhaupt an Workarounds denke. Danach bleibt die Frage, welche Option im Alltag wirklich sauber ist und welche ich nur im Notfall anrühre.
Welche Optionen ich wann nutze und welche ich meide
Ich verwende `-k` nur dann, wenn ich ein Problem eingrenzen will und die Verbindung keinerlei produktive Bedeutung hat. Bei TLS bedeutet `--insecure`, dass curl die Zertifikatsprüfung aussetzt; bei SFTP und SCP wird zusätzlich die `known_hosts`-Prüfung übersprungen. Das ist bequem, aber sicherheitstechnisch falsch, sobald Daten, Zugangsdaten oder produktive Systeme im Spiel sind.
Für echte Probleme gibt es bessere Entscheidungen:
- Private CA im Unternehmen: Ich verteile die CA sauber und nutze `--cacert` oder den Betriebssystem-Trust-Store.
- Desktop oder Admin-Rechner: Ich bevorzuge `--ca-native`, wenn der Build das unterstützt und der native Store gepflegt ist.
- Container und CI: Ich installiere die CA-Basis im Image statt zur Laufzeit an jedem Job herumzuflicken.
- Proxy-Szenarien: Ich prüfe Server und Proxy getrennt mit `--proxy-ca-native` oder `--proxy-cacert`, statt beide Ebenen zu vermischen.
Der wichtigste Denkfehler ist aus meiner Sicht dieser: „Im Browser geht es doch, also ist curl zu streng.“ Nein. Meistens zeigt curl nur früher und klarer, dass die Kette nicht sauber ist oder dass der Client nicht den Store benutzt, den man erwartet. Genau darum ist der Fehler diagnostisch wertvoll. Damit der Fehler nicht bei jedem Container- oder Zertifikatswechsel wieder auftaucht, brauche ich noch einen dauerhaften Betriebsstandard.
Wie ich den Fix für Server, Container und Pipelines dauerhaft absichere
Wenn ich das Thema nachhaltig lösen will, behandle ich Zertifikate wie einen Teil der Infrastruktur und nicht wie ein einmaliges Browserproblem. Auf der Serverseite bedeutet das: Jede Deployment-Pipeline muss die komplette Zertifikatskette ausliefern, einschließlich der Zwischenzertifikate, und das nach jeder Erneuerung validieren.
- Serverseitig testen: Nach jedem Zertifikatswechsel prüfe ich mit `curl -v` und einem frischen Client, ob die Kette vollständig ist.
- Container härten: Ich halte das CA-Paket im Base-Image aktuell und verlasse mich nicht auf zufällige Host-Zustände.
- Proxies getrennt behandeln: Für interne TLS-Inspection richte ich den Proxy-Trust separat ein, statt den Origin-Trust zu verbiegen.
- Automatisierung dokumentieren: In Skripten schreibe ich explizit hinein, welches CA-Bundle genutzt wird und warum.
- Erneuerungen planen: Root- und Intermediate-Rotationen prüfe ich frühzeitig, damit alte Images nicht plötzlich ausfallen.
Wenn Kette, Trust Store und Proxy-Vertrauen sauber zusammenpassen, verschwindet der Fehler ohne Sicherheitskompromiss. Genau das ist für mich die robuste Lösung: nicht Verification abschalten, sondern die Vertrauensbasis korrekt aufbauen und in Betrieb, CI und Container durchziehen.