Powtórzono zestawy adresów IP
Odpowiedź już wspomina o zestawach adresów IP. Jest jednak raczej jednowymiarowy, ponieważ koncentruje się na wzroście wydajności w porównaniu z klasycznymi regułami oraz na tym, że zestawy IP łagodzą problem z wieloma indywidualnymi adresami IP, których nie można łatwo wyrazić jako podsieć w notacji CIDR.
Notacja używana poniżej
Dla ipset
będę używać notacji odczytywane przez ipset restore
scenariusz ipset save
.
Odpowiednio do iptables
(i ip6tables
) reguł użyję notacji odczytanej iptables-restore
i napisanej przez iptables-save
. To sprawia, że notacja jest krótsza i pozwala mi wyróżnić potencjalne reguły tylko dla IPv4 (z prefiksem -4
) lub tylko z IPv6 (z prefiksem -6
).
W niektórych przykładach przekierujemy przepływ pakietów na inny łańcuch. Zakłada się, że łańcuch istnieje w tym momencie, więc linie do tworzenia łańcuchów nie są tworzone (nie wymieniono też nazwy tabeli ani poleceń COMMIT
na końcu).
Zaawansowane zestawy adresów IP
Zestawy IP mogą zrobić znacznie więcej niż zostało wspomniane w drugiej odpowiedzi i zdecydowanie powinieneś przeczytać dokumentację zestawu IP ( ipset(8)
) oraz iptables-extensions(8)
oprócz tego krótkiego wpisu tutaj.
Na przykład skupię się głównie na trzech ustawionych typów: hash:ip
, hash:net
i list:set
, ale jest więcej niż tych, a wszystkie one mają ważne przypadki użycia.
Możesz na przykład dopasować również numery portów, a nie tylko adresy IP .
Zapisywanie i przywracanie zestawów adresów IP jak przy pomocy iptables-save
iiptables-restore
Można tworzyć zbiorcze deklaracje zestawu adresów IP i importować je, przesyłając je do pipety ipset restore
. Jeśli chcesz, aby twoje polecenie było bardziej odporne na już istniejące wpisy, użyj ipset -exist restore
.
Jeśli Twoje reguły znajdują się w pliku o nazwie, default.set
możesz użyć:
ipset -exist restore < default.set
Taki plik może zawierać wpisy do create
zestawów i add
wpisów do nich. Ale generalnie większość poleceń z wiersza poleceń wydaje się mieć odpowiednią wersję w plikach. Przykład (tworzenie zestawu serwerów DNS):
create dns4 hash:ip family inet
create dns6 hash:ip family inet6
# Google DNS servers
add dns4 8.8.8.8
add dns4 8.8.4.4
add dns6 2001:4860:4860::8888
add dns6 2001:4860:4860::8844
Tutaj tworzony jest jeden zestaw dla IPv4 ( dns4
), a drugi dla IPv6 ( dns6
).
Przekroczono limity czasu w zestawach adresów IP
Limity czasu w zestawach IP można ustawić domyślnie dla każdego zestawu, a także dla jednego wpisu. Jest to bardzo przydatne w scenariuszach, w których chcesz tymczasowo zablokować kogoś (np. Podczas skanowania portów lub próby brutalnego użycia serwera SSH).
Sposób działania jest następujący (domyślny podczas tworzenia zestawów adresów IP):
create ssh_loggedon4 hash:ip family inet timeout 5400
create ssh_loggedon6 hash:ip family inet6 timeout 5400
create ssh_dynblock4 hash:ip family inet timeout 1800
create ssh_dynblock6 hash:ip family inet6 timeout 1800
Wrócimy do tych konkretnych zestawów poniżej i uzasadnienia, dlaczego są ustawione takimi, jakimi są.
Jeśli chcesz ustawić limit czasu dla określonego adresu IP, możesz po prostu powiedzieć:
add ssh_dynblock4 1.2.3.4 timeout 7200
Aby zablokować IP 1.2.3.4 na dwie godziny zamiast (ustawionego) domyślnego pół godziny.
Jeśli spojrzysz na to ipset save ssh_dynblock4
po chwili, zobaczysz coś w rodzaju:
create ssh_dynblock4 hash:ip family inet hashsize 1024 maxelem 65536 timeout 1800
add ssh_dynblock4 1.2.3.4 timeout 6954
Ograniczenia dotyczące limitu czasu
- limity czasu są funkcją w danym zestawie. Jeśli zestaw nie został utworzony z obsługą limitu czasu, pojawi się błąd (np
Kernel error received: Unknown error -1
.).
- limity czasu są podawane w sekundach. Używaj wyrażeń arytmetycznych Bash, na przykład, aby uzyskać minuty od sekund. Na przykład:
sudo ipset add ssh_dynblock4 1.2.3.4 timeout $((120*60))
Sprawdzanie, czy istnieje wpis w danym zestawie adresów IP
Wewnątrz skryptów przydatne może być sprawdzenie, czy pozycja już istnieje. Można to osiągnąć, przy pomocy ipset test
którego zwracane jest zero, jeśli pozycja istnieje, a w przeciwnym razie niezerowa. Tak więc zwykłe kontrole można zastosować w skrypcie:
if ipset test dns4 8.8.8.8; then
echo "Google DNS is in the set"
fi
Jednak w wielu przypadkach wolisz użyć -exist
przełącznika, aby ipset
nakazać mu nie narzekać na istniejące wpisy.
Wypełnianie zestawów adresów IP z iptables
reguł
To, moim zdaniem, jest jedną z zabójczych cech zestawów IP. Nie tylko możesz dopasować do wpisów zestawu IP, ale możesz także dodawać nowe wpisy do istniejącego zestawu IP.
Na przykład w tej odpowiedzi na to pytanie masz:
-A INPUT -p tcp -i eth0 -m state --state NEW --dport 22 -m recent --update --seconds 15 -j DROP
-A INPUT -p tcp -i eth0 -m state --state NEW --dport 22 -m recent --set -j ACCEPT
... z zamiarem ograniczenia prób połączenia z SSH (port TCP 22). Używany moduł recent
śledzi ostatnie próby połączenia. Zamiast state
modułu wolę jednak conntrack
moduł.
# Say on your input chain of the filter table you have
-A INPUT -i eth+ -p tcp --dport ssh -j SSH
# Then inside the SSH chain you can
# 1. create an entry in the recent list on new connections
-A SSH -m conntrack --ctstate NEW -m recent --set --name tarpit
# 2. check whether 3 connection attempts were made within 2 minutes
# and if so add or update an entry in the ssh_dynblock4 IP set
-4 -A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock4 src --exist
-6 -A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock6 src --exist
# 3. last but not least reject the packets if the source IP is in our
# IP set
-4 -A SSH -m set --match-set ssh_dynblock4 src -j REJECT
-6 -A SSH -m set --match-set ssh_dynblock6 src -j REJECT
W tym przypadku przekierowuję przepływ do SSH
łańcucha, tak że nie muszę powtarzać się -p tcp --dport ssh
dla każdej reguły.
Powtarzać:
-m set
sprawia, że iptables
zdaje sobie sprawę, że jesteśmy za pomocą przełączników z set
modułu (który obsługuje zestawy IP)
--match-set ssh_dynblock4 src
mówi, iptables
aby dopasować adres source ( src
) do nazwanego set ( ssh_dynblock4
)
- odpowiada to
sudo ipset test ssh_dynblock4 $IP
(gdzie $IP
zawiera źródłowy adres IP pakietu)
-j SET --add-set ssh_dynblock4 src --exist
dodaje lub aktualizuje adres source ( src
) z pakietu do zestawu IP ssh_dynblock4
. Jeśli pozycja istnieje ( --exist
), zostanie po prostu zaktualizowana.
- odpowiada to
sudo ipset -exist add ssh_dynblock4 $IP
(gdzie $IP
zawiera źródłowy adres IP pakietu)
Jeśli zamiast tego chcesz dopasować adres docelowy / docelowy, użyj dst
zamiast niego src
. Więcej informacji znajdziesz w instrukcji obsługi.
Zestawy zestawów
Zestawy IP mogą zawierać inne zestawy. Teraz, jeśli śledziłeś ten artykuł do tej pory, zastanawiasz się, czy można łączyć zestawy. I oczywiście tak jest. Dla zestawów IP z góry możemy utworzyć dwa zestawy wspólne ssh_dynblock
i ssh_loggedon
odpowiednio zawierać tylko zestawy IPv4 i tylko IPv6:
create ssh_loggedon4 hash:ip family inet timeout 5400
create ssh_loggedon6 hash:ip family inet6 timeout 5400
create ssh_dynblock4 hash:ip family inet timeout 1800
create ssh_dynblock6 hash:ip family inet6 timeout 1800
# Sets of sets
create ssh_loggedon list:set
create ssh_dynblock list:set
# Populate the sets of sets
add ssh_loggedon ssh_loggedon4
add ssh_loggedon ssh_loggedon6
add ssh_dynblock ssh_dynblock4
add ssh_dynblock ssh_dynblock6
Kolejne pytanie, które powinno pojawić się w twojej głowie, brzmi: czy pozwala nam to dopasowywać i manipulować zestawami adresów IP w sposób niezależny od wersji IP.
Odpowiedź na to brzmi: TAK! (niestety, nie było to wyraźnie udokumentowane podczas ostatniej kontroli)
W związku z tym można przepisać zasady z poprzedniej sekcji, aby przeczytać:
-A INPUT -i eth+ -p tcp --dport ssh -j SSH
-A SSH -m conntrack --ctstate NEW -m recent --set --name tarpit
-A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock src --exist
-A SSH -m set --match-set ssh_dynblock src -j REJECT
co jest o wiele bardziej zwięzłe. I tak, jest to wypróbowane i przetestowane i działa jak urok.
Wszystko razem: obrona SSH przed brutalną siłą
Na moich serwerach mam skrypt uruchamiany jako cron
zadanie, które pobiera kilka nazw hostów i przetwarza je na adresy IP, a następnie podaje je do zestawu adresów IP dla „zaufanych hostów”. Chodzi o to, że zaufani gospodarze podejmują więcej prób zalogowania się na serwerze i niekoniecznie są blokowani tak długo, jak inni.
I odwrotnie, mam zablokowane całe kraje z łączeniem się z moim serwerem SSH, z (potencjalnym) wyjątkiem zaufanych hostów (tj. Kolejność reguł ma znaczenie).
Pozostaje to jednak ćwiczeniu dla czytelnika. W tym miejscu chciałbym dodać porządne rozwiązanie, które wykorzysta zestawy zawarte w ssh_loggedon
zestawie, aby umożliwić przekazywanie kolejnych prób połączenia i nie podlegać takiej samej kontroli, jak inne pakiety.
Ważne jest, aby pamiętać o domyślnych limitach czasu 90 minut ssh_loggedon
i 30 minut dla ssh_dynblock
następujących iptables
zasad:
-A INPUT -i eth+ -p tcp --dport ssh -j SSH
-A SSH -m set --match-set ssh_loggedon src -j ACCEPT
-A SSH -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A SSH -m conntrack --ctstate NEW -m recent --set --name tarpit
-A SSH -m conntrack --ctstate NEW -m recent --rcheck --seconds 120 --hitcount 3 --name tarpit -j SET --add-set ssh_dynblock src --exist
-A SSH -m set --match-set ssh_dynblock src -j REJECT
Do tej pory powinieneś zadać sobie pytanie, w jaki sposób łączący adres IP kończy się w ssh_loggedon
podzestawach. Więc czytaj dalej ...
Bonus: dodanie adresu IP, z którego logujesz się podczas logowania SSH
Jeśli eksperymentowałeś z sshrc
przyjaciółmi i dowiesz się o ich wadach. Ale PAM przybywa na ratunek. Moduł o nazwie pam_exec.so
pozwala nam wywoływać skrypt podczas logowania SSH w punkcie, w którym wiemy, że użytkownik jest dopuszczony.
W /etc/pam.d/sshd
poniżej pam_env
i pam_selinux
wpisy dodać następujący wiersz:
session optional pam_exec.so stdout /path/to/your/script
i upewnij się, że twoja wersja skryptu ( /path/to/your/script
powyżej) istnieje i jest wykonywalna.
PAM używa zmiennych środowiskowych do komunikowania o tym, co się dzieje, dzięki czemu możesz użyć prostego skryptu takiego jak ten:
#!/bin/bash
# When called via pam_exec.so ...
SETNAME=ssh_loggedon
if [[ "$PAM_TYPE" == "open_session" ]] && [[ -n "$PAM_RHOST" ]]; then
[[ "x$PAM_RHOST" != "x${PAM_RHOST//:/}" ]] && SETNAME="${SETNAME}6" || SETNAME="${SETNAME}4"
ipset -exist add $SETNAME "$PAM_RHOST"
fi
Niestety ipset
narzędzie nie ma wbudowanych inteligentnych funkcji netfiltera. Musimy więc odróżnić IPv4 od IPv6 IP set podczas dodawania naszego wpisu. W przeciwnym razie ipset
założymy, że chcemy dodać kolejny zestaw do zestawu, zamiast adresu IP. I oczywiście jest mało prawdopodobne, aby istniał zestaw nazwany na podstawie adresu IP :)
Sprawdzamy więc :
adres IP i dołączamy 6
do nazwy zestawu w takim przypadku i w 4
innym przypadku.
Koniec.