SSH mit Zwei Faktor Authentifizierung (2FA) absichern

Heute mal eine kurze Anleitung, wie ich unter Debian Jessie meinen SSH-Login über Zwei Faktor Authentifizierung (2FA) abgesichert habe. Am einfachsten geht das mit dem von google bereitgestelltem PAM-Modul „libpam-google-authenticator“. Dieses installieren wir als erste auf unserem Server mit folgendem Befehl:

$ apt-get install libpam-google-authenticator

Danach installiert sich automatisch das benötigte Modul. Dies müssen wir nun noch konfigurieren. Als erstes legen für die Nutzer, die später die 2FA nutzen sollen, Tokens an. Dazu loggen wir uns als der User, für den das Token bestimmt ist, ein. Am einfachsten geht das mit folgendem Befehl:

$ su [USER]

Sind wir nun als der korrekte Nutzer am System angemeldet, führen wir folgenden Befehl aus:

$ google-authenticator

Nun führt uns ein Assistent durch die Erstellung des Tokens und stellt uns einige Fragen. Zuerst werden wir gefragt, ob wir zeitbasierende Tokens haben wollen (Do you want authentication tokens to be time-based). Dies beantworten wir mit ja (y). Anschließend wird uns unser Token sowie die Notfall-Codes präsentiert. Die Notfall-Codes notiert ihr euch auf einen Zettel, den ihr sicher verwahrt! Das Token könnt ihr entweder als QR-Code mit einer passenden App scannen (wie dem Google Authenticator – Google Play Store – Apple AppStore) oder aber auch das per Hand in euer Handy tippen.

Anschließend werden wir gefragt, ob wir unsere /.google_authenticator Datei aktualisieren wollen. Dies bejahen wir. Die restlichen Fragen könnt ihr nach eurem belieben beantworten, unten seht ihr einen Beispiel-Verlauf für das Anlegen eines Tokens:

$ google-authenticator

Do you want authentication tokens to be time-based (y/n) y

HIER KOMMT DEIN TOKEN ALS URL & QR-CODE

Your new secret key is: ************** <- dein Token
Your verification code is ****** <- nicht so wichtig
Your emergency scratch codes are: ! die folgenden Notfall-Codes notieren !
  *******2  
  *******8
  *******5
  *******0
  *******3

Do you want me to update your "/home/****/.google_authenticator" file (y/n) y

Do you want to disallow multiple uses of the same authentication
token? This restricts you to one login about every 30s, but it increases
your chances to notice or even prevent man-in-the-middle attacks (y/n) y

By default, tokens are good for 30 seconds and in order to compensate for
possible time-skew between the client and the server, we allow an extra
token before and after the current time. If you experience problems with poor
time synchronization, you can increase the window from its default
size of 1:30min to about 4min. Do you want to do so (y/n) n

If the computer that you are logging into isn't hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting (y/n) y

Nun haben wir für diesen Nutzer erfolgreich ein Token angelegt. Dies wiederholen wir nun für alle Nutzer, die ein Token benötigen.

Als nächstes müssen wir SSH beibringen, die Tokens auch abzufragen. Dazu öffnen wir unsere ssh-config unter /etc/ssh/sshd_config

$ mcedit /etc/ssh/sshd_config

Dort fügen wir nun die folgenden zwei Zeilen am Ende ein. Achtet darauf, das die Eigenschaften nicht schon vorher auf einen anderen Wert gesetzt werden. Bei ChallengeResponseAuthentication ist des zum Beispiel in der Standardkonfiguration der Fall. Kommentiert diese Zeile dann obendrüber einfach mit einem # aus.

ChallengeResponseAuthentication yes
AuthenticationMethods keyboard-interactive:pam

Nun müssen wir noch das PAM-Modul aktivieren. Dazu öffnen wir als nächstes die /etc/pam.d/sshd Datei, wie gewohnt mit mcedit

$ mcedit /etc/pam.d/sshd

und fügen ganz oben als erste Zeile folgendes ein:

auth [success=ok new_authtok_reqd=done default=die] pam_google_authenticator.so nullok

Nun haben wir noch mehrere Möglichkeiten, das Verhalten zu konfigurieren. Zunächst einmal das nullok am Ende der Zeile. Dies bedeutet, das Nutzer, die über kein Token verfügen, sich trotzdem am System anmelden dürfen. Wollt ihr erzwingen, dass sich nur noch Nutzer mit Token anmelden dürfe, müsst ihr diesen Parameter einfach weglassen.

Es kann auf Dauer nervig sein, sich immer mit dem Token anzumelden, vor allem wenn man ein „vertrauenswürdiges Netzwerk“ hat, wie z.B. das Heimnetzwerk. Daher kann man für manche IP-Bereiche die 2FA einfach überspringen. Wir nehmen mal an, unser Rechner soll Nutzer die physikalisch vorm PC sitzen trauen, sowie seinem „Heimnetzwerk“ 192.168.178.0/24 und der IP 1.2.3.4.

Um dies einzurichten, öffnen wir wieder die Datei /etc/pam.d/sshd

$ mcedit /etc/pam.d/sshd

und fügen ganz oben eine neue Zeile ein, sodass die obersten zwei Zeilen letztliche wie folgt aussehen:

auth [success=1 default=ignore] pam_access.so accessfile=/etc/security/access-trust.conf
auth [success=ok new_authtok_reqd=done default=die] pam_google_authenticator.so nullok

Anschließend legen wir die Datei /etc/security/access-trust.conf an und fügen folgende Zeilen ein:

+:ALL:127.0.0.1
+:ALL:192.168.178.0/24
+:ALL:1.2.3.4
-:ALL:ALL

Nun wird die 2FA nur noch verlangt, sofern man sich außerhalb der oben konfigurierten IP-Bereiche befindet. Besitzt man eine der konfigurierten IPs, so ist pam_access.so erfolgreich und überspringt den nachfolgenden Eintrag (success=1), welcher eine 2FA erzwingt, andernfalls wird die Zeile einfach ignoriert (default=ignore).

Abschließend müssen wir noch unseren SSH-Server neustarten, damit die Konfiguration wirksam wird und danach habt ihr beim Login etwas mehr Sicherheit

service ssh restart

Ein Login-Vorgang mittels PuttY sieht z.B. jetzt so aus:

login as: [USER]
Using keyboard-interactive authentication.
Verification code:
Using keyboard-interactive authentication.
Password: 

Bei der Frage nach dem Verification code gebt ihr nun euren 6-stelligen Code ein, der eure Handy-App generiert hat und schon könnt ihr den Anmeldevorgang fortsetzen. Bei einer Fehlerhaften Eingabe bricht die Anmeldung sofort ab.

fail2ban einrichten

Falls ihr auf eurem Server fail2ban nutzt, was ich euch nur empfehlen kann, müsst ihr noch eine kleine Zeile in der Konfiguration hinzufügen, damit fail2ban fehlgeschlagene Login-Versuche mit 2FA erkennt. Öffnet dazu zuerst die /etc/fail2ban/filter.d/sshd.conf auf eurem Server

$ mcedit /etc/fail2ban/filter.d/sshd.conf

und fügt zu failregex nach dem letzten Eintrag folgende Spalte hinzu

^%(__prefix_line)s(?:error: PAM: )?Cannot make/remove an entry for the specified session for .* from <HOST>\s*$	

Danach müsst ihr noch fail2ban neustarten und schon werden auch Attacken auf den 2FA-Login erkannt und Angreifer konsequent gesperrt!

$ service fail2ban restart
# Fail2Ban filter for openssh
#

[INCLUDES]

# Read common prefixes. If any customizations available -- read them from
# common.local
before = common.conf


[Definition]

_daemon = sshd

failregex = ^%(__prefix_line)s(?:error: PAM: )?[aA]uthentication (?:failure|error) for .* from <HOST>( via \S+)?\s*$
            ^%(__prefix_line)s(?:error: PAM: )?User not known to the underlying authentication module for .* from <HOST>\s*$
            ^%(__prefix_line)sFailed \S+ for .*? from <HOST>(?: port \d*)?(?: ssh\d*)?(: (ruser .*|(\S+ ID \S+ \(serial \d+\) CA )?\S+ %(__md5hex)s(, client user ".*", client host ".*")?))?\s*$
            ^%(__prefix_line)sROOT LOGIN REFUSED.* FROM <HOST>\s*$
            ^%(__prefix_line)s[iI](?:llegal|nvalid) user .* from <HOST>\s*$
            ^%(__prefix_line)sUser .+ from <HOST> not allowed because not listed in AllowUsers\s*$
            ^%(__prefix_line)sUser .+ from <HOST> not allowed because listed in DenyUsers\s*$
            ^%(__prefix_line)sUser .+ from <HOST> not allowed because not in any group\s*$
            ^%(__prefix_line)srefused connect from \S+ \(<HOST>\)\s*$
            ^%(__prefix_line)sReceived disconnect from <HOST>: 3: \S+: Auth fail$
            ^%(__prefix_line)sUser .+ from <HOST> not allowed because a group is listed in DenyGroups\s*$
            ^%(__prefix_line)sUser .+ from <HOST> not allowed because none of user's groups are listed in AllowGroups\s*$
            ^%(__prefix_line)s(?:error: PAM: )?Cannot make/remove an entry for the specified session for .* from <HOST>\s*$	

ignoreregex = 

# DEV Notes:
#
#   "Failed \S+ for .*? from <HOST>..." failregex uses non-greedy catch-all because
#   it is coming before use of <HOST> which is not hard-anchored at the end as well,
#   and later catch-all's could contain user-provided input, which need to be greedily
#   matched away first.
#
# Author: Cyril Jaquier, Yaroslav Halchenko, Petr Voralek, Daniel Black

One thought to “SSH mit Zwei Faktor Authentifizierung (2FA) absichern”

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.