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.