Ukrywanie udostępnianych na routerze usług sieciowych

Port knocking ma na celu zewnętrzne otwieranie portów na zaporze w oparciu o stukanie w inne i przy tym niekoniecznie otwarte porty. Przykładowo mamy usługę SSH na porcie 22 na interfejsie WAN ale ten port zostanie standardowo odfiltrowany w iptables i nikt z zewnątrz nie da rady się połączyć z naszym routerem. Możemy, co prawda, otworzyć ten port ale wtedy wszyscy będą mieć możliwość podłączenia się do routera. Oczywiście pozostanie jeszcze im do wprowadzenia hasło lub posłużenie się kluczem SSH ale to nie zmienia faktu, że usługa będzie otwarta na świat.

Są dwa rodzaje port knockingu . Pierwszy z nich do zaimplementowania via knockd , oraz drugi (o wiele bezpieczniejszy), przy pomocy fwknopd . Korzystanie z knockd ma taką wadę, że można podsłuchać sekwencję portów. A jeśli ktoś ją pozna, jego IP zostanie dopisane do filtra jako zaufane. Jeśli atakujący nie ma pojęcia o zastosowanej metodzie, ten mechanizm powinien być w miarę bezpieczny. Niemniej jednak, knockd jest strasznie przestarzały i lepiej sobie nim głowy nie zawracać i od razu wdrożyć fwknopd.

Proces uwierzytelniania i autoryzacji w przypadku fwknopd odbywa się przy pomocy czegoś na wzór zaszyfrowanego ciasteczka wysyłanego na określony port na routerze i nie musimy przy tym otwierać tego portu na firewallu. Daemon fwknopd działa w niższej warstwie niż iptables i potrafi wyłapać pakiety zanim te nawet dotrą do zapory. Wszystko działa w oparciu o klucze, które są generowane per klient i dostarczane na serwer. Później już tylko się przesyła zaszyfrowane ciasteczko do serwera i po jego pomyślnym odszyfrowaniu, serwer podejmuje szereg działań -- od dodania prostej regułki do iptables umożliwiającej dostęp do usługi nasłuchującej na określonym porcie, na wykonywaniu skryptów kończąc.

Poniższy howto nie korzysta z filtra iptables tworzonego przez skrypty OpenWRT. Dokładny opis jak stworzyć własny skrypt firewalla został umieszczony w tym wątku.

Instalujemy na routerze daemona fwknopd :

# opkg update
# opkg install fwknopd

Na klientach zaś będzie trzeba doinstalować narzędzie fwknop -- w różnych dystrybucjach ten pakiet się inaczej nazywa -- u mnie na debianie zwie on się fwknop-client .

By skonfigurować klienta, któremu zezwolimy na zdalne połączenie się do naszego routera domowego, musimy wygenerować klucze szyfrujące. Wpisujemy zatem w terminalu poniższą linijkę:

$ fwknop -A tcp/22 -a 11.11.11.11 -D 22.22.22.22 --key-gen --use-hmac -p 23451 --save-rc-stanza
[*] Creating initial rc file: /home/morfik/.fwknoprc.
[+] Wrote Rijndael and HMAC keys to rc file: /home/morfik/.fwknoprc

Użyte opcje powyżej definiują port (-A tcp/22), który powinien zostać otwarty po pomyślnym rozszyfrowaniu ciasteczka, adres IP (-a 11.11.11.11), z którego może nastąpić połączenie i adres routera na porcie WAN (-D 22.22.22.22). Z kolei --key-gen oraz --use-hmac mają na celu wygenerowanie unikatowych kluczy, dzięki którym zostaniemy uwierzytelnieni przy połączeniu. Opcja -p 23451 określa port, na który będą przychodzić zaszyfrowane ciasteczka. Parametr --save-rc-stanza zapisuje wprowadzone w powyższej linijce opcje do pliku .fwknoprc w katalogu domowym użytkownika.
Plik z grubsza jest podzielony na dwie części. W jednej z nich mamy sekcję [default] , która to określa parametry domyślne dla wszystkich połączeń. Kolejne bloki mogą posiadać szereg podobnych opcji i w przypadku, gdy w którymś z nich występuje ten sam parametr, jego wartość zostaje nadpisana. Poniżej jest przykład sekcji [default] :

[default]
DIGEST_TYPE		sha512
FW_TIMEOUT		20
SPA_SERVER_PORT		23451
SPA_SERVER_PROTO	udp
#ALLOW_IP		<ip addr>
#SPOOF_USER		<username>
#SPOOF_SOURCE_IP	<IPaddr>
#TIME_OFFSET		0
USE_GPG			N
#GPG_HOMEDIR		/path/to/.gnupg
#GPG_SIGNER		<signer ID>
#GPG_RECIPIENT		<recipient ID>

Niżej zaś opcje dla połączenia z routerem:

[22.22.22.22]
SPA_SERVER_PORT             23451
ALLOW_IP                    11.11.11.11
ACCESS                      tcp/22
SPA_SERVER                  22.22.22.22
KEY_BASE64                  QwJANH9Aic0KVwanEgWuS2uF+M+9uwmhJKwV5/m2WNk=
HMAC_KEY_BASE64             8o3xm80SIt8jd6YjgPT1V4gOvE+r9y2xNOl9ktSk8Hbt//OCNn0j1o4WKbxVbB/8R+c4i+7ketBqx38TaErfaw==
USE_HMAC                    Y

KEY_BASE64 oraz HMAC_KEY_BASE64 to klucze, które musimy przesłać w bezpieczny sposób na router. Opcji do określenia jest dość sporo, powyższy kawałek to niezbędne minimum.

Rzućmy teraz okiem na router -- logujemy się na niego via SSH. Musimy pierw wyedytować dwa pliki /etc/fwknop/access.conf oraz /etc/fwknop/fwknopd.conf . W pliku /etc/fwknop/access.conf umieszczamy min. klucze wszystkich hostów, które będą mieć prawo łączyć się do routera, przykładowo:

SOURCE		11.11.11.11
OPEN_PORTS  		tcp/22
FW_ACCESS_TIMEOUT 		20
REQUIRE_SOURCE_ADDRESS		Y
KEY_BASE64                  QwJANH9Aic0KVwanEgWuS2uF+M+9uwmhJKwV5/m2WNk=
HMAC_KEY_BASE64             8o3xm80SIt8jd6YjgPT1V4gOvE+r9y2xNOl9ktSk8Hbt//OCNn0j1o4WKbxVbB/8R+c4i+7ketBqx38TaErfaw==

Tego typu zwrotek można umieszczać ile się chce, trzeba jednak pamiętać, że plik jest przeszukiwany od góry do dołu pod kątem dopasowań i pierwsze z nich zostanie zaakceptowane. Porty oraz adresy można podawać w jednej linijce, oddzielając je za pomocą przecinka, z kolei FW_ACCESS_TIMEOUT określa czas otwarcia okna.
W drugim pliku, /etc/fwknop/fwknopd.conf , definiujemy opcje samego daemona:

VERBOSE                     1;
PCAP_FILTER                 udp port 23451;
PCAP_INTF                   eth0;
IPT_INPUT_ACCESS        ACCEPT, filter, INPUT, 4, fwknop_input, 1;

Precyzujemy interfejs, który ma być poddany analizie pakietów. W moim przypadku jest to eth0 , czyli interfejs WAN . Ważne jest by interfejs posiadał adres IP, bo jeśli go nie posiada, daemon nie wystartuje. Dobrze jest też zmienić domyślny port, z którego wyłapywane będą pakiety wysyłane przez klienta fwknop.

Ostatnia linijka z tych powyżej określa, w którym miejscu umieścić łańcuch fwknop_input, do którego z kolei trafiać będą reguły generowane przez fwknopd . ACCEPT określa politykę reguły, filter tablicę iptables, INPUT łańcuch w tej tablicy. Cyfra 4, definiuje pozycję, na której umieścić łańcuch fwknop_input, z kolei zaś cyfra 1 precyzuje, na której pozycji będą dodawane reguły przez fwknopd w tym łańcuchu. Warto także ustawić tryb verbose , przynajmniej z początku i obserwować logi systemowe podczas próby połączenia.

Musimy także wykomentować te dwie poniższe linijki z pliku /etc/fwknop/fwknopd.conf :

#IPT_FORWARD_ACCESS      ACCEPT, filter, zone_wan_forward, 1, FWKNOP_FORWARD, 1;
#IPT_DNAT_ACCESS         DNAT, nat, zone_wan_prerouting, 1, FWKNOP_PREROUTING, 1;

Startujemy daemona fwknopd i dodajemy go do autostartu routera:
# /etc/init.d/fwknopd enable
# /etc/init.d/fwknopd start

Jeśli ustawiliśmy wszystko z powyższym opisem, po przesłaniu ciasteczka, będziemy mieć okno otwarte przez 20s -- na ten czas zostanie dopisana reguła do iptables. Po zamknięciu okna, komunikacja będzie się odbywać za pośrednictwem reguły --ctstate RELATED,ESTABLISHED , którą już mamy dodaną do iptables.
Sprawdźmy pierw jak port od SSH (22) odpowie na skan syn:

$ sudo nmap -sS -Pn -p 22 22.22.22.22
Starting Nmap 6.25 ( http://nmap.org ) at 2014-10-26 15:23 CET
Nmap scan report for 22.22.22.22
Host is up (0.050s latency).
PORT   STATE  SERVICE
22/tcp closed ssh
Nmap done: 1 IP address (1 host up) scanned in 1.35 seconds

Zatem, port jest zamknięty, mimo, że usługa na nim nasłuchuje. Prześlijmy teraz ciasteczko i ponownie przeskanujmy port:

$ fwknop -n 22.22.22.22 -v -p 23451
...
Generating SPA packet:
			protocol: udp
		 source port: <OS assigned>
	destination port: 23451
			 IP/host: 22.22.22.22
send_spa_packet: bytes sent: 289
$ sudo nmap -sS -Pn -p 22 22.22.22.22
Starting Nmap 6.25 ( http://nmap.org ) at 2014-10-26 15:23 CET
Nmap scan report for 22.22.22.22
Host is up (0.067s latency).
PORT   STATE SERVICE
22/tcp open  ssh
Nmap done: 1 IP address (1 host up) scanned in 0.90 seconds

Po przesłaniu ciasteczka, port został otwarty, czyli odpowiednia reguła została dodana do iptables. Sprawdźmy czy tak faktycznie się stało.

# iptables -nvL
Chain INPUT (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
...
	6   473 fwknop_input  all  --  *      *       0.0.0.0/0            0.0.0.0/0           
...
Chain fwknop_input (2 references)
 pkts bytes target     prot opt in     out     source               destination         
	6   468 ACCEPT     tcp  --  *      *       11.11.11.11          0.0.0.0/0            tcp dpt:22 /* _exp_1414272273 */

Jak widzimy powyżej, źródło wskazuje adres IP, z którego może nastąpić połączenie. Również jest określony port. na które mogą przychodzić zapytania. Zatem wszystko jest w porządku. Po 20s, okno powinno się automatycznie zamknąć usuwając tym samym regułę zezwalającą na dostęp do serwera dla nowych połączeń.

NAT

Co w przypadku jeśli mamy jakąś usługę zlokalizowaną na maszynach za routerem i to do nich chcielibyśmy uzyskać dostęp? Musimy wygenerować nieco inną konfigurację dla połączenia. Wydajemy zatem poniższe polecenie na kliencie:

$ fwknop -A tcp/22 -a 11.11.11.11 -D 22.22.22.22 --key-gen --use-hmac -p 23451 --nat-access 192.168.1.150 --nat-port 2222 --save-rc-stanza

Przy pomocy tej linijki, powstanie poniższy blok

[22.22.22.22]
KEY_BASE64                  QwJANH9Aic0KVwanEgWuS2uF+M+9uwmhJKwV5/m2WNk=
HMAC_KEY_BASE64             8o3xm80SIt8jd6YjgPT1V4gOvE+r9y2xNOl9ktSk8Hbt//OCNn0j1o4WKbxVbB/8R+c4i+7ketBqx38TaErfaw==
SPA_SERVER_PORT             23451
ALLOW_IP                    11.11.11.11
ACCESS                      tcp/22
SPA_SERVER                  22.22.22.22
USE_HMAC                    Y
NAT_ACCESS                  192.168.1.150
NAT_PORT                    2222

Pojawiły się dwie dodatkowe opcje: --nat-access oraz --nat-port . Pierwsza z nich określa adres IP hosta za routerem, druga zaś port na routerze, na który będą przychodzić zapytania. Z kolei port usługi (-A tcp/22) dotyczy otwartego portu na hoście, a nie na routerze.

Na routerze musimy także dostosować nieco daemona fwknopd . Edytujemy zatem plik /etc/fwknop/fwknopd.conf i zmieniamy odpowiednie linijki:

ENABLE_IPT_FORWARDING       Y;
IPT_DNAT_ACCESS         DNAT, nat, PREROUTING, 3, fwknop_prerouting, 1;
IPT_FORWARD_ACCESS      ACCEPT, filter, FORWARD, 5, fwknop_forward, 1;

Niczym się one zbytnio nie różnią, od tej reguły określanej wcześnie -- jedyne co, to zmieniły się pozycje, łańcuchy i tablice.

W pliku /etc/fwknop/access.conf musimy także uwzględnić porty forwardowane:

SOURCE                          11.11.11.11                              
OPEN_PORTS                      tcp/22,tcp/2222                           
FW_ACCESS_TIMEOUT               20                                            
REQUIRE_SOURCE_ADDRESS          Y                                      
KEY_BASE64                  QwJANH9Aic0KVwanEgWuS2uF+M+9uwmhJKwV5/m2WNk=
HMAC_KEY_BASE64             8o3xm80SIt8jd6YjgPT1V4gOvE+r9y2xNOl9ktSk8Hbt//OCNn0j1o4WKbxVbB/8R+c4i+7ketBqx38TaErfaw==

Restartujemy daemona i jeśli teraz byśmy przesłali ciasteczko do serwera via:

$ fwknop -n 22.22.22.22 -v -p 23451

Na routerze zostałyby dodane do iptables poniższe reguły :

root@the-mountain:~# iptables -nvL -t nat
Chain PREROUTING (policy ACCEPT 83 packets, 5376 bytes)
 pkts bytes target     prot opt in     out     source               destination         
...
  163 14336 fwknop_prerouting  all  --  *      *       0.0.0.0/0            0.0.0.0/0           
...
Chain fwknop_prerouting (1 references)
 pkts bytes target     prot opt in     out     source               destination         
	0     0 DNAT       tcp  --  *      *       11.11.11.11          0.0.0.0/0            tcp dpt:2222 /* _exp_1414337691 */ to:192.168.1.150:22
root@the-mountain:~# iptables -nvL
...
Chain FORWARD (policy DROP 0 packets, 0 bytes)
...
	0     0 fwknop_forward  all  --  *      *       0.0.0.0/0            0.0.0.0/0
...
Chain fwknop_forward (1 references)
 pkts bytes target     prot opt in     out     source               destination
	0     0 ACCEPT     tcp  --  *      *       11.11.11.11          192.168.1.150        tcp dpt:22 /* _exp_1414337691 */

I w ten sposób wszystkie połączenia do routera na port 2222, zostaną przekierowane do hosta za routerem na port 22.

Dzięki mechanizmowi jaki oferuje fwknopd możemy spać spokojnie nawet w przypadku gdy ktoś wystrzeli publicznie z informacją o jakiejś luce 0day dla SSH, a to z tego powodu, że sama usługa jest schowana za firewallem cały czas.