Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung |
linux:ssh:tofu_und_cert [04.05.2024 11:35. ] – [Ausgangssituation] typofixing django | linux:ssh:tofu_und_cert [28.04.2025 09:20. ] (aktuell) – Haeder angepasst django |
---|
{{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 ===== |
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 Kommunukation 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)]]**. |
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). |
| |
==== signierte SSH-Keys - SSH-Zertifikate ====== | ==== signierte SSH-Keys - SSH-Zertifikate ====== |
Die OpenSSH-Implementierung von SSH, bei der Benutzer SSH-Hosts und umgekehrt Hosts SSH-Benutzern vertrauen, unterstützt einen zertifikatsbasierten Mechanismus, der zur Reduktion von Komplexität und Verminderungen von Administrationsaufwänden genutzt werden kann. Anstelle eines Satzes von öffentlichen/privaten Schlüsseln werden signierte Zertifikate verwendet. Dies hat den Vorteil, dass eine einzige vertrauenswürdige Zertifizierungsstelle anstelle vieler öffentlicher/privater Schlüssel verwendet werden kann. | Die OpenSSH-Implementierung von SSH, bei der Benutzer SSH-Hosts und umgekehrt Hosts SSH-Benutzern vertrauen, unterstützt einen Zertifikat basierten Mechanismus, der zur Reduktion von Komplexität und Verminderungen von Administrationsaufwänden genutzt werden kann. Anstelle eines Satzes von öffentlichen/privaten Schlüsseln werden signierte Zertifikate verwendet. Dies hat den Vorteil, dass eine einzige vertrauenswürdige Zertifizierungsstelle anstelle vieler öffentlicher/privater Schlüssel verwendet werden kann. |
| |
Im Abschnitt **AUTHENTICATION** der Manual-Page von **''ssh''** und im Abschnitt **CERTIFICATES** der Manual-Page von **''ssh-keygen''** finden sich, etwas versteckt nicht auf den ersten Blick jedem sofort ersichtlich, hierzu tiefer gehende Erklärungen und Beispiele. | Im Abschnitt **AUTHENTICATION** der Manual-Page von **''ssh''** und im Abschnitt **CERTIFICATES** der Manual-Page von **''ssh-keygen''** finden sich, etwas versteckt nicht auf den ersten Blick jedem sofort ersichtlich, hierzu tiefer gehende Erklärungen und Beispiele. |
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>... |
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 |
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- |
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>... |
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! |
| |