2FA podczas logowania przez SSH

2FA podczas logowania przez SSH? Tak, Linux to umożliwia!

Coraz więcej serwisów zaczyna wprowadzać wieloetapowe mechanizmy logowania znane na przykład z aplikacji bankowych. Dlaczego jest to bezpieczniejsze? Czy można z tego skorzystać, logując się do serwera? W tym artykule pokażemy, w jaki sposób skonfigurować serwer Linux, aby samo hasło już nie wystarczało. Opowiemy także o części systemu, która za to odpowiada.

Coraz więcej serwisów zaczyna wprowadzać wieloetapowe mechanizmy logowania znane na przykład z aplikacji bankowych. Dlaczego jest to bezpieczniejsze? Czy można z tego skorzystać, logując się do serwera? W tym artykule pokażemy, w jaki sposób skonfigurować serwer Linux, aby samo hasło już nie wystarczało. Opowiemy także o części systemu, która za to odpowiada.

Zaczniemy nieco teoretycznie od przybliżenia, czym jest uwierzytelnianie wieloetapowe, które najczęściej spotykane jest w postaci tzw. 2FA, czyli Two-Factor Authentication. Można to przetłumaczyć jako uwierzytelnianie dwuskładnikowe. Proces uwierzytelniania jest już wszystkim użytkownikom internetu dobrze znany. Wpisujemy login oraz hasło mające za zadanie potwierdzić naszą tożsamość i po zaakceptowaniu takiej kombinacji otrzymujemy dostęp do wcześniej ukrytych treści.

Nie jest to jednak jedyny istniejący model uwierzytelniania. Patrząc funkcjonalnie, można zdefiniować trzy grupy czynników ze względu na:

  • posiadaną wiedzę (something you know) – informacji znanych tylko nam, na przykład: hasła, numery PIN, wzór (jak w blokadzie telefonu)
  • posiadane przedmioty (something you have) – unikalne przedmioty, jak na przykład: fizyczne tokeny, karty kredytowe, tokeny w formie oprogramowania (np. Google Authenticator), telefon komórkowy z powiadomieniem SMS
  • posiadane cechy charakterystyczne (something you are or do) – informacje biometryczne, jak na przykład: układ linii papilarnych czy siatkówki, kod DNA, podpis, twarz czy głos.

Hasła mogą zostać skradzione na przykład przez użycie keyloggera bądź przez phishing. Podobnie jest z fizycznymi przedmiotami. Tworzenie systemów polegających na biometrii jest kosztowne oraz zdecydowanie bardziej wymagające.

Z tego powodu opracowano proces uwierzytelnienia wieloetapowego wymagającego użycia minimum dwóch niezależnych czynników. Najczęściej spotykanym połączeniem jest hasło plus podanie kodu przesłanego na telefon. Innym częstym oraz bardzo wygodnym sposobem logowania jest połączenie hasła z tokenem zaimplementowanym w zgodzie ze standardem TOTP. To rozwiązanie wykorzystuje generowanie jednorazowych haseł w oparciu o aktualny czas, dzięki czemu jest niezależne od przesyłania informacji przez sieć (jak w przypadku SMS).

Po tym dość rozbudowanym wstępie przejdźmy do sedna – czy skonfigurowanie 2FA jest w ogóle na Linuksie możliwe? Oczywiście!

Alternatywne metody uwierzytelniania w Linuksie

Uwierzytelnianie w systemach opartych o kernel Linux jest oparte o mechanizm PAM, czyli Pluggable Authentication Modules. To właśnie dzięki temu systemowi można implementować różne metody uwierzytelniania. Dzieje się tak, ponieważ pozwala on na odseparowanie czynności związanych z uwierzytelnianiem od aplikacji. Programy takie jak `login` czy `sshd` (a także wiele innych) potrzebują wiedzieć jedynie to, że użytkownik faktycznie jest tym, za kogo się podaje. Aczkolwiek istnieje wiele możliwości na uzyskanie takiego potwierdzenia. Poza kombinacją nazwy użytkownika i hasła przechowywaną lokalnie uwierzytelnienia można dokonać przy pomocy centralnych baz, takich jak LDAP czy Kerberos. Ponadto można wykorzystywać różnego rodzaju certyfikaty jak klucze ssh. Implementowanie wszystkich możliwych sposobów uwierzytelniania w kodzie aplikacji znacznie utrudniałoby życie deweloperom. Dlatego wykorzystanie biblioteki PAM jest tak szerokie w świecie Enterprise Linux. Pluggable oznacza, że różne aplikacje mogą wykorzystywać inne metody uwierzytelnienia. Przez modules rozumie się możliwość dopisania nowych metod uwierzytelnienia w formie modułów wykorzystujących to samo API.

Ciekawymi modułami oferującymi alternatywy są na przykład pam_pgsql wykorzystujący bazę danych PostgreSQL czy pam_fprintd pozwalający na wykorzystanie czytnika linii papilarnych.

Moduł PAM umożliwiający 2FA podczas logowania

Skoro wiemy już, który element odpowiada za te możliwości, przejdźmy do jego konfiguracji. Naszym celem jest to, aby podczas logowania przez SSH w celu uwierzytelnienia wymagany był jeszcze kod z aplikacji, np. Google Authenticator.

Szczęśliwie dla nas odpowiedni moduł PAM jest dostępny dla dystrybucji z rodziny Enterprise Linux w formie pakietu RPM w repozytorium EPEL. W naszych przykładach będziemy używać EuroLinuksa.

Pierwszym krokiem jest dodanie repozytorium EPEL:

sudo yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm

Następnym będzie zainstalowanie wymaganego przez nas modułu:

sudo yum install google-authenticator

Konfiguracja kodów dla użytkownika

By skonfigurować moduł, należy wywołać polecenie `google-authenticator`, które zada nam kilka pytań w celu ustalenia trybu działania. Rekomendujemy skorzystanie z wybranych przez nas ustawień widocznych poniżej:

$ google-authenticator

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

Po tej linii pojawia się sporo informacji, które będą potrzebne do skonfigurowania aplikacji generującej jednorazowe kody, takie jak:

  • kod QR – pozwalający na sparowanie aplikacji
  • sekretny klucz – pozwalający na skonfigurowanie aplikacji, które nie korzystają np. z elementów graficznych
  • kod weryfikacyjny – pierwszy kod wygenerowany na podstawie klucza zawartego w kodzie QR
  • zapasowe kody jednorazowe – pozwalające na zalogowanie w przypadku braku dostępu do aplikacji, czy to w związku ze zgubieniem telefonu, czy też z jego kradzieżą. Z tego powodu bardzo ważne jest, aby przechowywać je w bezpiecznym miejscu.

Po zapisaniu wygenerowanych danych aplikacja będzie jeszcze pytać o zapisanie wygenerowanego kodu. W celu przejścia dalej należy się zgodzić.

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

Kolejnym krokiem jest skonfigurowanie opcji wpisywania tego samego kodu wielokrotnie.

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

Kolejne pytanie jest bardzo istotne z punktu widzenia bezpieczeństwa. Wymaga jednak, aby czas w urządzeniu oraz na serwerze pozostawał zawsze zsynchronizowany. Pozwala to bowiem na przedłużenie okienka czasowego, w którym kod będzie akceptowany.

By default, a new token is generated every 30 seconds by the mobile app.
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. This allows for a
time skew of up to 30 seconds between authentication server and client. If you
experience problems with poor time synchronization, you can increase the window
from its default size of 3 permitted codes (one previous code, the current
code, the next code) to 17 permitted codes (the 8 previous codes, the current
code, and the 8 next codes). This will permit for a time skew of up to 4 minutes
between client and server.
Do you want to do so? (y/n) n

Ostatnie pytanie dotyczy ograniczania możliwości logowania w przypadku wystąpienia błędu, co spowalnia potencjalne ataki i pozwala na dalsze poprawienie bezpieczeństwa.

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

W ten sposób skonfigurowaliśmy generację kodów dla użytkownika niebędącego administratorem. Pozostaje nam jeszcze odpowiednio skonfigurować PAM oraz `sshd` tak, aby wykorzystywały 2FA przy logowaniu.

Konfiguracja PAM i sshd

Docelowo chcielibyśmy, aby podczas logowania konieczne było podanie hasła oraz kodu z aplikacji działającej jako token. W tym celu będziemy musieli zmienić konfiguracje następujących plików:

  • /etc/pam.d/sshd
  • /etc/ssh/sshd_config.

Pierwszy z nich odpowiada za sposób działania `sshd` podczas uwierzytelnienia przy pomocy PAM (co jest standardowym zachowaniem w dystrybucjach typu Enterprise). Drugi natomiast konfiguruje działanie samego daemona ssh.

Na koniec pliku /etc/pam.d/sshd należy dodać następującą linijkę:

auth required pam_google_authenticator.so nullok

Dzięki niej PAM podczas uwierzytelniania (auth) będzie wymagał (required), aby test wykorzystujący kod TOTP zakończył się powodzeniem. Dodatkowo użytkownicy, którzy jeszcze nie skonfigurowali aplikacji, nie będą mieli blokowanego dostępu (nullok).

Następnym krokiem jest zmiana następujących opcji w /etc/ssh/sshd_config do poniższych wartości:

  • ChallengeResponseAuthentication yes – w celu umożliwienia skorzystania z 2FA przy pomocy PAM
  • AuthenticationMethods keyboard-interactive – tutaj chcemy wymusić logowanie przy pomocy hasła oraz (za sprawą PAM) kodu z aplikacji Authenticator.

Warto wspomnieć, że globalna zmiana AuthenticationMethods nie zawsze jest najlepszym wyjściem. By skonfigurować to, dla kogo ma być ona zaaplikowana, można skorzystać ze składni jak poniżej:

Match User eladmin
    AuthenticationMethods keyboard-interactive

Opcja ta będzie aplikowana tylko do wybranego użytkownika. W przypadku gdy jest dużo użytkowników, warto rozważyć stworzenie grupy i następnie wykorzystanie dopasowania po grupie (Match Group).

Po zmianach w plikach konfiguracyjnych możemy zrestartować usługę:

sudo systemctl restart sshd

i spróbować logowania przez SSH z wykorzystaniem dwuskładnikowego uwierzytelniania:

$ ssh eladmin@pam-test-machine 
Password: 
Verification code: 
Last login: Tue Aug 11 12:59:30 2020 from 192.168.121.1
[eladmin@localhost ~]$

Jak widać, po podaniu hasła musimy jeszcze dodatkowo wpisać kod z aplikacji.

Podsumowanie

Skonfigurowanie 2FA do logowania przez SSH nie jest trudnym zadaniem. Kwestię tego, czy zastosowanie takiego rozwiązania w środowiskach produkcyjnych jest konieczne, pozostawiamy otwartą. W przypadku zarządzania ogromem maszyn pod względem bezpieczeństwa oraz ułatwiania automatyzacji prym niepodzielnie będzie wiodło uwierzytelnianie przy pomocy klucza. W przypadku jednak gdy dostęp do infrastruktury zapewniany jest z tzw. serwera przesiadkowego, zastosowanie uwierzytelnienia wieloetapowego może mieć realny wpływ na poprawienie bezpieczeństwa oraz zmniejszenie ryzyka nieautoryzowanego dostępu.

Autorzy

Artykuły na blogu są pisane przez osoby z zespołu EuroLinux. 80% treści zawdzięczamy naszym developerom, pozostałą część przygotowuje dział sprzedaży lub marketingu. Dokładamy starań, żeby treści były jak najlepsze merytorycznie i językowo, ale nie jesteśmy nieomylni. Jeśli zauważysz coś wartego poprawienia lub wyjaśnienia, będziemy wdzięczni za wiadomość.