Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen Revision Vorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
linux:ssh:tofu_und_cert [22.04.2025 18:07. ] – [signierte SSH-Keys - SSH-Zertifikate] typofix djangolinux:ssh:tofu_und_cert [28.04.2025 09:20. ] (aktuell) – Haeder angepasst django
Zeile 1: Zeile 1:
-{{htmlmetatags>metatag-robots=()  
-metatag-keywords=(TOFU,SSH,Zertifikate,ED25519,SHA256,Arch Linux, sicherer IT Betrieb,authorized_keys)  
-metatag-description=(TOFU - Trust On First Use - SSH Zertifikate) 
-}} 
 ====== TOFU - Trust On First Use - SSH Zertifikate ====== ====== TOFU - Trust On First Use - SSH Zertifikate ======
 ===== Ausgangssituation ===== ===== Ausgangssituation =====
Zeile 35: Zeile 31:
 Im obigen Abschnitt **[[#ausgangssituation|Ausgangssituation]]** haben wir schon gesehen, dass beim ersten Verbindungsaufbau uns mitgeteilt wird, dass der erkannte Fingerprint des ssh_host_ed25519_key unbekannt ist und daher dem System erst einmal nicht vertraut wird. Üblich ist nun hier, den Fingerprint zu akzeptieren wenn man sich das erste Mal mit einem Host verbindet. Die präsentierte Warnung wird also einfach als notwendiges Übel abgetan. Und genau hier sind wir nun mittendrin beim Thema **TOFU**! Was sollen, nein was werden wir nun tun? Ganz einfach, wir nehmen diese SSH-Warnung ernst und tun sie nicht ab also lästiges vernachlässigbares Übel!  Im obigen Abschnitt **[[#ausgangssituation|Ausgangssituation]]** haben wir schon gesehen, dass beim ersten Verbindungsaufbau uns mitgeteilt wird, dass der erkannte Fingerprint des ssh_host_ed25519_key unbekannt ist und daher dem System erst einmal nicht vertraut wird. Üblich ist nun hier, den Fingerprint zu akzeptieren wenn man sich das erste Mal mit einem Host verbindet. Die präsentierte Warnung wird also einfach als notwendiges Übel abgetan. Und genau hier sind wir nun mittendrin beim Thema **TOFU**! Was sollen, nein was werden wir nun tun? Ganz einfach, wir nehmen diese SSH-Warnung ernst und tun sie nicht ab also lästiges vernachlässigbares Übel! 
  
-Ja, eine Verbindung ist später dann verschlüsselt dank des SSH-Schlüsselmaterials von Client und Server von dessen Public-Key die jeweiligen Endstellen Kenntnis haben. Tatsache ist aber auch dass SSH die Authentizität des Hosts nicht überprüfen kann und das ist ein ernstes Problem! Verschlüsselte Kommunikation ist sehr gut aber ist sie denn auch vertrauenswürdig, sprich haben wir uns wirklich mit dem Zielsystem verbunden, oder ist da ein kompromittiertes System als **MITM**((**M**an**I**n**T**he**M**iddle)) im Einsatz? Was das bei Passwort gestützten SSH-Verbindungen bedeutet, ist wohl jedem glasklar - ja genau daher kommt bei uns auch auch nur Schlüsselbasierter Zugriff zum Einsatz! +Ja, eine Verbindung ist später dann verschlüsselt dank des SSH-Schlüssel Materials von Client und Server von dessen Public-Key die jeweiligen Endstellen Kenntnis haben. Tatsache ist aber auch dass SSH die Authentizität des Hosts nicht überprüfen kann und das ist ein ernstes Problem! Verschlüsselte Kommunikation ist sehr gut aber ist sie denn auch vertrauenswürdig, sprich haben wir uns wirklich mit dem Zielsystem verbunden, oder ist da ein kompromittiertes System als **MITM**((**M**an**I**n**T**he**M**iddle)) im Einsatz? Was das bei Passwort gestützten SSH-Verbindungen bedeutet, ist wohl jedem glasklar - ja genau daher kommt bei uns auch auch nur Schlüssel basierter Zugriff zum Einsatz! 
  
 Beim ersten Verbindungsaufbau werden wir also nun in unserem Beispiel hier eben darauf hingewiesen, dass wir dem System noch nicht vertrauen und wir das Vertrauen entweder bestätigen oder vielleicht auch auf andere Weise anderweitig herstellen wollen. Doch hierzu später mehr im Abschnitt **[[#loesung_smoeglichkeit|Lösung(smöglichkeit)]]**. Beim ersten Verbindungsaufbau werden wir also nun in unserem Beispiel hier eben darauf hingewiesen, dass wir dem System noch nicht vertrauen und wir das Vertrauen entweder bestätigen oder vielleicht auch auf andere Weise anderweitig herstellen wollen. Doch hierzu später mehr im Abschnitt **[[#loesung_smoeglichkeit|Lösung(smöglichkeit)]]**.
Zeile 64: Zeile 60:
 Die SSH implementiert das Protokoll zur Authentifizierung mit öffentlichem Schlüssel automatisch, wobei einer der Algorithmen DSA, ECDSA, Ed25519 oder RSA verwendet wird.   Die SSH implementiert das Protokoll zur Authentifizierung mit öffentlichem Schlüssel automatisch, wobei einer der Algorithmen DSA, ECDSA, Ed25519 oder RSA verwendet wird.  
  
-Die Datei **''~/.ssh/authorized_keys''** listet die öffentlichen Schlüssel auf, die für das Einloggen erlaubt sind.  Wenn sich der Benutzer anmeldet, teilt das ssh-Programm dem Server mit, welches Schlüsselpaar es für die Authentifizierung verwenden möchte. Der Client weist nach, dass er Zugriff auf den privaten Schlüssel hat, und der Server prüft, ob der entsprechende öffentliche Schlüssel berechtigt ist, das Konto zu akzeptieren.+Die Datei **''~/.ssh/authorized_keys''** listet die öffentlichen Schlüssel auf, die für das Einloggen erlaubt sind.  Wenn sich der Benutzer anmeldet, teilt das SSH-Programm dem Server mit, welches Schlüsselpaar es für die Authentifizierung verwenden möchte. Der Client weist nach, dass er Zugriff auf den privaten Schlüssel hat, und der Server prüft, ob der entsprechende öffentliche Schlüssel berechtigt ist, das Konto zu akzeptieren.
  
 Der Server kann den Client über Fehler informieren, die den Erfolg der Authentifizierung mit dem öffentlichen Schlüssel verhindert haben, nachdem die Authentifizierung mit einer anderen Methode abgeschlossen wurde.  Diese können angezeigt werden, indem der LogLevel auf DEBUG oder höher erhöht wird (z. B. durch Verwendung der Option -v). Der Server kann den Client über Fehler informieren, die den Erfolg der Authentifizierung mit dem öffentlichen Schlüssel verhindert haben, nachdem die Authentifizierung mit einer anderen Methode abgeschlossen wurde.  Diese können angezeigt werden, indem der LogLevel auf DEBUG oder höher erhöht wird (z. B. durch Verwendung der Option -v).
Zeile 1841: Zeile 1837:
 ED25519 key fingerprint is SHA256:kIe5Ki/ItvQq9Cpfw/qLReo1wiVkdREVOz6a67I8nO4. ED25519 key fingerprint is SHA256:kIe5Ki/ItvQq9Cpfw/qLReo1wiVkdREVOz6a67I8nO4.
 This key is not known by any other names This key is not known by any other names
-Are you sure you want to continue connecting (yes/no/[fingerprint])?</code> \\ Wird jedoch ein gültiges Zertifikat und ein gültiger SSH HOST CA verwendet und sind ferner die Hostnamen im DNS gepflegt wird ein Verbindungsaufbau zum Zielhost anstandslos klappen. Wir entfernen aber noch mit Hilfe des Befehls **'' ssh-keygen -f  ~/.ssh/known_hosts -R <hostname>''** den bisher genutzten Eintrag für unseren Host, damikt auch wirklich ausgeschlossen werden kann dass uns ein alter Eintrag hier "in die Suppe spucken könnte". In unserem Beispiel entfernen wir den Eintrtag für den Host **kvm**. <code>$ ssh-keygen -f ~/.ssh/known_hosts -R kvm.intra.nausch.org</code><code># Host kvm.intra.nausch.org found: line 1408+Are you sure you want to continue connecting (yes/no/[fingerprint])?</code> \\ Wird jedoch ein gültiges Zertifikat und ein gültiger SSH HOST CA verwendet und sind ferner die Hostnamen im DNS gepflegt wird ein Verbindungsaufbau zum Zielhost anstandslos klappen. Wir entfernen aber noch mit Hilfe des Befehls **'' ssh-keygen -f  ~/.ssh/known_hosts -R <hostname>''** den bisher genutzten Eintrag für unseren Host, damit auch wirklich ausgeschlossen werden kann dass uns ein alter Eintrag hier "in die Suppe spucken könnte". In unserem Beispiel entfernen wir den Eintrag für den Host **kvm**. <code>$ ssh-keygen -f ~/.ssh/known_hosts -R kvm.intra.nausch.org</code><code># Host kvm.intra.nausch.org found: line 1408
 /home/django/.ssh/known_hosts updated. /home/django/.ssh/known_hosts updated.
 Original contents retained as /home/django/.ssh/known_hosts.old</code>Nun starten wir eine SSH-Session zu unserem Zielhost.<code>  $ ssh kvm.intra.nausch.org -v</code><code>... Original contents retained as /home/django/.ssh/known_hosts.old</code>Nun starten wir eine SSH-Session zu unserem Zielhost.<code>  $ ssh kvm.intra.nausch.org -v</code><code>...
Zeile 1906: Zeile 1902:
 TrustedUserCAKeys /etc/ssh/ssh_ca_user_key.pub TrustedUserCAKeys /etc/ssh/ssh_ca_user_key.pub
  
-...</code> Das Neustarten des Daemons verschieben wir noch etwas, da wir noch eine weitere Anpassung vornehmen werden. \\ Damit nun nicht jeder Admin sich auf jedem System mit Hilfe seines User-Zertifikates anmelden kann, bietet uns SSH die Option **''AuthorizedPrincipalsFile''** an. Hiermit kann festgelegt werden, ob sich der Admin, der gerade sein Zertifikat präsentiert, auch wirklich autorisiert ist sich anzumelden. Es könnte ja sein, dass unterschiedliche Admins sich nur auf eine begrenzte Anzahl von Maschinen verbinden darf, andere Admins unterliegen __nicht__ dieser Beschränkung. Bevor man nun auf die Idee kommt hierzu viele User CAs hierfür anzulegen, nutzen wir doch lieber Prüfung des principals, den wir beim Erstellen des User-Zertifikates angegeben hatten. Werfen wirdazu kurz einen Blick in das Zertifikat. <code> $ ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub</code><code>/home/django/.ssh/id_ed25519-cert.pub:+...</code> Das Neustarten des Daemons verschieben wir noch etwas, da wir noch eine weitere Anpassung vornehmen werden. \\ Damit nun nicht jeder Admin sich auf jedem System mit Hilfe seines User-Zertifikates anmelden kann, bietet uns SSH die Option **''AuthorizedPrincipalsFile''** an. Hiermit kann festgelegt werden, ob sich der Admin, der gerade sein Zertifikat präsentiert, auch wirklich autorisiert ist sich anzumelden. Es könnte ja sein, dass unterschiedliche Admins sich nur auf eine begrenzte Anzahl von Maschinen verbinden darf, andere Admins unterliegen __nicht__ dieser Beschränkung. Bevor man nun auf die Idee kommt hierzu viele User CAs hierfür anzulegen, nutzen wir doch lieber Prüfung des principals, den wir beim Erstellen des User-Zertifikates angegeben hatten. Werfen wir dazu kurz einen Blick in das Zertifikat. <code> $ ssh-keygen -L -f ~/.ssh/id_ed25519-cert.pub</code><code>/home/django/.ssh/id_ed25519-cert.pub:
         Type: ssh-ed25519-cert-v01@openssh.com user certificate         Type: ssh-ed25519-cert-v01@openssh.com user certificate
         Public key: ED25519-CERT SHA256:IqPRdnvM1+xSqaESGaxmfevXUsay8yRKPm9sn3eHTz0         Public key: ED25519-CERT SHA256:IqPRdnvM1+xSqaESGaxmfevXUsay8yRKPm9sn3eHTz0
Zeile 1923: Zeile 1919:
                 permit-user-rc</code>Hier haben wir also bei den **''Principals''** den Wert **''django''**. In der Datei **''/etc/ssh/ssh_auth_principals''** wird nun definiert welche Admins/User sich anmelden darf, sprich wer ist in dieser Datei gelistet und wer nicht. Wir hinterlegen als den **''Principal''**-Namen aus dem Zertifikat in dieser Liste. <code> # vim /etc/ssh/ssh_auth_principals</code><file bash /etc/ssh/ssh_auth_principals># Liste aller Administratoren, die berechtigt sind sich hier mit Hilfe der SSH zu verbinden.                 permit-user-rc</code>Hier haben wir also bei den **''Principals''** den Wert **''django''**. In der Datei **''/etc/ssh/ssh_auth_principals''** wird nun definiert welche Admins/User sich anmelden darf, sprich wer ist in dieser Datei gelistet und wer nicht. Wir hinterlegen als den **''Principal''**-Namen aus dem Zertifikat in dieser Liste. <code> # vim /etc/ssh/ssh_auth_principals</code><file bash /etc/ssh/ssh_auth_principals># Liste aller Administratoren, die berechtigt sind sich hier mit Hilfe der SSH zu verbinden.
 django django
-michael</file> In diesem Beispiel wären das die User **michael** und **django**, der User **christoph** könnte sich nicht anmelden, selnst wenn er ein gültiges User-Zertifikat der gleichen User CA hätte, wie der User **michael**.  Damit der SSH-Daemon diese Liste auch verwerten kann, muss er natürlich wissen dass es sie gibt und wo er sie findet; hierzu ergänzen wir die **''/etc/ssh/sshd_config''** um folgenden Abschnitt.<code> # vim /etc/ssh/sshd_config</code><code>...+michael</file> In diesem Beispiel wären das die User **michael** und **django**, der User **christoph** könnte sich nicht anmelden, selbst wenn er ein gültiges User-Zertifikat der gleichen User CA hätte, wie der User **michael**.  Damit der SSH-Daemon diese Liste auch verwerten kann, muss er natürlich wissen dass es sie gibt und wo er sie findet; hierzu ergänzen wir die **''/etc/ssh/sshd_config''** um folgenden Abschnitt.<code> # vim /etc/ssh/sshd_config</code><code>...
  
 # Specifies a file that lists principal names that are accepted for certifi- # Specifies a file that lists principal names that are accepted for certifi-
Zeile 1982: Zeile 1978:
 Obwohl OpenSSH keinen Mechanismus zum Verteilen der Sperrliste bereitstellt, ist es immer noch einfacher, die Sperrliste zu erstellen und sie auf anderem Wege z.B. Orchestrierung mit Hilfe von Ansible zu verteilen, als die CA-Schlüssel und alle zuvor erstellten und verteilten Host- und Benutzerzertifikate zu ändern. Diese Sperrliste **''ssh_ca_krl''** haben wir bereits beim Anlegen unserer CAs im Abschnitt **[[#zertifizierungsstellen_host_und_user_ca|Zertifizierungsstellen HOST und USER CA]]** angelegt. Diese befindet sich im CA-Verzeichnis auf unserem CA-Host unter **''/etc/ssh/ssh_ca''**. Diese Datei kopieren wir nun regelmässig auf unsere Maschinen und legen diese im Verzeichnis **''/etc/ssh/''** ab. Obwohl OpenSSH keinen Mechanismus zum Verteilen der Sperrliste bereitstellt, ist es immer noch einfacher, die Sperrliste zu erstellen und sie auf anderem Wege z.B. Orchestrierung mit Hilfe von Ansible zu verteilen, als die CA-Schlüssel und alle zuvor erstellten und verteilten Host- und Benutzerzertifikate zu ändern. Diese Sperrliste **''ssh_ca_krl''** haben wir bereits beim Anlegen unserer CAs im Abschnitt **[[#zertifizierungsstellen_host_und_user_ca|Zertifizierungsstellen HOST und USER CA]]** angelegt. Diese befindet sich im CA-Verzeichnis auf unserem CA-Host unter **''/etc/ssh/ssh_ca''**. Diese Datei kopieren wir nun regelmässig auf unsere Maschinen und legen diese im Verzeichnis **''/etc/ssh/''** ab.
  
-Damit der SSH-Daemon dieseSperrliste auch berücksichtigen kann, muss dieser natürlich wissen, dass es diese gibt ond wo diese zu finden ist. Wir ergänzen also unsere SSH-Daemon-Konfiguration noch einmal.+Damit der SSH-Daemon diese Sperrliste auch berücksichtigen kann, muss dieser natürlich wissen, dass es diese gibt und wo diese zu finden ist. Wir ergänzen also unsere SSH-Daemon-Konfiguration noch einmal.
    # vim /etc/ssh/sshd_config    # vim /etc/ssh/sshd_config
 <code>... <code>...
Zeile 2053: Zeile 2049:
                 permit-user-rc</code>Wir sehen also hier die **''Serial: 4''** (Serien-Nummer) und den **''Principals: django''**. O.K. das haben wir geprüft, es ist genau das Zertifikat, welches wir vom Nutzer **django** auf die Sperrliste setzen sollen.                  permit-user-rc</code>Wir sehen also hier die **''Serial: 4''** (Serien-Nummer) und den **''Principals: django''**. O.K. das haben wir geprüft, es ist genau das Zertifikat, welches wir vom Nutzer **django** auf die Sperrliste setzen sollen. 
   - **Prüfen des Aktuellen Zertifikatsstatus** \\ Eine Vorabprüfung, ob das Zertifikat aktuell noch nicht revoked wurde, sehen wir hier:<code> ssh-keygen -Qf /etc/ssh/ssh_ca/ssh_ca_krl /etc/ssh/ssh_ca/id_ed25519_test.pub</code><code>/etc/ssh/ssh_ca/id_ed25519_test.pub (django@nausch.org): ok</code> Das Zertifikat ist aktuell also noch gültig!   - **Prüfen des Aktuellen Zertifikatsstatus** \\ Eine Vorabprüfung, ob das Zertifikat aktuell noch nicht revoked wurde, sehen wir hier:<code> ssh-keygen -Qf /etc/ssh/ssh_ca/ssh_ca_krl /etc/ssh/ssh_ca/id_ed25519_test.pub</code><code>/etc/ssh/ssh_ca/id_ed25519_test.pub (django@nausch.org): ok</code> Das Zertifikat ist aktuell also noch gültig!
-  - **Revoken des Zertifikates** \\ Zum Rückrufen des Zertifikates mit der Seriennummer **''4''** müssen wir nun die beiden Optionen **''-k''** zusammen mit der Option **''-u''** angeben, da wir einen Update der Sperrlistendatei vornehmen wollen. Mit der Option **''-f''** definieren wir die SPerrlistendatei selbst. Wichtigste Option ist nun die Seriennummer die wir mit der Option **''-z 4''** angeben. Würden wir diese Option weglassen, würden wir **__ALLE__** Zertifikate des Benutzers revoken!<code> # ssh-keygen -u -k -f /etc/ssh/ssh_ca/ssh_ca_krl -z 4 /etc/ssh/ssh_ca/id_ed25519_test-cert.pub</code><code>Revoking from /etc/ssh/ssh_ca/id_ed25519_test-cert.pub</code>Die Rückmeldung war entsprechend positiv, das Zertifikat kann ab sofort nicht mehr benutzt werden um sich an einem Host anzumelden.+  - **Revoken des Zertifikates** \\ Zum Rückrufen des Zertifikates mit der Seriennummer **''4''** müssen wir nun die beiden Optionen **''-k''** zusammen mit der Option **''-u''** angeben, da wir einen Update der Sperrlistendatei vornehmen wollen. Mit der Option **''-f''** definieren wir die Sperrlistendatei selbst. Wichtigste Option ist nun die Seriennummer die wir mit der Option **''-z 4''** angeben. Würden wir diese Option weglassen, würden wir **__ALLE__** Zertifikate des Benutzers revoken!<code> # ssh-keygen -u -k -f /etc/ssh/ssh_ca/ssh_ca_krl -z 4 /etc/ssh/ssh_ca/id_ed25519_test-cert.pub</code><code>Revoking from /etc/ssh/ssh_ca/id_ed25519_test-cert.pub</code>Die Rückmeldung war entsprechend positiv, das Zertifikat kann ab sofort nicht mehr benutzt werden um sich an einem Host anzumelden.
   - **Prüfen des Aktuellen Zertifikatsstatus** \\ Eine erneute Prüfung, wie nun der aktuelle Status des Zertifikats ist, sehen wir hier:<code> ssh-keygen -Qf /etc/ssh/ssh_ca/ssh_ca_krl /etc/ssh/ssh_ca/id_ed25519_test.pub</code><code>/etc/ssh/ssh_ca/id_ed25519_test.pub (django@nausch.org): REVOKED</code> Das Zertifikat ist also definitiv gesperrt/zurückgerufen!   - **Prüfen des Aktuellen Zertifikatsstatus** \\ Eine erneute Prüfung, wie nun der aktuelle Status des Zertifikats ist, sehen wir hier:<code> ssh-keygen -Qf /etc/ssh/ssh_ca/ssh_ca_krl /etc/ssh/ssh_ca/id_ed25519_test.pub</code><code>/etc/ssh/ssh_ca/id_ed25519_test.pub (django@nausch.org): REVOKED</code> Das Zertifikat ist also definitiv gesperrt/zurückgerufen!
  
  • linux/ssh/tofu_und_cert.1745345249.txt.gz
  • Zuletzt geändert: 22.04.2025 18:07.
  • von django