Aby zabezpieczyć serwer przed większością ataków wystarczy wdrożyć kilka prostych rozwiązań. Co jednak, jeśli ktoś się na naszą maszynę uweźmie i będzie dysponował dużą ilością czasu i pomysłów? Można mu bardziej utrudnić zadanie.
We współpracy z firmą ArubaCloud pokazaliśmy Wam, jak postawić swój serwer backupów, jak skonfigurować własny serwer VPN i podłączyć do niego komputer, telefony komórkowe oraz domowy ruter a także jak schować się przed cenzurą sieci i jak zacząć serwer zabezpieczać, jak postawić swój własny zdalny pulpit i swój serwer WWW oraz jak monitorować dowolnie wybrane pliki lub katalogi. Dzisiaj pokażemy pewną rzadziej spotykaną metodę zabezpieczenia serwera przed wyjątkowo natrętnymi atakującymi.
Po co mi to
Kilka miesięcy temu pokazaliśmy Wam, jak serwer zabezpieczyć – logować się po SSH za pomocą klucza, skonfigurować proste banowanie natrętnych intruzów i aktywować automatyczne aktualizacje. Wiemy jednak, że sporo z Was lubi podnosić poziom swoich zabezpieczeń dużo powyżej przeciętnego, dlatego dzisiaj pokażemy, jak serwer zabezpieczyć tak, by praktycznie nikt nie mógł się do niego połączyć. Nikt – oprócz Was.
Konfigurację pokażemy na przykładzie serwera w Aruba Cloud, sponsora naszych poradników. Jeśli nie macie jeszcze swojej własnej maszyny, to jest to dobra okazja by się w taką wyposażyć. Aruba oferuje dwa miesiące korzystania ze swojego podstawowego serwera gratis – a po zakończeniu promocji będzie on Was kosztował zaledwie 4 złote miesięcznie. Instrukcję jak krok po kroku skorzystać z promocji i uruchomić swój serwer znajdziecie w tym artykule. Jeśli macie już swój serwer – to zapraszamy do lektury kolejnych akapitów.
Pokażemy Wam dzisiaj, jak serwer skonfigurować tak, by nie odpowiadał na połączenia na żadnym porcie – ale byście mogli się do niego połączyć gdy tego zechcecie. Oczywiście można to zrealizować za pomocą reguł firewalla, ograniczających zakres źródłowych adresów IP. Kiedy jednak nie wiecie, z jakiego IP będzie się łączyć, z pomocą przychodzi tzw. port knocking, czyli pukanie do portów. Serwer będzie miał zamknięty port SSH dopóki nie wykonacie określonej w konfiguracji sekwencji połączeń. Gdy Wasz komputer połączy się z odpowiednimi portami we właściwej kolejności za pomocą odpowiednich pakietów, serwer otworzy dla Was SSH. Gdy skończycie pracę, będziecie mogli ten port w podobny sposób zamknąć. Jak takie cudo skonfigurować?
Konfigurujemy serwer
Zainstalujemy usługę knockd która będzie odpowiedzialna za otwieranie portu ssh (tcp/22) po uzyskaniu odpowiedniej sekwencji.
Na początku instalujemy wymagane pakiety do skompilowania usługi knockd ze źródeł:
$ sudo yum install git libpcap-devel.x86_64 autoconf automake
Następnie musimy pozyskać źródło usługi knockd. Usługa ta jest dostępna w serwisie GitHub pod tym adresem. Pozyskujemy źródła knockd za pomocą polecenia git:
git clone https://github.com/jvinet/knock
oraz kompilujemy w następujący sposób:
$ cd knock $ autoreconf -fi $ ./configure --prefix=/usr/local/ $ make $ sudo make install
Po wydaniu polecenia make install
plik binarny knockd
został skopiowany do katalogu /usr/local/sbin/knockd, natomiast przykładowy plik konfiguracyjny knockd.conf został skopiowany do katalogu /usr/local/etc/.
Edytujemy plik /usr/local/etc/knockd.conf według naszych potrzeb:
[options] logfile = /var/log/knockd.log [openSSH] sequence = 4444,8989,6500 seq_timeout = 5 tcpflags = syn Start_command = /sbin/iptables -I INPUT -s %IP% -p tcp --dport 22 -j ACCEPT [closeSSH] sequence = 4445,8990,6501 seq_timeout = 5 tcpflags = syn command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
W powyższej konfiguracji w sekcji [options] ustawiamy plik logowania – /var/log/knockd.log – w tym pliku będą zapisane wszelkie informacje o zdarzeniach. Następnie w sekcji [openSSH] ustawiamy sekwencję na portach 4444,8989, 6500. Seq_timeout określa maksymalny czas na uzyskanie pełnej sekwencji połączeń do wskazanych przez nas portów – w naszym przykładzie jest to 5 sekund. Tcpflags określa które pakiety mają być uwzględniane – w naszym przypadku są to pakiety tcp z ustawioną flagą SYN.
Jeśli sekwencja będzie poprawna nastąpi uruchomienie polecenia zdefiniowanego w start_command. W opisywanym przykładzie wykonujemy polecenie iptables
które dodaje regułę zezwalającą na nawiązanie połączenia z adresu ip, który wykonał poprawną sekwencję do naszego serwera na port 22 (ssh).
Bardzo podobną regułę zastosowano w dyrektywie [closeSSH] w której określono połączenia na porty 4445, 8990, 6501. Wysłanie pakietów tcp z flagą SYN w czasie nie dłuższym niż 5 sekund spowoduje wywołanie komendy iptables
w której zdefiniowano zablokowanie dostępu do portu 22 dla adresu ip, który wykonał poprawną sekwencje połączeń.
Plik startowy
Tworzymy i edytujemy plik /etc/systemd/system/knockd.service:
[Unit] Description=knockd service After=network.target [Service] ExecStart=/usr/local/sbin/knockd -d -c /usr/local/etc/knockd.conf Type=forking PIDFile=/var/run/knockd.pid [Install] WantedBy=multi-user.target
Aby zastosować zmiany w systemie wydajemy polecenie:
sudo systemctl daemon-reload
i uruchamiamy usługę knockd:
sudo service knockd start
Jeśli chcemy aby usługa była uruchamiana podczas rozruchu systemu, wydajemy polecenie:
sudo systemctl enable knockd
Wysyłamy sekwencję
Serwer otworzy nam port 22/tcp jeśli wykonamy sekwencję prób połączeń na porty określone w konfiguracji: 4444, 8989, 6500.
Sekwencję możemy wykonać za pomocą:
nmap -p 4444,8989,6500 adres-ip-naszego-serwera
lub za pomocą klienta, który został utworzony podczas kompilowania usługi knockd:
knock adres-ip-naszego-serwera 4444:tcp 8989:tcp 6500:tcp
Po wykonaniu powyższych sekwencji usługa knockd powinna otworzyć port 22/tcp, a w logu (/var/log/knockd.log) powinno pojawić się następujące zdarzenie:
[2017-03-07 11:00] nasz-ip: openSSH: Stage 1 [2017-03-07 11:00] nasz-ip: openSSH: Stage 2 [2017-03-07 11:00] nasz-ip: openSSH: Stage 3 [2017-03-07 11:00] nasz-ip: openSSH: OPEN SESAME [2017-03-07 11:00] openSSH: running command: /sbin/iptables -I INPUT -s nasz-ip -p tcp --dport 22 -j ACCEPT
Przedstawiliśmy przykładową konfigurację która umożliwia otwarcie portu 22/tcp dla usługi ssh po wykonaniu sekwencji połączeń do naszego serwera. Usługę można dostosować do swoich potrzeb jak chociażby ustawienie jakie pakiety tcp powinny być uwzględniane do wykrycia sekwencji – do wyboru mamy pakiety z flagą fin, syn, rst, psh,ack,urg.
Gdy już jesteśmy pewni, że wszystko działa jak należy, możemy zablokować dostęp do ssh wydając polecenie:
iptables -A INPUT -p tcp --dport 22 -j DROP
Dostęp do 22/tcp (ssh) będzie otwierany adresom IP, które wykonały poprawną sekwencję połączeń. Poniżej znajdziecie powyższą instrukcję w wersji wideo:
Wady i zalety
Zalety port knockingu:
- zabezpiecza przed atakami brute force w ramach protokołu np. SSH,
- zabezpiecza przed atakami na sam protokół,
- nawet prosta sekwencja (jak opisana powyżej) wymaga dużego nakładu sił do przełamania,
- może być jedną z warstw obrony (oprócz uwierzytelnienia loginem, hasłem czy kluczem),
- otwiera port tylko dla konkretnego adresu IP, który może być zmienny (lub dla wielu, jeśli jest wielu użytkowników znających sekwencję).
Wady port knockingu:
- w razie awarii demona nasłuchującego połączeń może odciąć od serwera (wtedy przydaje się procedura awaryjna opisana w tym artykule),
- jeśli ktoś uzyska w inny sposób dostęp do serwera (np. przez dostęp fizyczny), to może w logach zaobserwować prawidłową sekwencję,
- sekwencja może zostać podsłuchana w ataku MiTM.
Jak w przypadku każdego mechanizmu obronnego to do Was należy decyzja czy warto go zastosować.
Dla pełnej przejrzystości – za przygotowanie oraz opublikowanie powyższego artykułu otrzymujemy wynagrodzenie.
Komentarze
Mam pytanie, czy da się ustawić, aby serwer jeśli po odblokowaniu portu nie wykryje ruchu na porcie, to po jakimś czasie go blokował? Gdybym przypadkiem zapomniał zablokować port po połączeniu? Pozdrawiam
Musiało by to być kilka sekund. Ruch na porcie 22 jest co chwila, bez przerwy różnego typu ataki jak sprawdzenie najczęstszych (często domyślnych) danych logowania
Zmiana portu na jakiś inny + fail2ban z długim czasem banowania adresów ip i po kłopocie.
Oczywiście przy założeniu, że możemy się logować przez hasła, bo jak damy ssh tylko po kluczach to bruteforce nam raczej nie grozi.
+ klucze (tylko ed25519) + PSAD
W konfigu knockd mozna ustawic czas po, którym odpalana jest automatycznie druga komenda np. zamykajaca dostep do portu ssh na fw po pierwszej komendzie.
Dodatkowo jesli ktos nie loguje sie z miejsc o bardzo zmiennym ip mozna napisac skrypt, ktory bedzie otwieral port ssh dla adresu ip, z ktorego byla wyslana poprawna sekwencja knockd.
Mozna tez ustawic na iptables aby utrzymywal nawiazane polaczenia i zamykac port do ssh po jakims czasie domyslnie.
Knockd znam od lat, jest swietny.
Bardzo prosto, przesyłam przykładowy config z knockd.conf:
[SSHD]
sequence = x,x,x,x
seq_timeout = 6
tcpflags = x,x,x
start_command = /sbin/iptables -I INPUT xxxxxx -m state –state NEW -j ACCEPT
cmd_timeout = 60
stop_command = /sbin/iptables -D INPUT xxxxxx -m state –state NEW -j ACCEPT
po 60 sekundach od chwili otwarcia portu SSH port się zamyka na nowe połączenia. Stare połączenia nadal działają, bo jest iptables -A INPUT xxxxx -m state –state ESTABLISHED,RELATED -j ACCEPT
Można zrobić skrypt który skanuje log knockda i wyławia ostatnią udaną sekwencję zwracają adres ip z jakiej pochodzi sekwencja.
Potem wystarczy zmodyfikować komendę ip tables aby otwierała port tylko na dany ip.
Wtedy uzyskujemy:
– otwarcie portu na 60 s
– dla danego ip
– automatycznie zamykamy
– utrzymujemy połączenie, które nawiązało się wcześniej
Mógłbyś podać jakiś pełny przykład z małym wyjaśnieniem ? :)
Jutro coś naskrobię więcej na temat :D
Spoko, z wyjątkiem instalowania softu przez „make install”, co powoduje, ze pliki nie są objęte systemem pakietów.
Tutaj masz, trzeba tylko paczkę instalacyjną stworzyć: http://www.invoca.ch/pub/packages/knock/RPMS/ils-7/SRPMS/
Dostępne są nieoficjalne pakiety knockd na różnych, nieoficjalnych repozytoriach. Instalację nieoficjalnych pakietów w systemie pozostawiam do własnego rozważenia.
Poco masz instalować z nie oficjalnych jak można z oficjalnych, które podałem wyżej!
Fajną opcją są tokeny od google połączone z ssh (tak wiem, google jest złe, ale te pliki są udostępnione gdzieś na gicie).
Właśnie takie coś mi chodzi od dłuższego czasu po głowie, ale w zestawieniu z port-knockingiem.
Token od Google’a to 6 cyfr. Myślę że spokojnie dałoby się chociażby przez zwykłe przypisanie określonego portu do każdej cyfry uzyskać knock-pattern zmienny w czasie i bardzo odporny na ataki MitM
Może przesada ale jak mawiają: „to że mam klinicznie stwierdzoną paranoję jeszcze nie oznacza że nikt mnie nie śledzi” :)
Ja używam prostego skryptu nasłuchującego na porcie 8888/UDP:
#!/usr/bin/env python
import socket
import sys
import os
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((„”,8888))
while True:
d,a = s.recvfrom(1024)
if d[:11] == „haslo_otworz_port”:
os.system(„iptables -R ssh 1 -p tcp –dport 22 ! -s”+ \
str(a[0])+” -j DROP”)
s.sendto(„ssh port open\n”, a)
elif d[:13] == „haslo_zamknij_port”:
os.system(„iptables -R ssh 1 -p tcp –dport 22 -j DROP”)
s.sendto(„ssh port closed\n”, a)
#end_file
Wystarczy że wyśle hasło za pomocą netcata:
echo „haslo_otworz_port” | nc -u 8888
Mam jeszcze dodane w iptables filtrowanie pakietów ICMP/port_unreachable żeby nie można było namierzyć otwartego portu UDP.
Niedawo mój domowy ISP zmnienił zewnętrzne IP (jestem za NAT-em) więc mogłem się połączyć z ssh dopiero jak ponownie wysłałem hasło na port 8888.
No tak ale tutaj wystarczy podsłuchać jeden pakiet aby zrobić replay z innego miejsca.
Dodatkowo knockd ma log aby sprawdzić czy wszystko jest ok a tu :D ?
Masz racje hasło jest jawne ale wysyłane jest raz na ruski rok i nie służy do autoryzacji tylko do otwacia portu.
Z drugiej strony gdzie jest większe ryzyko istnienia potencjanych dziur czy w 10 linijkowym pythonowym skrypcie czy w prawie 2000 lini kodu C ?
To jest pytanie retoryczne ;)
Ostrożnie! Sama koncepcja knockd jest fajna, ale czyż nie okaże się, że nie będziemy się mogli połączyć z naszym serwerem gdy będziemy to robić z sieci, która wycina ruch na egzotyczne porty? Trzeba szukać serwera na zewnątrz sieci w której jesteśmy i z niego dopiero odpukać sekwencję. Odradzam również dodawania do sekwencji standardowych portów często używanych usług, bo są one skanowane tak często, że istnieje niezerowa szansa, że przypadkiem ktoś nam będzie port SSH włączać i wyłączać.
Skoro vps w aruba to tylko 4zł i ma stałe IP, to jakimś rozwiązaniem może być logowanie się po ssh, tylko z tego serwera.
tylko jak sie zalogowac do tego vps? przez kosole www? to jeszcze bardziej upierdliwe niz pukanie
Fajne dajecie te art, nie ma co:) Własnie myslalem, co tu zrobic, zeby zabezpieczyc serwer :)
IMHO co sekwencji portów, mozna by to zrobic w funkcji czasu/daty, i wtedy na podstawie znanej zmiennej (czas z NTP) wyliczac sekwencje portów.
A jak z pod Windy automatycznie pukać gdy łączymy się putty ?
w przegladarce otwieramy kolejne strony:
http://serwer:port1
http://serwer:port2
http://serwer:port3
A po co uzywac knockd? To samo mozna uzyskac w czystym iptables (wlacznie z czasami blokowania)
Podasz przykład?
A takie pytanie do przemyślenia – czy jest możliwe ustalenie przybliżonych zakresów IP, które częściej są wykorzystywane do malware / ransomware i zablokować ruch do nich?
Po części sam sobie odpowiadam – https://ransomwaretracker.abuse.ch/blocklist/
I jeszcze jeden przykład ciekawego zabezpieczenia:
Automatically Ban Hosts That Attempt to Access Invalid Services
ipset also provides a „target extension” to iptables that provides a mechanism for dynamically adding and removing set entries based on any iptables rule. Instead of having to add entries manually with the ipset command, you can have iptables add them for you on the fly.
For example, if a remote host tries to connect to port 25, but you aren’t running an SMTP server, it probably is up to no good. To deny that host the opportunity to try anything else proactively, use the following rules:
ipset -N banned_hosts iphash
iptables -A INPUT \
-p tcp –dport 25 \
-j SET –add-set banned_hosts src
iptables -A INPUT \
-m set –set banned_hosts src \
-j DROP
If a packet arrives on port 25, say with source address 1.1.1.1, it instantly is added to banned_hosts, just as if this command were run:
ipset -A banned_hosts 1.1.1.1
All traffic from 1.1.1.1 is blocked from that moment forward because of the DROP rule.
Note that this also will ban hosts that try to run a port scan unless they somehow know to avoid port 25.
za: http://www.linuxjournal.com/content/advanced-firewall-configurations-ipset?page=0,2
Ja mam tak zabezpieczone
https://www.cyberciti.biz/tips/linux-unix-bsd-openssh-server-best-practices.html#comment-629576
Pozmieniałem tylko lekko czasy, bo 5 sekund to czasami za mało.
Zapomniałem co to boty w ssh.
Wydaje mi się, że nmap nie zadziała, bo skanuje porty losowo.
Można użyć przełącznika -r, aby skanował po kolei, ale wtedy leci numerami portów od najniższego, a więc zamiast 4444,8989,6500 będzie 4444,8989,6500.
Ew. próbować z losową kolejnością do skutku