Jak ukryć ruch OpenVPN przy pomocy stunnel

Spis treści

Ci z nas, którzy korzystają codziennie z internetu, wiedzą, że większość nawiązywanych połączeń między dwoma punktami w tej sieci globalnej przechodzi przez szereg węzłów i jest podatnych na przechwycenie i podsłuchanie. Nawet jeśli ruch z określonymi serwisami jest szyfrowany, to w dalszym ciągu nie jesteśmy w stanie ukryć pewnych newralgicznych informacji, takich jak docelowy adres IP i port, na którym nasłuchuje zdalna usługa. Wszystkie połączenia sieciowe zestawiane z naszego komputera czy routera domowego przechodzą przez infrastrukturę ISP, u którego mamy wykupiony internet. Tacy ISP są nam w stanie pod naciskiem rządu zablokować połączenia z konkretnymi adresami wprowadzając na terenie danego kraju cenzurę treści dostępnej w internecie, czego obecnie jesteśmy świadkami w Europie, no i w Polsce. Można oczywiście posiłkować się rozwiązaniami opartymi o VPN, np. stawiając serwer OpenVPN w innym kraju. Problem jednak w tym, że ruch OpenVPN różni się od tego, z którym mamy do czynienia w przypadku choćby HTTPS. Jest zatem możliwość rozpoznania ruchu VPN i zablokowania go stosując głęboką analizę pakietów (Deep Packet Inspection, DPI). By się przed tego typu sytuacją ochronić, trzeba upodobnić ruch generowany przez OpenVPN do zwykłego ruchu SSL/TLS. Do tego celu służy narzędzie stunnel.

Konfiguracja usługi stunnel na serwerze

Konfiguracja dla demona stunnel jest trzymana standardowo w katalogu /etc/stunnel/ . Domyślnie jest tam tylko plik README z instrukcjami, które pomogą nam skonfigurować usługę. Jest tam zawarta informacja, że przykładowy plik konfiguracyjny znajduje się w /usr/share/doc/stunnel4/examples/stunnel.conf-sample . Trzeba go będzie dostosować zarówno na serwerze jak i na kliencie. Przekopiujmy zatem ten plik do katalogu /etc/stunnel/ nazywając go dowolnie ale tak, by jego rozszerzenie wskazywało na .conf , przykładowo:

# cp /usr/share/doc/stunnel4/examples/stunnel.conf-sample /etc/stunnel/morfitronik-stunnel-server.conf

Teraz musimy poddać edycji tak utworzony plik i zmienić w nim szereg rzeczy:

foreground = yes
setuid = stunnel4
setgid = stunnel4
pid = /var/run/stunnel4/stunnel4.pid
;chroot = /var/lib/stunnel4/
debug = 7
;output = /var/log/stunnel.log
socket = l:TCP_NODELAY=1
sslVersion = TLSv1.2
ciphers = ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDH-RSA-AES256-GCM-SHA384:ECDH-ECDSA-AES256-GCM-SHA384:AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:ECDH-RSA-AES128-GCM-SHA256:ECDH-ECDSA-AES128-GCM-SHA256:AES128-GCM-SHA256

[openvpn]
client = no
accept = 11.22.33.44:443
connect = 127.0.0.1:1194
delay = yes
CAfile = /etc/openvpn/certs/morfitronik-ca.crt
cert = /etc/openvpn/certs/morfitronik-server-vpn-stunnel.crt
key = /etc/openvpn/certs/morfitronik-server-vpn.key
requireCert = yes
verifyChain = yes
;verifyPeer = yes

Ten powyższy plik składa się z dwóch części. W pierwszej mamy parametry dla samego demona, w drugiej zaś konfigurację usługi. Większość opcji powinna być raczej zrozumiała ale wypadałoby powiedzieć kilka słów na temat sekcji konfigurującej usługę openvpn .

Generalnie rzecz biorąc, nazwa samej sekcji jest dowolna ale musi być ujęta w [ ]. Parametr client przełącza demona w tryb klienta lub serwera. W accept definiujemy adres i port serwera, na który będą przychodzić zapytania z sieci (zewnętrzny adres IP). Z kolei w connect określamy adres pętli zwrotnej i jej port, na którym będzie nasłuchiwał demon OpenVPN. Pozostałe opcje dotyczą konfiguracji certyfikatów i akceptowania jedynie tych klientów, którzy przedstawią ważny certyfikat.

Konfiguracja certyfikatów

Każda usługa oferująca szyfrowanie danych, a do takich zalicza się stunnel , wymaga zestawienia bezpiecznego połączenia. Potrzebne nam zatem będą odpowiednie certyfikaty i klucze szyfrujące. Nie musimy ich jednak generować na nowo. Mając działający serwer OpenVPN możemy wykorzystać jego certyfikaty i podać tylko odpowiednie ścieżki w pliku /etc/stunnel/morfitronik-stunnel-server.conf , tak jak to zostało zrobione wyżej. Trzeba tylko pamiętać, że stunnel akceptuje certyfikaty jedynie w formacie PEM. Dodatkowo, parametry Diffie-Hellman'a (DH), które w przypadku OpenVPN są zawarte w pliku dh4096.pem trzeba dołączyć do pliku z certyfikatem dla demona stunnel :

# cat /etc/openvpn/certs/dh4096.pem >> /etc/openvpn/certs/morfitronik-server-vpn-stunnel.crt

W konfiguracji demona stunnel mogliśmy zauważyć parametry requireCert , verifyChain oraz verifyPeer . Odpowiadają one, jak nazwa wskazuje, za wymaganie i weryfikacje certyfikatów klienckich. W zasadzie są to parametry jedynie opcjonalne i generalnie potrzebny nam jest zwykle jeden z nich, tj. verifyChain lub verifyPeer . W przypadku wykorzystania któregoś z nich, parametr requireCert jest implikowany automatycznie.

Jaka jest zatem różnica między verifyChain i verifyPeer ? Certyfikaty zarówno serwera jak i klienta stunnel są podpisane przez CA. Jeśli klient przedstawi certyfikat podpisany przez inne CA niż to, którym został podpisany certyfikat serwera, to verifyChain nie zezwoli na podłączenie się takiej maszynie. Jeśli zaś chodzi o verifyPeer , to w tym przypadku certyfikat klienta musi zostać dołączony do pliku zdefiniowanego w opcji CAfile w konfiguracji stunnel .

Konfiguracja usługi stunnel na kliencie

W przypadku drugiej strony połączenia, tj. klienta, również musimy w odpowiedni sposób skonfigurować sobie demona stunnel . Kopiujemy zatem przykładowy plik konfiguracyjny do katalogu /etc/stunnel/ :

# cp /usr/share/doc/stunnel4/examples/stunnel.conf-sample /etc/stunnel/morfitronik-stunnel-client.conf

Plik poddajemy edycji i dostosowujemy w nim poniższe parametry:

foreground = yes
setuid = stunnel4
setgid = stunnel4
pid = /var/run/stunnel4/stunnel4.pid
;chroot = /var/lib/stunnel4/
debug = 7
;output = /var/log/stunnel.log
socket = r:TCP_NODELAY=1
sslVersion = TLSv1.2
ciphers = ECDHE-RSA-AES256-GCM-SHA384

[openvpn]
client = yes
accept = 127.0.0.1:1194
connect = 11.22.33.44:443
delay = yes
CAfile = /etc/openvpn/certs/morfitronik-ca.crt
cert = /etc/openvpn/certs/morfitronik-client-vpn-laptop.crt
key = /etc/openvpn/certs/morfitronik-client-vpn-laptop.key
requireCert = yes
verifyChain = yes
;verifyPeer = yes
checkHost = morfitronik-server-vpn

Konfiguracja klienta stunnel jest podobna do tej po stronie serwera. Sekcja z parametrami dla samego demona nie różni się zbytnio, no może za wyjątkiem opcji socket (o niej będzie później) oraz zawęziliśmy też możliwe do wykorzystania szyfry. O ile serwer powinien wspierać wszystkie szyfry TLS 1.2, o tyle klient może ograniczyć się do jednego.

Jeśli zaś chodzi o sekcję usługi openvpn , to naturalnie przestawiamy parametr client tak, by demon stunnel wiedział, że ma pracować w roli klienta. Z kolei w accept podajemy adres pętli zwrotnej i port, na którym działa klient OpenVPN, a w connect wpisujemy adres zdalnego serwera i jego port.

Konfiguracja certyfikatów i uwierzytelniania drugiego końca połączenia jest mniej więcej taka sama co w przypadku serwera. Możemy także skorzystać z parametru checkHost weryfikującego pole CN w certyfikacie serwera. Generalnie możemy dowolnie sobie te cztery ostatnie opcje dobrać, tylko pamiętajmy o różnicach między verifyChain i verifyPeer .

Usługa dla systemd

W paczce stunnel4 próżno szukać usługi dla systemd. Możemy jednak takową dorobić sami. Wystarczy stworzyć plik /etc/systemd/system/stunnel@.service i dodać do niego poniższą zawartość:

[Unit]
Description=SSL tunnel for %I
Documentation=man:stunnel(8)
After=syslog.target network.target

[Service]
Type=simple
PrivateTmp=true
ExecStart=/usr/bin/stunnel /etc/stunnel/%i.conf
ExecReload=/bin/kill -HUP $MAINPID
PIDFile=/var/run/stunnel4/stunnel4.pid

[Install]
WantedBy=multi-user.target

Pamiętajmy tylko, by dodać do pliku konfiguracyjnego demona stunnel opcję foreground=yes , bo ten demon nie może nam forkować przy takiej konfiguracji usługi systemd jak widzimy powyżej.

Musimy także stworzyć katalog, w którym będzie przechowywany PID demona stunnel tak, by nie było problemów ze zrzucaniem uprawnień. Tworzymy zatem plik /etc/tmpfiles.d/stunnel.conf i wrzucamy do niego poniższą zawartość:

#Type   Path              Mode    UID       GID        Age   Arguments
d       /run/stunnel4     0755    stunnel4  stunnel4   -     -

Usługę uruchamiamy standardowo podając po znaku @ nazwę pliku konfiguracyjnego obecnego w katalogu /etc/stunnel/ ale bez końcówki .conf przykładowo:

# systemctl enable stunnel@morfitronik-stunnel-server
# systemctl start stunnel@morfitronik-stunnel-server

Dostosowanie konfiguracji OpenVPN

Generalnie rzecz biorąc, jeśli dysponowaliśmy wcześniej działającym serwerem VPN i klienci byli w stanie z nim nawiązywać połączenia bez większego problemu, to dostosowanie całej konfiguracji OpenVPN sprowadza się jedynie do zmiany kilku parametrów w plikach serwera i klienta (katalog /etc/openvpn/ ). W obu przypadkach musimy przepisać opcje odpowiadające za protokół i adres IP. Wykorzystywany protokół musi wskazywać na TCP, natomiast jako adres IP trzeba określić pętlę zwrotną (loopback). Jako, że demon stunnel będzie zbierał ruch od OpenVPN i go szyfrował przed przesłaniem na drugi koniec połączenia, to możemy także zrezygnować z szyfrowania ruchu w samym VPN:

Dla serwera będą to te trzy poniższe wpisy:

local 127.0.0.1
proto tcp
cipher none

A dla klienta te trzy:

proto tcp
remote 127.0.0.1 1194
cipher none

Wykorzystywanie protokołu TCP nie jest najlepszym pomysłem w przypadku VPN, bo drastycznie odbije się to na wydajności ogólnej połączenia ale nie mamy zbytnio innego wyjścia. Oprogramowanie stunnel potrafi zabezpieczać jedynie komunikację opartą na protokole TCP. Oczywiście nic nie stoi na przeszkodzie, by uruchomić dwie osobne instancje VPN i wykorzystywać je w zależności od panujących warunków cenzorskich. Pamiętajmy tylko o dostosowaniu opcji server w konfiguracji serwera OpenVPN odpowiadającej za adresację sieci LAN wykorzystywanej w VPN.

Dodatkowo, w przypadku klienta VPN trzeba także uwzględnić dodatkową trasę w tablicy routingu. Musi ona wskazywać na adres zewnętrzny naszego serwera.

route 11.22.33.44 255.255.255.255 net_gateway

Dobór portu dla usługi stunnel

Przy określaniu portu, na którym ma nasłuchiwać demon stunnel , dobrze jest wybrać ten, który jest wykorzystywany oficjalnie przez jakieś usługi. Poniżej jest krótka rozpiska takich portów:

nsiiops      261/tcp   # IIOP Name Service over TLS/SSL
https        443/tcp   # http protocol over TLS/SSL
smtps        465/tcp   # smtp protocol over TLS/SSL (was ssmtp)
nntps        563/tcp   # nntp protocol over TLS/SSL (was snntp)
imap4-ssl    585/tcp   # IMAP4+SSL (use 993 instead)
sshell       614/tcp   # SSLshell
ldaps        636/tcp   # ldap protocol over TLS/SSL (was sldap)
ftps-data    989/tcp   # ftp protocol, data, over TLS/SSL
ftps         990/tcp   # ftp protocol, control, over TLS/SSL
telnets      992/tcp   # telnet protocol over TLS/SSL
imaps        993/tcp   # imap4 protocol over TLS/SSL
ircs         994/tcp   # irc protocol over TLS/SSL
pop3s        995/tcp   # pop3 protocol over TLS/SSL (was spop3)
msft-gc-ssl  3269/tcp  # Microsoft Global Catalog with LDAP/SSL

Generalnie rzecz biorąc, chodzi tutaj o niewzbudzanie podejrzeń. Jeśli przy skanowaniu naszego serwera zostanie wykryty port 993, który jest standardowo powiązany z bezpiecznym protokołem pocztowym, to raczej mało kto pomyśli, że tutaj nasłuchuje serwer VPN chroniony przez demona stunnel . Gdybyśmy na tym porcie umieścili jedynie sam serwer OpenVPN, to podłączenie do niego (nawet zakończone niepowodzeniem) ujawniłoby, że na tym porcie jest VPN. A tak, ruch będzie wyglądał jakby został zaszyfrowany zwykłym protokołem SSL/TLS i głęboka inspekcja pakietów nie będzie w stanie wykazać co na tym porcie faktycznie działa.

Problem z wydajnością połączenia VPN

W przypadku obserwowania znacznego spowolnienia transmisji danych przez VPN po wdrożeniu demona stunnel , możemy spróbować włączyć opcję TCP_NODELAY na obu końcach połączenia dodając do pliku konfiguracyjnego serwera poniższy wpis:

socket = l:TCP_NODELAY=1

Podobną linijkę trzeba dodać do pliku konfiguracyjnego klienta:

socket = r:TCP_NODELAY=1

W razie innych problemów z działaniem demona stunnel warto zajrzeć również do do FAQ na stronie projektu, jak i do man stunnel.

Mikhail Morfikov avatar
Mikhail Morfikov
Po ponad 10 latach spędzonych z różnej maści linux'ami (Debian/Ubuntu, OpenWRT, Android) mogę śmiało powiedzieć, że nie ma rzeczy niemożliwych i problemów, których nie da się rozwiązać. Jedną umiejętność, którą ludzki umysł musi posiąść, by wybrnąć nawet z tej najbardziej nieprzyjemniej sytuacji, to zdolność logicznego rozumowania.