Jeśli masz jakikolwiek serwer podłączony do Internetu, na pewno zdajesz sobie sprawę, że niezależnie od tego, jak mały lub nieistotny się wydaje, jest on często sondowany, testowany lub poddawany różnym próbom nadużycia. Ataki te pochodzą od wielu złośliwych hostów, co sprawia, że niemożliwe jest ich ręczne śledzenie. Dlatego zacząłem szukać sposobu na wprowadzenie zautomatyzowanej blokady do użycia z iptables i firewalld, które stosuję na swoich serwerach.
ipset
Istnieją dobre rozwiązania do wykrywania i blokowania hostów atakujących twój system Linux, takie jak denyhosts lub fail2ban , i gorąco polecam zastosowanie jednego z nich, w zależności od twoich potrzeb. Jednak jeśli hosty są znane w społeczności bezpieczeństwa jako złośliwe, znacznie elegancko jest przechwycić je na wstępie, przed połączeniem się z twoimi usługami. Tutaj przychodzi blokada znanych złośliwych hostów.
Oczywiście nie byłem pierwszy, który zastanawiał się nad wykorzystaniem blokad z iptables. Wiele komercyjnych rozwiązań z zakresu zapór sieciowych regularnie dystrybuuje aktualizacje, dodając sygnatury wykrywania i informacje o blokadach. Ktoś musiał jednak zrobić coś podobnego z iptables i firewalld, pomyślałem. I rzeczywiście, kilka chwil spędzonych na poszukiwaniach online ujawniło wiele pytań i kilka dobrych odpowiedzi. Metoda, która szczególnie mi się podobała, to wykorzystanie ipset do zarządzania dużymi listami adresów IP wewnątrz jądra, eliminując konieczność tworzenia tysięcy reguł iptables lub firewalld. Brzmiało to dokładnie tak, jak chciałem. Teraz potrzebowałem tylko dobrej listy blokowanych hostów.
Tworzenie listy blokowanych hostów
Trochę więcej poszukiwań doprowadziło mnie na stronę beris.nl, która zawierała zarówno skrypt powłoki do tworzenia obszernej listy blokowanych hostów, jak i sposób jej przekazywania do ipset, dzięki czemu iptables lub firewalld mogły używać tej listy. Odtwarzam ten skrypt poniżej w całości, na wypadek, gdyby oryginalny zniknął. Nie biorę na siebie żadnej odpowiedzialności za napisanie tego skryptu, który został nieco zmodyfikowany przeze mnie, tylko dlatego, że lista wizcrafts nie działa już. Zdecydowałem się użyć listy badips.com.
Zainstalowałem ipset. Następnie utworzyłem katalog w /opt o nazwie blocklist i utworzyłem plik o nazwie blocklist.sh, do którego wkleiłem zawartość poniższego skryptu.
|
|
Wciśnij klawisz “Insert” w edytorze Vi, następnie skopiuj poniższy skrypt i wklej go prawym przyciskiem myszy do edytora Vi. Następnie naciśnij klawisz “Esc” na klawiaturze, wpisz :x i naciśnij Enter.
|
|
Wyjaśnienie skryptu Bash
|
|
Zapisuje deskryptory plików, aby można je było przywrócić do ich stanu sprzed przekierowania lub używać ich do wyjścia do tego, do czego były przekierowane przed następnym przekierowaniem.
|
|
Przywraca deskryptory plików dla określonych sygnałów. Zazwyczaj nie jest to konieczne, ponieważ powinny być one przywracane po zakończeniu podpowłoki.
|
|
Przekierowuje stdout do pliku log.out, a następnie przekierowuje stderr do stdout. Zwróć uwagę, że kolejność jest istotna, gdy chcesz, aby oba wyprowadzały dane do tego samego pliku. stdout musi być przekierowany przed przekierowaniem stderr do stdout.
|
|
Jeśli ma to zakończyć działanie w przypadku błędu.
Teraz możesz umieścić skrypt w katalogu /opt/blocklist i uruchomić go w tle.
|
|
Jeśli chcesz śledzić postęp, po prostu wpisz:
|
|
Aby przerwać, użyj ctrl+c.
Aby sprawdzić, czy zadanie jest uruchomione, wpisz
|
|
lub
|
|
Aby zobaczyć, czy działa to w tle.
W zasadzie to, co robi ten skrypt, to pobiera listy blokowanych adresów IP i adresów IP z różnych witryn hostingowych takie listy oraz lokalny plik txt shodan.io (możesz przygotować więcej niż jedną lokalną bazę złych adresów IP), usuwa wszystko, co nie jest blokiem IP ani adresem, a następnie umieszcza wszystkie te linie w jednym pliku tekstowym. Powoduje to uzyskanie pliku zawierającego tysiące linii (obecnie ponad 100 000 adresów IP). Byłoby to niemożliwe do zarządzania ręcznie. Jeśli ustawisz ten skrypt do uruchamiania raz dziennie z crontab, będziesz miał dość aktualną listę złośliwych hostów. Proszę powstrzymać się od uruchamiania tego skryptu zbyt często, ponieważ witryny, na których hostowane są różne listy źródłowe, muszą płacić za ten ruch. Zbyt częsta aktualizacja prawdopodobnie spowoduje zablokowanie dostępu. Na samym dole, zestaw ipset jest opróżniany, a nowa lista jest dodawana linia po linii do listy blokowanych.
Znalazłem w internecie jedną witrynę, która zawiera listy baz danych IP i domen. Proszę pamiętać, że niektóre z tych witryn nie są aktualizowane lub zostały wyłączone, zhakowane, usunięte itp. Jednak nadal duża część z nich działa, i jest to dobre źródło, od którego możemy zacząć: http://www.covert.io/threat-intelligence/ aby dodać więcej zasobów do tego skryptu, co jest dosyć proste, lub przygotować ręcznie własne bazy danych w formacie txt, tak jak zrobiłem to w przypadku shodan.io.
Adresy IP z Shodan.io
|
|
Proszę zauważyć, że instrukcje, które następują, są napisane dla CentOS 7, ale powinny być odpowiednie dla innych dystrybucji, ponieważ używamy tylko wiersza poleceń.
Dodawanie skryptu do crontab.
Edytuj crontab za pomocą tego polecenia:
|
|
Dodaj tę linię:
|
|
Skrypt uruchamia się każdego niedzielnego północu. Dodałem funkcję losowej godziny (3600 sekund), co zapobiega sytuacji, gdy wszyscy próbują pobierać listy IP z serwerów o tej samej porze. Zalecam zmianę godzin w tym zadaniu. Bądź ostrożny, bo mogą cię zbanować, jeśli będziesz próbował pobierać IP zbyt często.
Doskonałe przykłady cronjobów znajdziesz tutaj: https://crontab.guru/
Podłączanie listy blokowanej do firewalld
Uruchomienie tego skryptu z wiersza poleceń nie powiedzie się w tej chwili. Chociaż utworzy plik listy blokowanej w /etc/ip-blocklist.conf, nie będzie w stanie załadować ipset, ponieważ jeszcze nie utworzyliśmy ipset o nazwie “blocklist”. Możemy utworzyć go ręcznie w następujący sposób:
|
|
To polecenie tworzy ipset o nazwie “blocklist” o typie “hash:net”. Ten typ ipset służy do przechowywania adresów IP o różnych rozmiarach, począwszy od dużych bloków sieciowych aż do pojedynczych hostów. Uruchomienie powyższego skryptu teraz utworzy listę blokowaną i wypełni ipset utworzoną listą blokowaną. Następnie musimy dodać regułę do firewalld, aby używał listy blokowanej. Zalecam wstawienie tej reguły na początku lub blisko początku łańcucha INPUT, aby była przetwarzana wcześnie w twoim zestawie reguł. Spójrzmy teraz na łańcuch INPUT, aby wiedzieć, gdzie wstawić nową regułę. Rozmiar hasha i wartość maxelem to potęga liczby 2.
|
|
Powyższa komenda wyświetla wszystkie obecne reguły w łańcuchu INPUT iptables, z numerami linii, co ułatwia wstawienie nowej reguły. Fragment mojego łańcucha INPUT można zobaczyć powyżej. Pierwsza reguła akceptuje cały ruch, druga reguła akceptuje cały ruch na interfejsie pętli zwrotnej, i zostawimy je na górze. Szósta reguła odrzuca wszystkie przychodzące pakiety w nieprawidłowym stanie, co jest dobrze. Reguła poniżej tego loguje liczbę odrzuconych przychodzących połączeń. Ponieważ jest to już dość konkretne, wstawmy naszą nową regułę powyżej tej.
|
|
To polecenie wstawia naszą regułę na pozycji 7 w łańcuchu INPUT i dopasowuje przychodzący ruch do zbioru o nazwie „blocklist”, odrzucając odpowiedni ruch. W tym momencie iptables cicho odrzuca cały ruch pochodzący od hostów i bloków sieciowych znajdujących się na liście blokowanej. Jednak jeśli chcemy zobaczyć, co jest blokowane, musimy dodać regułę logowania powyżej reguły 7. Ponieważ jestem ciekawy, co jest blokowane, dodałem następującą regułę:
|
|
Ta reguła jest wstawiana na pozycji 7, przesuwając regułę odrzucania na pozycję 8. Teraz każdy przychodzący ruch odpowiadający naszej liście blokowanej jest najpierw logowany z prefiksem „IP Blocked:” przez regułę 7, a następnie odrzucany przez regułę 8. W logach możemy zobaczyć coś takiego:
|
|
Tutaj widzimy kilka prób połączenia się z serwerem przez hosty o różnych adresach IP. Te połączenia nig
dy nie zostaną nawiązane, ponieważ iptables jest gotowy do pracy.
Ponieważ reguły, które napisaliśmy, wydają się działać, musimy je zapisać, aby zostały ponownie załadowane, jeśli iptables zostanie ponownie uruchomiony. W CentOS 7 możemy użyć do tego celu iptables. Wydanie poniższej komendy zapisze reguły w /etc/sysconfig/iptables.
|
|
Tworzenie ipset przy uruchamianiu systemu
Do tej pory udało nam się pobrać i skompilować obszerną listę blokowanych adresów, nauczyliśmy się, jak wczytać ją do ipset i podłączyć to ipset do iptables lub firewalld jako listę blokowaną. Ustawiliśmy również regułę do logowania prób połączenia z naszej listy blokowanej. Jak dotąd wszystko jest w porządku. Jednak istnieje jeszcze jeden problem. W momencie ponownego uruchomienia serwera, zauważysz, że zapora nie może zainicjować zapisanych przez iptables/firewalld reguł, ponieważ odwołuje się do ipset, który nie istnieje. Będziemy potrzebować sposobu na tworzenie ipset podczas uruchamiania systemu. Przeszukałem wiele dokumentacji, ale wydaje się, że jest niewiele dostępnych informacji. Ostatecznie zdecydowałem, że stworzenie skryptu do tworzenia i wypełniania ipset, gdy jedna lub więcej interfejsów Ethernetowych zostaną uruchomione, ma sens. W tym celu stworzyłem skrypt do uruchamiania podczas inicjalizacji systemu sieciowego.
- Utwórz plik o nazwie
ipset.sh
w lokalizacji/usr/local/bin/
:
|
|
- Wstaw następujący skrypt dokładnie w taki sam sposób jak wcześniej:
|
|
Ten skrypt najpierw sprawdza, czy nie istnieje ipset o nazwie “blocklist”, opróżnia i usuwa jakikolwiek istniejący ipset o tej nazwie. Następnie (ponownie) tworzy ipset o nazwie “blocklist” i zapełnia go, używając pliku /etc/ip-blocklist.conf
, który wcześniej utworzyliśmy. Następnie zintegrowałem ten skrypt z nową jednostką usługi systemd, dodając go jako skrypt uruchamiany po konfiguracji interfejsu sieciowego w pliku /etc/systemd/system/ipset_blocklist.service
:
|
|
|
|
- Przeładuj proces systemd, aby uwzględniał nowo utworzoną usługę:
|
|
- Włącz tę usługę, aby uruchamiała się automatycznie po ponownym uruchomieniu:
|
|
- Uruchom usługę:
|
|
W systemie Debian/Ubuntu można zintegrować to nieco inaczej, dodając go jako skrypt uruchamiany po konfiguracji interfejsu sieciowego w pliku /etc/network/interfaces
:
|
|
Kiedy interfejs główny (w wielu przypadkach eth0) jest gotowy, ipset jest tworzony i zapełniany, uruchamiając skrypt /usr/local/bin/ipset.sh
. W momencie inicjalizacji iptables ipset jest dostępny i wypełniony, więc szkodliwe hosty są blokowane niemal natychmiast. Po wykonaniu tych kroków nasza lista blokowanych adresów przetrwa ponowne uruchomienie, zapewniając nam ciągłą ochronę.
firewalld ipset
Skrypt zawiera dodatkową część dla firewalld, która jest wynikiem poszukiwań rozwiązania dla dystrybucji z rodziny Red Hat.
|
|
Pierwsza linia usuwa istniejący ipset. Druga dodaje go z odpowiednim rozmiarem hasha i maksymalną ilością elementów (adresów IP), które można dodać do ipset. Trzecia dodaje dwie reguły do rejestrowania i odrzucania złych adresów IP. Piąta reguła wczytuje złe adresy IP z lokalnego pliku utworzonego przez skrypt. Następnie przeładowuje firewalld, aby zastosować nowe ustawienia ipset. Przedostatnia linia liczy wszystkie adresy IP w ipsecie firewalld, a ostatnia linia liczy adresy IP w ipsecie.
Aby włączyć rejestrowanie odrzuconych połączeń, wykonaj ten polecenie.
|
|
Zmienia to wartość w pliku /etc/firewalld/firewalld.conf
. Możesz sprawdzić to polecenie:
|
|
Ostatecznie zdecydowałem się wyłączyć rejestrowanie złych adresów IP, ponieważ tworzyło to duży bałagan w dzienniku wiadomości. Zmieniłem to w ten sposób.
sudo vi /etc/firewalld/direct.xml
Zawartość tego pliku wygląda następująco:
|
|
Usunąłem tylko pierwszą regułę:
|
|
Do filtrowania używam poleceń takich jak te poniżej:
|
|
Było to lepsze, ale wszystko trafiło do /var/log/messages
. To jest błąd. Postanowiłem przekierować te wiadomości do osobnych dzienników. Oto, jak to zrobiłem.
|
|
Dodałem tam te linie:
|
|
Zapisałem plik i zakończyłem. Następnie postanowiłem stworzyć logrotate dla tych dzienników.
|
|
Dodałem ten zawartość:
|
|
Następnie zrobiłem to samo dla odrzuconych.
|
|
I dodałem:
|
|
Świetnie, to było to, co chciałem, ale nadal wiadomości były nieczytelne, więc postanowiłem edytować rsyslog.conf
i zmienić go zgodnie z rozwiązaniem z tej strony internetowej:
https://serverfault.com/questions/557885/remove-iptables-log-from-kern-log-syslog-messages
|
|
Znajdź część z regułami i zmodyfikuj ją zgodnie z poniższym przykładem.
|
|
Zostaw wszystko p
oniżej bez zmian.
Następnie edytuj poniższy plik:
|
|
I ustaw linię args
w ten sposób:
|
|
Następnie zrestartuj auditd
i rsyslog
za pomocą poniższych poleceń:
|
|
Następnie zdecydowałem się pobrać multitail
, aby móc monitorować je jednocześnie.
|
|
Następnie uruchomiłem to w ten sposób:
|
|
To było fajne, ale nadal nieczytelne, więc postanowiłem poszukać rozwiązania dotyczącego wiadomości jądra. I znalazłem je tutaj: https://superuser.com/questions/351387/how-to-stop-kernel-messages-from-flooding-my-console
Rozwiązanie było proste, i zmieniłem wartości w ten sposób:
|
|
Następnie sprawdziłem te wartości tym poleceniem:
|
|
Konfiguracja parametrów jądra w czasie rzeczywistym - wyjaśnienie
Zobacz man sysctl
- „konfiguracja parametrów jądra w czasie rzeczywistym” aby dowiedzieć się więcej.
Przypomnienie o poziomach ważności i czterech wartościach kernel.printk:
- CUR = bieżący poziom ważności; tylko komunikaty ważniejsze niż ten poziom są wyświetlane
- DEF = domyślny poziom ważności przypisywany do komunikatów bez poziomu
- MIN = minimalny dopuszczalny CUR
- BTDEF = domyślny CUR przy uruchamianiu
Na moim CentOS: 7 4 1 7
|
|
To jest zbyt głośne, chcę tylko krytyczne i wyżej (bez błędów). Komunikaty bez etykiety powinny być traktowane jako ostrzeżenia, więc DEF jest dobry:
|
|
Ustaw na: 3 4 1 3 i problem rozwiązany. Teraz, gdy używam multitail do oglądania logów, widzę wszystko jak należy.
Ostatnią rzeczą, którą musiałem zrobić, było dodanie do białej listy adresów IP Google, ponieważ e-maile z Gmaila były odrzucane, ponieważ powyższe rozwiązanie blokowało adresy IP. Zrobiłem to w ten sposób.
|
|
Powyższe polecenia pozwalają zapisać ich adresy IP w dwóch plikach. Musisz je oczyścić i pozostawić tylko adresy IP (jeden IP na linię). Następnie utworzyłem białą listę i dodałem do niej adresy IP z tych dwóch plików.
Po tym naciśnij ctrl+d, aby się wylogować i wrócić do standardowego użytkownika.
|
|
Wówczas wystarczy tylko ponownie załadować zasady firewalld.
|
|
Jeśli masz jakiekolwiek poprawki lub wskazówki dotyczące powyższego, chętnie je usłyszę. Jeśli ten post w jakikolwiek sposób Ci pomógł, również chciałbym o tym usłyszeć.
Referencje
- https://www.beris.nl/2015/04/22/using-blacklists-with-iptables/
- https://fedoramagazine.org/protect-your-system-with-fail2ban-and-firewalld-blacklists/
- https://www.thegeekdiary.com/centos-rhel-7-how-to-make-custom-script-to-run-automatically-during-boot/
- https://www.howtoforge.com/tutorial/protect-your-server-computer-with-badips-and-fail2ban/
- https://serverfault.com/questions/842749/firewalld-logging-denied-packets-enabled-not-logging
- https://www.cyberciti.biz/faq/enable-firewalld-logging-for-denied-packets-on-linux/
- https://serverfault.com/questions/859572/missed-kernel-messages
- https://serverfault.com/questions/557885/remove-iptables-log-from-kern-log-syslog-messages
- https://superuser.com/questions/351387/how-to-stop-kernel-messages-from-flooding-my-console
- https://wiki.gentoo.org/wiki/Rsyslog