Heute habe ich versucht, mich von meiner neuen Linux Mint Installation aus mit einem meiner SSH-Server zu verbinden. Mein SSH-Client hat mich direkt gefragt, ob ich dem Hostkey vertrauen möchte:
ssh username@hostname.kernel-error.org The authenticity of host 'hostname.kernel-error.org (2a01:5a8:362:4416::32)' can't be established. ED25519 key fingerprint is SHA256:kTRGVCMRLiHfvJunW2CbW5H3NZmn3Wkx2KnHJXl3iJu. This key is not known by any other names Are you sure you want to continue connecting (yes/no/[fingerprint])?
Für viele ist das normal — man tippt „yes“ und sieht die Meldung nie wieder. Aber diese Meldung hat ihren Grund. Beim ersten Verbindungsaufbau zeigt SSH den Fingerprint des Server-Hostkeys an, damit man prüfen kann, ob man wirklich mit dem richtigen Server spricht und nicht mit einem Angreifer. Wer eh immer „yes“ sagt, könnte den Check auch gleich in seiner ~/.ssh/config abschalten:
Host *
StrictHostKeyChecking no
SSHFP — Hostkeys per DNS verifizieren
Es gibt einen besseren Weg: SSHFP-Records (RFC 4255). Man hinterlegt die Fingerprints der erwarteten Hostkeys als DNS-Einträge. Der SSH-Client prüft diese automatisch — vorausgesetzt die DNS-Antwort ist per DNSSEC abgesichert. In der ~/.ssh/config:
Host * VerifyHostKeyDNS yes
Meine DNS-Server unterstützen alle DNSSEC, mein lokaler Resolver auf dem Router auch, die SSH-Config stimmt — und trotzdem erscheint die Meldung. Also mit ssh -vvv debuggen:
debug1: found 2 insecure fingerprints in DNS
Insecure. SSH findet die SSHFP-Records, vertraut ihnen aber nicht, weil die DNS-Antwort nicht als DNSSEC-validiert markiert ist.
Das Problem: systemd-resolved
Schneller Test mit dig +dnssec gegen Google DNS:
dig +dnssec hostname.kernel-error.org @8.8.8.8 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
Das ad-Flag (Authenticated Data) ist gesetzt — meine DNS-Server liefern DNSSEC korrekt aus. Auch der lokale Router-Resolver liefert ad. Aber ohne expliziten @server:
dig +dnssec hostname.kernel-error.org ;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
Kein ad. Was steht in /etc/resolv.conf? 127.0.0.53 — systemd-resolved. Der Stub-Resolver von systemd schluckt das AD-Flag.
Man könnte in /etc/systemd/resolved.conf einfach DNSSEC=yes setzen — bei mir ging danach aber gar keine DNS-Auflösung mehr. Das liegt am Stub-Resolver, den man ebenfalls umkonfigurieren müsste. Nennt mich oldschool, aber für meine Zwecke reicht der klassische Weg über die vom NetworkManager gepflegte resolv.conf.
Lösung: systemd-resolved abschalten
sudo systemctl disable systemd-resolved sudo systemctl stop systemd-resolved sudo rm /etc/resolv.conf
In /etc/NetworkManager/NetworkManager.conf in der [main]-Sektion:
dns=default
NetworkManager neu starten:
sudo systemctl restart NetworkManager cat /etc/resolv.conf # Generated by NetworkManager search kernel-error.local nameserver 10.10.88.1 nameserver fd00:424e:6eff:f525:454e:6eff:f525:4241
DNS-Auflösung geht. Aber SSH sagt weiterhin „insecure“. Es fehlen noch zwei Optionen in der resolv.conf.
edns0 und trust-ad
Erste Erkenntnis — edns0 muss aktiviert sein, damit DNSSEC-Daten überhaupt transportiert werden. In /etc/resolv.conf:
options edns0
Jetzt zeigt dig das ad-Flag. Aber SSH sagt immer noch „insecure“. Warum? Ein Blick in den SSH-Quellcode — die ldns-Bibliothek macht die DNSSEC-Validierung:
/* Check for authenticated data */
if (ldns_pkt_ad(pkt)) {
rrset->rri_flags |= RRSET_VALIDATED;
} else { /* AD is not set, try autonomous validation */
ldns_rr_list * trusted_keys = ldns_rr_list_new();
/* ... */
if ((err = ldns_verify_trusted(ldns_res, rrdata, rrsigs,
trusted_keys)) == LDNS_STATUS_OK) {
rrset->rri_flags |= RRSET_VALIDATED;
}
}
ldns prüft das AD-Flag im DNS-Paket. Aber die glibc setzt das AD-Flag in der Antwort nur dann, wenn trust-ad in der resolv.conf steht — sonst wird es aus Sicherheitsgründen herausgefiltert. Die vollständige Option:
options edns0 trust-ad
Und jetzt:
ssh username@hostname.kernel-error.org -vvv [...] debug1: found 2 secure fingerprints in DNS debug3: verify_host_key_dns: checking SSHFP type 4 fptype 1 debug1: verify_host_key_dns: matched SSHFP type 4 fptype 1 debug3: verify_host_key_dns: checking SSHFP type 4 fptype 2 debug1: verify_host_key_dns: matched SSHFP type 4 fptype 2 debug1: matching host key fingerprint found in DNS
secure statt insecure. SSH verifiziert den Hostkey automatisch per DNSSEC — keine manuelle Fingerprint-Prüfung mehr nötig.
Rebootfest machen
Die manuell eingetragenen Optionen in der resolv.conf überleben keinen Reboot — der NetworkManager überschreibt die Datei. Per nmcli die Optionen dauerhaft im Netzwerkprofil setzen, für IPv4 und IPv6:
nmcli conn modify DEINE-PROFIL-UUID ipv4.dns-options edns0,trust-ad nmcli conn modify DEINE-PROFIL-UUID ipv6.dns-options edns0,trust-ad
Die UUID des aktiven Profils findet man mit nmcli conn show. Beide Zeilen sind nötig — fehlt eine, greift es nicht.
Zusammenfassung: systemd-resolved unter Linux Mint und Ubuntu filtert das DNSSEC-AD-Flag heraus. Ohne AD-Flag kann SSH die SSHFP-Records nicht als vertrauenswürdig einstufen. Lösung: systemd-resolved abschalten, NetworkManager mit dns=default nutzen, edns0,trust-ad per nmcli setzen.
Wer einen DNSSEC-validierenden Resolver sucht — dns.kernel-error.de ist ein öffentlicher DNS-Resolver mit DNSSEC, DNS over TLS und DNS over HTTPS.
Und die offene Frage: Ich bin mit meinem FreeBSD-Wissen an das Thema gegangen. Wie macht man das als Linux-User mit systemd-resolved richtig? Schreibt mir, wenn ihr es wisst.
Siehe auch: SSH Host Keys per SSHFP























