poniedziałek, 7 lutego 2011

Knock, knock, knocking on the server ports

Mamy mocno zabezpieczony serwer gdzieś w Internecie. Aby nim zarządzać, chcemy mieć doń dostęp przez SSH. Ale nie chcemy, by dostęp ów miały osoby niepowołane, a zwłaszcza wszelakie sieciowe skanery portów. Rozwiązanie z pozoru łatwe. Do iptables dodajemy regułkę:
-A INPUT -p tcp --dport 22 -s [nasz_ip] -m state --state NEW -j ACCEPT
Schody zaczynają się, gdy zachodzi potrzeba od czasu do czasu dostać się na serwer np. z domu lub z laptopa w delegacji. Nasze IP bowiem bez wątpienia będzie zmienne. Zatem należałoby zastosować taką metodę, która na pewien czas otworzy nam dostęp. I tu z pomocą przychodzi knockd. Jest to sprytny demon, który nasłuchuje na wybranym interfejsie i po napotkaniu sekwencji połączeń tcp lub udp na określone porty (czyli "zapukań") wykonuje jakąś komendę. Czyli np. dodaje lub usuwa jakąś regułkę iptables.

Konfiguracja serwera

W pierwszej kolejności warto określić inny niż standardowy port dla SSH. Niechaj 22 zostanie dla znanych adresów IP na stałe otwartych w firewallu. dla dynamicznych będzie to 666. Jest to trywialne: edytujemy plik /etc/ssh/sshd_config i dodajemy doń linijkę:
Port 666
oczywiście pozostawiając istniejącą linijkę z portem 22 :-)

Kolejny krok to instalacja serwera knockd, co jest równie trywialne w Ubuntu:
~# apt-get install knockd
W innych systemach może nie być tego pakietu, wtedy trzeba się posiłkować pakietem ze strony projektu lub kompilacją ze źródeł. Teraz edytujemy plik konfiguracyjny /etc/knockd.conf. Przyjmijmy, że chcemy sekwencją zapukań na porty 1666, 8081, 6667 otworzyć dostęp do portu 666, a sekwencją odwrotną go zamknąć.
[options]
UseSyslog

[openSSH]
sequence    = 1666,8081,6667
seq_timeout = 5
command     = /sbin/iptables -I INPUT 6 -s %IP% -p tcp --dport 666 -j ACCEPT
tcpflags    = syn

[closeSSH]
sequence    = 6667,8081,1666
seq_timeout = 5
command     = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 666 -j ACCEPT
tcpflags    = syn
Po czym edytujemy /etc/default/knockd, w którym dajemy START_KNOCKD=1 i już możemy startować usługę.
~# service knockd start

Pukamy do serwera

Pora przesiąść się do domowego Linuksa i zapukać w nasz tajemy sposób :-)
~$ knock example.com 1666 8081 6667
I już możemy cieszyć się z dostępu do SSH na porcie 666. Gdy pracę skończymy, warto port zamknąć, pukając odwrotnie
~$ knock example.com 6667 8081 1666
Proste jak puknięcie w stół :-)
Dla innych systemów operacyjnych (w tym Windowsa, MacOSa czy iPhona) klient pukający do pobrania ze strony http://www.zeroflux.org/projects/knock.

Oczywiście możemy wykorzystać mechanizm puknięć także w dowolnym programie bez instalacji dodatkowych pakietów. Wystarczy tylko otworzyć nieblokujące sockety na poszczególne porty z sekwencji. Przykładowy program w Pythonie, który to robi mógłby wyglądać tak:
import os, socket, fcntl

addr = 'example.com'
seq = [1666,8081,6667]

for port in seq:
    print('Knocking %s' % port)
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    flags = fcntl.fcntl(s, fcntl.F_GETFL, 0)
    fcntl.fcntl(s, fcntl.F_SETFL, flags | os.O_NONBLOCK)
    try:
        s.connect((addr,port))
    except:
        pass
    s.close()

Bardziej zaawansowane konfiguracje knockd

Powyższy przykład był zupełnie podstawową konfiguracją serwera knockd i w praktyce raczej tak nie skonfigurujemy dostępu do serwera. Poważnym brakiem jest choćby konieczność samodzielnego zapukania aby zamknąć wcześniej otwarty dostęp.
Można użyć np. takiej konfiguracji:
[opencloseSSH]
sequence      = 1590:tcp,7201:tcp,9999:udp
seq_timeout   = 15
tcpflags      = syn
start_command = /sbin/iptables -I INPUT 6 -s %IP% -p tcp --syn --dport 666 -j ACCEPT
cmd_timeout   = 10
stop_command  = /sbin/iptables -D INPUT -s %IP% -p tcp --syn --dport 666 -j ACCEPT
Po pierwsze - sekwencja zapukań pozwala ściśle definiować protokół (tcp lub udp), po drugie - możliwe jest określenie dwóch komend, jakie mają być wykonane. Komendę startową i komendę kończącą, która zostanie wykonana po np. 10 sekundach. Czyli po zapukaniu mamy 10 sekund na połączenie się SSH z serwerem, po czym knockd automatycznie zamknie możliwość nawiązywania nowych połączeń i przed kolejną próbą trzeba będzie ponownie zapukać.


Ciekawym (acz możliwe że trudnym do wykorzystania w praktyce) jest też mechanizm sekwencji jednorazowych.

Szczegółowy opis wszystkich opcji i parametrów na stronie projektu: http://www.zeroflux.org/projects/knock

1 komentarz:

  1. Optymalnym rozwiązaniem jest zamknięcie połączenia portu w dwóch przypadkach:

    1. Gdy tak jak w przykładzie przez np. 10s nie zostanie wywołana odpowiednia sekwencja puknięć.

    2. 10s po wykonaniu tej sekwencji (10s w zupełności wystarczy na połączenie się klientem SSH), Firewalla można skonfigurować tak, żeby zezwalał na kontynuowanie wcześniej nawiązanego połączenia, mimo iż w danym momencie port jest już zablokowany. W rezultacie łączymy się na port 666, FW blokuje go - nikt inny nie ma do niego dostępu - jednocześnie FW umożliwia nam normalną pracę w ramach nawiązanego połączenia.

    OdpowiedzUsuń