Skip to content

Meine Best Practice für Shell-Accounts und ihre Absicherung (mit ssh)

Auf meinen Systemen bin ich im wesentlichen der einzige Shell-Benutzer. Die hier vorgeschlagenen Methoden können natürlich auch auf eine größere Menge von Accounts angewendet werden, wobei ab einer gewissen Grenze manche Verfahren nicht mehr skalieren.

lokaler Login

Lokaler Login auf der Konsole ist bei meiner Arbeitsweise die Ausnahme. Das passiert im Wesentlichen nur bei Störungen, wenn Netz und/oder ssh nicht mehr funktionieren. Die bei weitem häufigste Methode des Zugriffs auf die Shell erfolgt via ssh über das Netz. Da ich auf meinen Systemen natürlich sudo-berechtigt bin, benötige ich das zum Account gehörende Passwort (außer zum Konsolen-Login im Störungsfall) nur, um mich bei sudo zu authentifizieren um root werden zu können.

Dabei gönne ich mir tatsächlich den "Luxus", auf jedem System ein eigenes Passwort zu haben. Das gibt dem Paranoiker in mir das gute Gefühl, die "Regeln" beachtet zu haben und mich im Fall eines kompromittierten Passworts nicht gleich überall aussperren zu müssen. Auf der anderen Seite ist das lästig, wenn man mit mssh auf einem größeren Stapel Systemen herumspringt und dann $BIGNUM verschiedene Passworte aus dem Passwortsafe rüberkopieren muss. Früher[tm] hatte ich die Passworte auch noch (fast) alle im Kopf, da ging das noch schneller. Aber man wird ja nicht jünger.

Ich habe auf (fast) allen Systemen den ssh-Login mit Passwort-Authentifikation abgeschaltet; die einzige Möglichkeit zum Login ist die Authentifikation mit ssh Public Key. Die Ausnahme von dieser Regel sind zwei Systeme, die als Shell-Server für den erweiterten Bekanntenkreis dienen und wo die Benutzer darum gebeten haben, sich auch ohne einen ssh-Key einloggen zu können. Kurz nach Veröffentlichung dieses Artikel hat mir jemand auf Twitter erklärt, dass man mit der "Match"-Direktive in der sshd_config auch Passwort-Authentifikation für einzelne Benutzer ein- und ausschalten kann. Das werde ich in Zukunft nutzen, um die sudo-berechtigten Accounts trotz grundsätzlich erlaubter Passwort-Authentifikation nur mit ssh-Key "rein" zu lassen (denn sonst reicht es, das User-Passwort zu wissen, um von "irgendwo" root werden zu können). Das geht sehr elegant mit Match Group sudo\\nPasswordAuthentication No für alle Mitglieder der Gruppe sudo, die an anderer Stelle benutzt wird, um genau dieser Benutzergruppe zu erlauben, root zu werden. Zwei Fliegen, eine Klappe.

Ich bemühe mich, auf allen meinen Systemen auch dieselbe numerische UID zu haben. Erstens weiß man ja nie, ob zwei Systeme nicht doch mal per NFS miteinander verkuppelt werden, und zweitens muss man dann beim Kopieren von Directory-Trees nicht höllisch darauf aufpassen, den Kommandos die richtigen Optionen zur Verwendung der alphanumerischen Accountnamen mitzugeben. Da muss ich allerdings zugeben, dass ich das schon auf meiner Handvoll Systeme nicht konsequent durchgehalten habe. Hier wird man ziemlich schnell auf zentalisierte Online-Dienste wie AD oder LDAP zurückgreifen wollen, oder die Accounts der Benutzer über ein anderes Verfahren systemübergreifend syncronisieren wollen.

Auf meinen Android-Mobiltelefonen habe ich übrigens keinen ssh-Key hinterlegt. Ich traue dieser Plattform so wenig, dass ich dort keine Login-Credentials für meine "wichtigen" Systeme hinterlegen möchte.

Zusätzlich zu meinem persönlichen Account wird bei der Installation eines Systems ein weiterer Account namens zgadmin erstellt, der dasselbe Passwort erhält wie root und sudo-berechtigt ist. Dies dient dazu, dass man sich auch dann, wenn man nicht direkt als root "rein" darf, trotzdem root werden kann, wenn man das (eklige) Root-Passwort kennt.

Mein Installations-Prozess wirft alle "System"-Benutzer in die Gruppe nosu, der man auf Debian durch das Auskommentieren einer Zeile in /etc/pam.d/su die Verwendung von su explizit verbieten kann.

Absicherung des ssh-Servers

Der ssh-Daemon auf den allermeisten "meiner" Systeme ist per TCP Wrapper auf ein paar wenige IP-Ranges limitiert, von denen aus ich mich einloggen kann. Da ich daheim ein statisches IPv6-Netz habe, ist diese Beschränkung machbar. Wenn ich unterwegs bin, hole ich mir per OpenVPN ein IPv6-Netz aus meinem statischen Bereich auf das Notebook; auf Systeme, die kein IPv6 haben, greife ich über einen im Housing stehenden Sprunghost mit statischer IPv4-Adresse zu (ja, unsicher, aber der Kunde darf sich gerne IPv6 holen wenn er es sicherer haben möchte).

Um die Absicherung des ssh-Servers weiter zu treiben, könnte man diese Adressbeschränkung noch einmal per Paketfilter erzwingen (was früher oder später kommen muss, denn der OpenSSH-Upstream möchte gerne die Unterstützung für die TCP-Wrapper aus dem Code herauswerfen) und die ssh-Berechtigung per Serverkonfiguration ((Allow|Deny)(Users|Groups) in der sshd_config) auf einzelne Benutzer und Gruppen einzuschränken. Das ist für die, denen "Du hast keine ~/.ssh/authorized_keys und Passwort-Authentifikation ist abgeschaltet, also kannst Du Dich nicht einloggen" oder auf den Systemen mit eingeschalteter Passwort-Authentifikation eine Option; ich habe sie zwar in der langfristigen Planung, aber (leider, mankommtjazunix) noch nirgendwo umgesetzt.

Den ssh-Server auf einem anderen Port als 22 zu betreiben, ist ein zweischneidiges Schwert. Auf Systemen mit abgeschalteter Password Authentication reduziert diese Maßnahme höchstens das Grundrauschen im Log, da ein Angreifer keine realistische Chance auf Erfolg mehr hat, und außerdem haben diejenigen, die in einschlägigen Netzwerkbereichen nach offenen ssh-Servern mit dummen Passworten suchen, längst damit angefangen, auch die gängigen Ausweichports (vor allem TCP/443) mitzuscannen. Und nach Murphy ist man irgendwann in einer "feindlichen" Umgebung hinter einer Firewall, die einen genau auf den selbst gewählten exotischen Port nicht connecten lässt.

Die in fail2ban für ssh für Arme vorgestellte Methode, als fail2ban-Ersatz die SYN-Pakete pro IP und Zeit zu messen und bei Überschreitung einer Grenze die IP zu sperren, hat an Biß verloren, seit solche Scans zum großen Teil unter Verwendung von Zombies erfolgen und die offensichtlich unter Kontrolle eines Angreifers ausgeführten Loginversuche von einer Vielzahl Systeme auf dem ganzen Erdball kommen. "Richtiges" fail2ban dürfte dasselbe Problem haben.

root-Shells und root werden

Für den Systemadministrator gehört die Arbeit mit erhöhten Rechten zum Tagesgeschäft. Und zwar so sehr, dass ich Fachkollegen kenne, die als allererstes nach dem Login den Account zu root wechseln und die dann offene Root-Shell bis zum Ausloggen so offen lassen. Im "schlimmsten" Fall hatte ein Kollege einen Alias "r", den er im Wesentlichen ohne nachzudenken sofort als erstes nach dem Login aufgerufen hat.

Seitdem ich vor vielen Jahren mal aus Versehen eine Mail in eine offene Root-Shell hineingepasted habe, vermeide ich es, Shells mit erhöhten Rechten offen stehen zu haben. Im konkreten Fall hatte ich nur einen ganzen Haufen leerer Dateien mit Deutschen Worten als Dateinamen im aktuellen Verzeichnis, aber in der Mail hätte es ja auch eine Zeile mit rm -rf / als Inhalt geben können. Das hätte auch gewaltig in die Hose gehen können.

Seitdem gibt es bei mir Root-Shells nur noch in Notfällen oder wenn es gar nicht mehr anders gut. Der Root-Prompt hat eine andere (und ekelhafte) Farbe. Nur Blinken habe ich mir gerade noch verkniffen.

Statt dessen tippe ich lieber vor jeden Befehl, den ich ausführe, ein eigenes sudo vornedran. Das hat den angenehmen Nebeneffekt, dass man im auth.log oder /var/log/secure einen schönen Audit-Trail dessen hinterlässt, was man getan hat. So muss man sich nicht mehr auf die Shell-History verlassen, wenn man "bei sich selbst" nachschlagen möchte wie ein gewiser Prozess noch geht.

Auf Debian-Systemen funktioniert diese Arbeit sehr gut, weil in Debian Policy 10.9 steht, dass man die Verzeichnisrechte möglichst offen einrichten soll, es sei denn es stehen zwingende Privacy-Concerns dagegen. Kann ich ein Verzeichnis als normaler Nutzer lesen, funktioniert file name globbing auch für "Normale" User, so dass man Dinge wie sudo rm /var/lib/foo/bar* tippen kann und auch Konstrukte wie for file in /etc/pam.d/*; do sudo vim $file; done nutzbar sind. Auch die Möglichkeit, als "normaler" Nutzer in "fremde" Verzeichnisse hineinzuwechseln erleichtert das Arbeiten ohne Sonderrechte sehr. Nur bei Pipes und vor allem Ausgabeumleitungen muss man mit sudo manchmal um die Ecke denken, und ein cat, ein dd oder ein tail extra investieren. Und es ist eine Ebene mehr in der Quoting-Hölle.

Dateien, die man als normaler Nutzer nicht unbedingt lesen können sollte (z.B. die Logs) gehören bei Debian in aller Regel einer paketspezifischen Gruppe, so dass man über die Aufname des eigenen Accounts in diese Gruppe (z.B. für Logs) erweiterte Rechte für diese Dateien bekommen kann, ohne root zu sein. So kann man auf Debian-Systemen zum Beispiel die System-Logs lesen, wenn man Mitglied in der Gruppe adm ist, so dass man sich das sudo für less, tail und grep in den Logs verkneifen kann.

Auf Red-Hat-Systemen ist diese Sitte nicht so verbreitet, so dass man hier sehr viel häufiger sudo tippen muss, und in der Red-Hat-Welt erwische ich mich selbst immer häufiger beim sudo -i (was übrigens gleichwertig zum weiter verbreiteten sudo su - ist, aber einen Prozess weniger benötigt und außerdem nicht so doppelt gemoppelt aussieht).

sudo

Zum eigentlichen "root werden" verwende ich sudo. Das hat sich glücklicherweise in den 20 Jahren, in denen ich das schon mache, auf derart breiter Front durchgesetzt, dass ich die Rückfrage "warum" lange nicht mehr gehört habe. Auch die Alternativen (erinnert sich hier noch jemand an super oder hat es jemals verwendet?) sind weitgehend in Vergessenheit geraten.

Zu den von mir als Vorteil gesehenen Features von sudo zählen:

  • ziemlich flexibel konfigurierbar (wenn ich mir auch noch etwas mehr Flexibilität beim Matching der erlaubten Kommandos wünschen würde)
  • über Gruppen steuerbar
  • in LDAP/SSS integrierbar
  • Ermöglicht das "root werden" unter Angabe des User-Passworts, das bei jedem User anders sein kann
  • Cached die Tatsache, dass auf diesem Terminal in letzter Zeit jemand erfolgreich root geworden ist und fragt in dieser Zeit nicht noch einmal nach einem Passwort.
  • Deswegen kann das Root-Passwort ekelhaft sein: In der Vor-systemd-Zeit benötigte man es nahezu nie; seit systemd eigentlich jedes Mal wenn beim Systemstart irgendwas schiefgegangen ist. Aber sonst auch nie.
  • logged die Aufrufe und das aufgerufene Kommando; dies hinterlässt nette Logeinträge, wenn man bei der Arbeit diszipliniert ist (sich also keine Shell geben lässt und keine Shell-Escapes nutzt und das Betriebssystem das zulässt).

Wenig Verständnis habe ich für Installationen, bei denen sudo explizit so konfiguriert wurde, nach dem root-Passwort zu fragen (Sinn von sudo nicht verstanden), bei denen die Karenzzeit für die Wiederholung des Passworts absurd niedrig eingestellt ist (überzogene Sicherheitsdenke) oder bei denen auch "normale" Benutzer jederzeit ohne Passwort root werden können (Faulheit siegt über Sicherheit).

Automatisiert root werden

Natürlich ist es notwendig, auch im Rahmen automatisierter Prozesse auf dem lokalen oder dem entfernten System Root-Rechte zu erlangen. Da die Prozesse automatisiert ablaufen müssen, verbieten sich hier Verfahren, die interaktiv nach einem Passwort fragen.

Für die lokale Rechteerhöhung versuche ich, mit NOPASSWD-sudo-Regeln zu arbeiten. Das ist oft nicht einfach, so dass es schon mal vorkommt, dass ich einen Prozess direkt als root starten lasse, obwohl er die Rechte nur für einige wenige Operationen benötigt. Die üblichen Sicherheitsregeln der Softwareentwicklung gelten; hierfür gibt es weitere Literatur. Vielleicht blogge ich auch mal darüber, aber das ist ein umfangreiches, komplexes Thema. Ich weiß nicht, ob ich jemals die Zeit haben werde, das in der gebotenen Tiefe zu behandeln.

Für den Login auf einem entfernten System mit erhöhten Rechten habe ich im Frühjahr schnmal geblogged und verweise deswegen darauf, was ich damals über die automatisierte und passwortlose Ausführung entfernter Prozesse schrieb.

ssh-Zertifikate

Eine relativ neue Variable im ssh-Spiel sind zertifizierte Public Keys, auch ssh-Zertifikate genannt. Dabei wird ein von einem Benutzer ganz normal mit ssh-keygen erstellter ssh-Key von einer Certification Authority, die nicht dauerhaft oder auch gar nicht online sein muss, zertifiziert, und die Zielsysteme erlauben pauschal den Login mit allen Zertifikaten, die von einer bestimmten Menge von CAs zertifiziert (und nicht revoked) wurden. Auf den Zielsystemen muss man damit nur noch die Zertifikate der CAs selbst und die Revocation List verteilen. Letztere ist immens wichtig, denn sonst kann man die Inhaber einmal ausgestellter Zertifikate vor Ablauf des Zertifikats nicht am Login hindern.

In einem Zertifikat steht zusätzlich zur Zertifizierung des ssh Public Keys drin, wie lange die Zertifizierung gültig sein soll, welche Accounts auf dem Zielsystem das Login-Ziel sein dürfen und auf welchen Systemen sich der Inhaber des Zertifikats einloggen darf. Zusätzlich - und hier beginnt jetzt der musikalische Teil - kann die CA den Key mit allen Restriktionen belegen, die in einer ~/.ssh/authorized_keys gesetzt werden dürfen, also konkret (und unter anderem):

  • von welchen IP-Adressen dieser Key benutzt werden darf
  • welches Kommando ausgeführt werden darf
  • ob ssh-Features wie Port-, X11- oder Agent-Forwarding genutzt werden dürfen.

Dadurch, dass diese Einschränkungen Teil des Zertifikats sind, stehen sie unter der alleinigen Kontrolle der CA. Nicht einmal der lokale Admin des Zielsystems kann eine im Zertifikat stehende Einschränkung aufheben. Gerade in größeren Umgebungen, bei denen man vielleicht nicht allen, die root werden dürfen, unbegrenzt traut, ist das ein erheblicher Sicherheitsgewinn - nur leider nicht so weit wie man denkt, denn root darf natürlich am ssh-Server herumdrehen und sich auf diese Weise (z.B. über eine hinterlegte AuthorizedKeysFile-Direktive) doch irgendwo eine unzertifizierte Zugangsmöglichkeit schaffen.

Literaturempfehlung: man ssh-keygen und man sshd_config haben jeweils einen Abschnitt CERTIFICATES.

Trackbacks

No Trackbacks

Comments

Display comments as Linear | Threaded

brainspiritus on :

SSH-Zertifikate finde ich auch spannend (und die Kategorie "was es nicht alles gibt, wenn man wirklich mal die Manpage liest", ebenso wie die Möglichkeit, die Fingerprints des Hostkeys im DNS abzulegen oder die auch von der SSH-CA signieren zu lassen), ich habe nur den Eindruck, dass das mit dem signierten Nutzerschlüssel dummerweise natürlich wieder alles nicht mehr funktioniert, wenn man das Schlüsselpaar per pkcs11 auf ein Token packt, weil dort dann nur x509 liegt. Ob man den private Key auf dem Token mit einem signierten public key auf der Festplatte nutzen kann, habe ich dann nicht mehr probiert, das erscheint mir konzeptuell blöd.

Marc 'Zugschlus' Haber on :

Ich benutze bei $KUNDE ein ssh-Zertifikat mit einem Yubikey. Das funktioniert transparent, weil der Private Key dasselbe ist Die "Anreicherung" des Public Key zum Zertifikat liegt in ~/.ssh/pubkeyname-cert.pub und ist ohne den Yubikey so unbrauchbar wie das klassische pubkeyname.pub.

Anfänglich hab ich das ja nicht so ganz verstanden, weil wer eine Revocation List zeitnah verteilen kann, kann auch pubkeys zeitnah entfernen, aber wenn man Systeme im Hunderterpack aufbaut und genau so schnell wieder wegwirft ist man dankbar über jedes Bit an lokaler individueller Konfiguration, das man nicht verteilen muss.

Add Comment

Markdown format allowed
Enclosing asterisks marks text as bold (*word*), underscore are made via _word_.
Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications.
Form options