Kolega poprosił mnie abym napisał coś krótkiego związanego z bezpieczeństwem IT. Szczerze mówiąc nie miałem ani weny ani pomysłu. Odmówiłem wykręcając się brakiem czasu, zwłaszcza że miałem nową zabawkę - elektroniczną Enigmę do zlutowania. Jednakże zgłębiając tematykę i historię łamania szyfrogramów Enigmy pomyślałem, że może dałoby się te dwa tematy jakoś luźno połączyć. W ten sposób pół żartem, pół serio napisałem instrukcję implementacji mechanizmu dwu-etapowego uwierzytelniania (MFA) w OpenVPN-ie z wykorzystaniem szyfrowania Enigmy i prawdziwej niemieckiej książki kodów. Więcej na temat samej Enigmy (tej prawdziwej jak i elektronicznej), zasady działania, ciekawostek - znajdziesz w menu z lewej strony.
rys. 1 moja elektroniczna Enigma
let's go - dodajmy MFA do OpenVPN!
Jakiś czas temu wdrażałem nową bramkę OpenVPN i pamiętam, że nie było wtedy gotowego mechanizmu MFA dostępnego dla OpenVPN. Aby dodać dwu-etapowe uwierzytelnianie napisałem swój skrypt uwierzytelniający i podpiąłem go po stronie serwera (auth-user-pass-verify).
Jeśli w configu OpenVPN, po stronie klienta, dopiszesz dyrektywę: auth-user-pass spowoduje to, że OpenVPN jeszcze przed połączeniem wyświetli monit o podanie użytkownika i hasła - patrz rys.2. Hasło to (oraz login) możemy wykorzystać właśnie w celu przeprowadzenia dodatkowego uwierzytelnienia (MFA).
rys.2 OpenVPN - okienko dialogowe z możliwością wprowadzenia użytkownika i hasła
OpenVPN w pierwszej kolejności sprawdzi podstawowe metody zabezpieczeń - tj. zgodność klucza ta.key, datę ważności i prawidłowość certyfikatu użytkownika (czy został podpisany przez nasze zaufane CA, czy nie został odwołany i umieszczony na liście CRL itd.). Dopiero gdy wszystkie zabezpieczenia w warstwie TLS powiodą się, wówczas klient prześle do serwera wprowadzone przez użytkownika parametry. Aby serwer mógł zweryfikować wprowadzone dane (tj. użytkownika i hasło), należy w jego pliku konfiguracyjnym dodać linię :
gdzie /etc/openvpn/server/mfa_helper.bash to skrypt weryfikujący wprowadzone dane.
Serwer OpenVPN wywoła wówczas wskazany skrypt i przekaże do niego ścieżkę tymczasowego pliku, w którym zapisane będą wprowadzone przez użytkownika dane (credentiale).
/etc/openvpn/server/mfa_helper.bash /sciezka/do/tymczasowego/pliku/z/credentialami
Skrypt należy napisać samemu, gdyż twórcy OpenVPN-a pozostawiają nam tutaj zupełną dowolność co do metod weryfikacji wprowadzonych danych jak i użytych do tego narzędzi. Skrypt możesz napisać w bashu, perlu, pythonie - czymkolwiek. Nazwa użytkownika oraz hasło zapisane są w pliku tymczasowym odpowiednio w pierwszej i drugiej linii. Przykładowy - najprostszy skrypt napisany w bashu pokazałem na listingu 1.
Dla OpenVPN-a ważny jest tak naprawdę kod z jakim skrypt zakończył działanie. Jeśli skrypt zakończy działanie z kodem 0 oznacza to, że uwierzytelnienie powiodło się i OpenVPN umożliwi zestawienie tunelu. Jeśli zakończymy skrypt kodem 1 (błąd) wówczas połączenie zostanie przerwane!. Nie musisz martwić się o uprawnienia jak i kasowanie pliku tymczasowego - OpenVPN sam o to dba.
Tyle teorii odnośnie sposobu w jaki OpenVPN umożliwia dodatkową weryfikację wprowadzonych przez użytownika danych. Tak naprawdę w tym momencie mógłbym zakończyć ten tutorial. Wybór dalszej implementacji zależy bowiem od środowiska w firmie, inwencji admina czy przyjętej polityki. Możesz tutaj podpiąć skrypt odpytujący serwer radiusa. Możesz pokusić się o uwierzytelnienie przez Active Directory. Możesz także dodać dość popularny mechanizm TOTP (Time-based one-time password). Świetnie nada się w tym celu biblioteka do Pythona pyotp. Być może zbiorę się w sobie niebawem i opiszę dokładnie mechanizm TOTP jako MFA do OpenVPN-a. Tymczasem miało być o Enigmie!
MFA z wykorzystaniem szyfrów Enigmy
W tej części zakładam, że czytelnik zna podstawy działania maszyny Enigma i wie czym są pojęcia Grundstellung (ustawienie wirników), Ringstellung(ustawienia pierścieni) czy też Steckerbrett verbindungen (połączenia na krosownicy kablowej). Jeśli nie, to zapraszam do mojego opisu w menu po lewej stronie. Starałem się opisać tam wszystkie te najważniejsze terminy prosto i konkretnie.
Po stronie serwera będziemy potrzebowali programu aenig4. Jest to konsolowy emulator Enigmy M4. Po rozpakowaniu ZIP-a program należy skompilować (jest też gotowa binarka pod Windows). Upewnij się, że w swojej dystrybucji linuksa masz zainstalowany kompilator gcc, program make itd. W przypadku Debiana i jego pochodnych mam tutaj na myśli pakiet build-essential. Upewnij się też czy masz zainstalowany pakiet help2man i w razie potrzeby doinstaluj (sudo apt-get install help2man). Na listingu 2 przedstawiłem proces kompilacji i instalacji.
Listing 2. kompilacja programu aenig4
Po stronie klienta (użytkownika) posłużymy się emulatorem Enigmy pod Androida. Teraz musisz zastanowić się, czy ustawienia parametrów Enigmy (wirniki, pierścienie) będą generowane na podstawie jakichś zmiennych środowiskowych (data, czas, adres IP etc.) czy też na podstawie wcześniej wygenerowanej listy (odpowiednik niemieckiej książki kodów). Na potrzeby niniejszego artykułu przyjąłem, że ustawienia wirtualnej Enigmy będą brane z wcześniej wygenerowanej listy kodów - dokładnie jak miało to miejsce podczas II Wojny. Przygotowałem w tym celu specjalny generator ustawień Enigmy. Jednakże aby uczynić nasz przykład jeszcze bardziej realistyczny wykorzystamy tutaj istniejący niemiecki dokument - patrz rys.3.
rys. 3 - miesięczna lista kodów obowiązująca w październiku 1944 (CSV)
Zauważ, że powżysza lista przygotowana jest dla Enigmy M3. Na serwerze mamy jednak emulator Enigmy M4 (program aenig4). Będziemy musieli to uwzględnić, mianowicie w skrypcie na serwerze będziemy musieli ustawić czwarty wirnik w pozycji
dzień | ustawienia wirników (Walzenlage) | ustawienia pierścieni (Ringstellung) | reflektor (UKW) | ustawienia łącznicy (stecker verb.) | ustawienia początkowe (Grundstellung) | parametry wywołania aenig4 |
---|---|---|---|---|---|---|
15 | III II V | 06 16 02 F P B | UKW-B | GT YC EJ UA RX PN IS WB MH ZV | Y Z K | aenig4 -k "b beta III II V 01 06 16 02 AYZK GT YC EJ UA RX PN IS WB MH ZV" |
rys. 4 - konfigurowanie ustawień |
rys. 5 - ustawienia początkowe |
rys. 6 - zakodowana testowa wiadomość |
Ustaw aplikacje w telefonie zgodnie z parametrami z rys. 4. Następnie wpisz na wirtualnej klawiatuże TESTOWAXWIADOMOSC. Teraz na serwerze VPN wywołaj polecenie
echo TESTOWAXWIADOMOSC | aenig4 -k "B beta III II V 01 06 16 02 AYZK GT YC EJ UA RX PN IS WB MH ZV" --filter
Porównaj teraz wynik polecenia z tekstem jaki pojawił się w aplikacji na telefonie. Dla powyższych ustawień zakodowany tekst to: UFJSCLSJUKWXGIPKK. Jeśli ciągi znaków nie zgadzają się, musisz dokładnie przeanalizować każdy z parametrów. W szczególności łatwo o pomyłkę w łącznicy. Zauważ, że w przypadku aplikacji na telefon ustawienia pierścieni podajemy literami, a w linuksie cyframi. Tutaj także łatwo o pomyłkę.
parser pliku CSV - generator ustawien Enigmy
Pozostało nam napisanie parsera książki kodów dla danego miesiąca. Zakładam tutaj, że książka kodów będzie dostępna w postaci pliku CSV na serwerze. OpenVPN podczas łączenia klienta musi przeanalizować (przeparsować) ten plik aby odczytać parametry Enigmy dla danego dnia. Następnie skrypt musi wywołać emulator Enigmy aby uzyskać zakodowaną wartość i porównać ją z hasłem przekazanym przez użytkownika. Poniżej przedstawiam wycinek pliku CSV dla listy kodów z rysunku 3.
Listing 3. wycinek pliku schluesselTafel.csv
Na listingu 4. przedstawiłem przykładowy helper do OpenVPN-a. Skrypt napisany w bashu, parsuje plik CSV i porównuje zaszyfrowane hasło z ciągiem wprowadzonym przez użytkownika w kliencie OpenVPN
Listing 4. Przykładowy helper dla OpenVPN-a napisany w bashu.
Pozostaje jeszcze kwestia hasła jakie podlegać będzie szyfrowaniu (co użytkownik ma wprowadzić jako tekst do zakodowania). Na potrzeby artykułu przyjąłem, że hasło będą stanowić dwa pierwsze bloki ostatniej kolumny (Kenngruppen) - pisane bez spacji i z wielkiej litery. W omawiannym przypadku będzie to
Dalszą część artykułu pisałem następnego dnia, tj. 16/07. Na rysunkach 7-10 przedstawiono zrzuty z aplikacji Androidowej dla tego dnia. Zakodowane hasło należy podać w kliencie OpenVPN.
rys. 7-10 ustawienia emulatora Enigmy
dzień | ustawienia wirników (Walzenlage) | ustawienia pierścieni (Ringstellung) | reflektor (UKW) | ustawienia łącznicy (stecker verb.) | ustawienia początkowe (Grundstellung) | parametry wywołania aenig4 |
---|---|---|---|---|---|---|
16 | I II III | 07 11 15 G K O | UKW-B | WZ AB MO TF RX SG QU VI YN EL | W Y T | aenig4 -k "B beta I II III 01 07 11 15 AWYT WZ AB MO TF RX SG QU VI YN EL" |
Jeśli wprowadzone hasło będzie zgodne z ciągiem wygenerowanym na serwerze, wówczas skrypt na serwerze zakończy działanie z kodem 0 i połączenie VPN zestawi się. Gdyby się tak nie stało należałoby sprawdzić jakie hasło wygenerował serwer. W tym celu najprościej będzie zapisać je do pliku loga wraz z pozostałymi parametrami wirtualnej Enigmy. Być może w samym pliku CSV jest dzieś błąd i emulator Enigmy zgłasza błąd.
Podobny mechanizm weryfikacji dwu-etapowej możesz zastosować dla innych usług - np. serwera SSH. Wówczas możnaby wykorzystać któryś z istniejących modułów / rozszerzeń dla PAM (pam_script.so, pam_python.so etc.).
07/2024 (c) MS
ms<małpa> helion kropka pl