SSD Nodes Learn Hosting plans →
Przewodniki Matt ConnorAutor: Matt Connor

UFW i IPv6: otwarte porty na VPS

Firewall może chronić tylko IPv4, choć usługi nasłuchują też przez IPv6. Sprawdź ekspozycję na Ubuntu 24.04 VPS i zamknij niechronione porty.

Pułapka firewalla IPv6 w jednym zdaniu

Twój firewall chroni protokół IPv4. Twój VPS niemal na pewno posiada również publiczny adres IPv6, a wiele usług nasłuchuje na nim domyślnie. Jeśli Twój firewall obsługuje tylko IPv4 lub polegasz na chmurowym firewallu filtrującym wyłącznie ruch IPv4, każda z tych usług jest dostępna z całego Internetu przez IPv6, podczas gdy strona IPv4 wydaje się zabezpieczona. Testujesz port za pomocą curl, widzisz odmowę połączenia i czujesz się bezpiecznie. Atakujący łączy się z tym samym portem przez IPv6 i uzyskuje dostęp.

Ten przewodnik pokazuje, skąd bierze się ta luka w standardowym systemie Ubuntu 24.04 VPS, jak dokładnie sprawdzić, co jest wystawione na zewnątrz, oraz jak to zabezpieczyć. UFW nie jest tutaj winowajcą. W nowoczesnej instalacji Ubuntu narzędzie UFW obsługuje już IPv6. Ekspozycja wynika z warstw otaczających firewall oraz z usług, o których nasłuchiwaniu nie wiesz.

Dlaczego Twój VPS posiada adres IPv6

Prawie każdy współczesny serwer VPS jest dostarczany z publicznym adresem IPv6, często z całą podsiecią /64, obok adresu IPv4. Sprawdź swój adres:

ip -6 addr show scope global
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
    inet6 2001:db8:2a::1/64 scope global

Wartość 2001:db8:2a::1 w tym przewodniku jest symbolem zastępczym z puli 2001:db8::/32, czyli prefiksu zarezerwowanego dla dokumentacji, który nie jest trasowany w Internecie. Rzeczywisty adres wyświetlony przez serwer w tej linii jest osiągalny z dowolnego miejsca w Internecie, dokładnie tak samo jak adres IPv4. Teraz sprawdź, co nasłuchuje na portach:

sudo ss -tlnp
State   Recv-Q  Local Address:Port   Process
LISTEN  0       0.0.0.0:22           sshd
LISTEN  0       [::]:22              sshd
LISTEN  0       127.0.0.1:5432       postgres
LISTEN  0       [::]:8080            docker-proxy

Przyjrzyj się uważnie kolumnie Local Address. 0.0.0.0:22 oznacza „nasłuchuj na każdym adresie IPv4”. [::]:22 oznacza „nasłuchuj na każdym adresie IPv6”. 127.0.0.1:5432 jest powiązane z interfejsem zwrotnym (loopback) i nie jest publicznie dostępne, więc linia Postgres jest bezpieczna. Dwie linie [::] odpowiadają całemu Internetowi przez IPv6, a linia docker-proxy to usługa, o której uruchomieniu mogłeś zapomnieć.

Większość demonów domyślnie wiąże się z ::, ponieważ w systemie Linux gniazdo :: zazwyczaj akceptuje również połączenia IPv4. Zatem domyślna konfiguracja świeżego serwera to „odpowiadaj na obu stosach, wszędzie”. Zapora sieciowa jest jedynym zabezpieczeniem przed tym stanem, dlatego zapora obsługująca tylko jeden stos jest poważnym problemem.

Skąd faktycznie bierze się luka w IPv6

Istnieją trzy powszechne źródła tego problemu. Na danym serwerze może występować jedno z nich lub kilka jednocześnie.

1. Chmurowy firewall filtrujący tylko IPv4. Wiele firewalli dostawców i produktów typu security-group powstało w oparciu o IPv4 i albo ignorują IPv6, albo wymagają ręcznego dodania osobnych reguł dla IPv6. Jeśli jedynym firewallem jest ten w panelu dostawcy i nie obejmuje on IPv6, Twoje usługi [::] są otwarte, niezależnie od tego, co panel wskazuje w kwestii portu 22 dla IPv4. Przeczytaj dokumentację firewalla swojego dostawcy i wyszukaj w niej konkretnie słowa IPv6.

2. Ręcznie tworzone reguły iptables bez ip6tables. Polecenie iptables modyfikuje wyłącznie tablice IPv4. IPv6 posiada całkowicie oddzielne polecenie, ip6tables, z własnym, niezależnym zestawem reguł. Jeśli skrypt firewalla zawiera wiele linii iptables -A INPUT ..., a nie zawiera odpowiadających im reguł ip6tables, firewall IPv6 pozostaje pusty. Pusty łańcuch INPUT z domyślną polityką ACCEPT przepuszcza cały ruch:

sudo ip6tables -L INPUT -n
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Ten wynik to pułapka w pigułce. Ruch IPv4 jest filtrowany, a IPv6 akceptuje wszystko.

3. Docker publikujący porty z pominięciem firewalla. Podczas uruchamiania docker run -p 8080:80, Docker wstawia własne reguły przed regułami UFW. Dzięki temu opublikowany port jest osiągalny, nawet gdy ufw status wskazuje, że port jest zablokowany. Obecne wersje Docker domyślnie zarządzają regułami ip6tables, więc gdy IPv6 jest włączone w sieci kontenera, to samo dotyczy ruchu IPv6. Dlaczego Docker omija UFW i jak poprawnie filtrować porty kontenerów wyjaśnia ten mechanizm oraz sposoby naprawy. Zobacz podstawy Docker Compose na VPS, aby dowiedzieć się, jak deklarowane są te opublikowane porty.

Uwaga na przełącznik IPv6 w UFW. UFW obsługuje IPv6, a odpowiedni przełącznik znajduje się w jednym pliku. Sprawdź go:

grep IPV6 /etc/default/ufw

Nowoczesne systemy Ubuntu dostarczane są z ustawieniem IPV6=yes, dzięki czemu UFW stosuje każdą regułę do obu stosów protokołów. Jeśli widzisz IPV6=no, co może wynikać ze starego obrazu lub przestarzałego poradnika, IPv6 nie jest pozostawione otwarte. Przy takim ustawieniu aktywny UFW odrzuca cały ruch IPv6 poza interfejsem loopback, więc problem występuje w drugą stronę: wszystko, co miało być udostępniane przez IPv6, przestaje odpowiadać. Ustaw wartość na yes, jeśli chcesz, aby UFW filtrował IPv6 zgodnie z Twoimi regułami, zamiast całkowicie go blokować.

Weryfikacja wystawionych usług

Nie należy zgadywać. Należy dokonać pomiaru z zewnątrz. Najpierw należy wyświetlić listę procesów nasłuchujących i zanotować każdy z nich powiązany z :::

sudo ss -tlnp | grep '::'

Następnie, z innej maszyny, należy połączyć się z publicznym adresem IPv6 serwera i sprawdzić port, który powinien być zamknięty:

curl -6 -v http://[2001:db8:2a::1]:8080/

Jeśli zwrócona zostanie strona lub baner, port jest otwarty w protokole IPv6. Zamknięty port zwraca Connection refused lub przekroczenie czasu oczekiwania. Te dwa błędy nie oznaczają tego samego, a różnica między połączeniem odrzuconym a przekroczeniem czasu informuje, czy host odpowiedział i odmówił dostępu, czy też firewall odrzucił pakiet w sposób niezauważalny. Aby uzyskać pełny obraz, należy przeskanować adres IPv6 za pomocą nmap z maszyny zewnętrznej:

nmap -6 2001:db8:2a::1

Każdy port, który nmap wskazuje jako otwarty w IPv6, jest portem dostępnym z całego Internetu, niezależnie od wyników skanowania IPv4. Porównanie skanów IPv4 oraz IPv6 obok siebie to najszybszy sposób na wykrycie luki: każda usługa otwarta na -6, a zamknięta na IPv4, jest usługą, której firewall nie zabezpiecza.

Zamknięcie luki

Skonfiguruj UFW dla obu stosów i ustaw domyślną politykę odmowy. Potwierdź przełączenie, ustaw domyślną politykę odmowy dla ruchu przychodzącego i zezwól tylko na niezbędny ruch:

sudo sed -i 's/^IPV6=no/IPV6=yes/' /etc/default/ufw
sudo ufw default deny incoming
sudo ufw allow 22/tcp
sudo ufw enable
sudo ufw status verbose

Jeśli UFW był aktywny w momencie zmiany IPV6=yes, zmiana nie wejdzie w życie do czasu wykonania sudo ufw reload.

ufw status wyświetla każdą regułę dwukrotnie: raz w wersji podstawowej, a raz z przyrostkiem (v6). Obecność linii (v6) oznacza, że UFW filtruje ruch IPv6:

22/tcp                     ALLOW IN    Anywhere
22/tcp (v6)                ALLOW IN    Anywhere (v6)

Jeśli zarządzasz iptables ręcznie, odzwierciedlaj każdą regułę w ip6tables lub przejdź na nftables, którego tablice inet obsługują IPv4 i IPv6 w jednym miejscu, eliminując ten rodzaj błędów. Pojedyncza tablica filtrów inet w nftables jest najczystszym rozwiązaniem przy samodzielnym tworzeniu reguł. Jeśli Twój VPS korzysta z Rocky lub AlmaLinux zamiast Ubuntu, nie ma tam UFW do skonfigurowania, a firewalld jest interfejsem, którym zarządzasz, który stosuje reguły strefowe do obu stosów jednocześnie.

Powiąż usługi, które nie powinny być publiczne, z interfejsem loopback. Baza danych, panel administracyjny czy punkt końcowy metryk rzadko wymagają publicznego adresu. Powiąż je z 127.0.0.1 oraz ::1, aby usługa w ogóle nie nasłuchiwała na adresie routowalnym. W przypadku Postgres ustaw listen_addresses = 'localhost'. W przypadku serwera aplikacji powiąż go z 127.0.0.1 i umieść przed nim reverse proxy. Zamknięcie portu nasłuchującego jest skuteczniejsze niż jego blokowanie firewallem, ponieważ w ten sposób usługa staje się nieosiągalna.

Nie ufaj UFW w kwestii ochrony portów udostępnionych przez Docker. Publikuj porty kontenerów na konkretnym adresie zamiast na wszystkich interfejsach, na przykład -p 127.0.0.1:8080:80, aby port był dostępny tylko z poziomu hosta oraz usług, które celowo przekierowujesz przez proxy. Gdy kontener musi być publicznie dostępny, umieść go za reverse proxy Traefik i publikuj tylko proxy, a nie każdą aplikację z osobna.

Dodaj reguły IPv6 do firewalla dostawcy lub zaakceptuj fakt, że nie jest on Twoim firewallem dla IPv6 i pozwól, aby to zadanie przejął UFW lub nftables na hoście.

Weryfikacja zamknięcia portów

Po wprowadzeniu zmian należy ponownie wykonać test zewnętrzny:

curl -6 -v http://[2001:db8:2a::1]:8080/
nmap -6 2001:db8:2a::1

Port, który wcześniej odpowiadał, powinien teraz odrzucić połączenie lub przekroczyć limit czasu, a nmap powinien zgłosić stan filtered lub closed. Jeśli port nadal pozostaje otwarty, należy ponownie przeanalizować trzy powyższe źródła: usługę nadal powiązaną z :: bez odpowiedniej reguły, regułę Docker znajdującą się przed UFW lub zaporę sieciową dostawcy, która nie obsługuje IPv6.

Całkowite odizolowanie wrażliwych usług od publicznego Internetu zapewnia jeszcze wyższy poziom bezpieczeństwa. Umieszczenie SSH oraz paneli administracyjnych za VPN WireGuard i ograniczenie dostępu do ich portów wyłącznie do interfejsu tunelu sprawia, że kwestia ekspozycji w IPv6 przestaje mieć znaczenie. Aby spowolnić ataki typu brute-force wymierzone w usługi publiczne, należy zastosować Fail2ban przed SSH jako warstwę ochronną działającą w połączeniu z domyślną polityką odmowy zapory sieciowej.

Jeśli zagadnienie portów jest nowe, czym są porty i jak usługi nasłuchują stanowi wprowadzenie, od którego należy zacząć lekturę.

FAQ

Czy UFW domyślnie blokuje IPv6?

W nowoczesnej instalacji Ubuntu 24.04, tak. UFW odczytuje IPV6=yes z pliku /etc/default/ufw i stosuje każdą regułę zarówno do IPv4, jak i IPv6, a polecenie ufw status wyświetla reguły IPv6 z przyrostkiem (v6). Pułapka pojawia się, gdy polegasz na firewallu dostawcy, który filtruje tylko IPv4, gdy ręcznie napisane reguły iptables nie mają odpowiedników w ip6tables lub gdy Docker udostępnia port z pominięciem UFW. Polecenie IPV6=no (pochodzące ze starego obrazu lub poradnika) nie otwiera IPv6: przy aktywnym UFW cały ruch IPv6 jest odrzucany, z wyjątkiem interfejsu loopback. Sprawdź ustawienie za pomocą grep IPV6 /etc/default/ufw.

Jak sprawdzić, co mój VPS udostępnia przez IPv6?

Uruchom sudo ss -tlnp i zanotuj każdy proces nasłuchujący, którego adres lokalny zaczyna się od [::], co oznacza, że odpowiada on na każdym interfejsie IPv6. Następnie, z innej maszyny, przetestuj publiczny adres IPv6 serwera bezpośrednio za pomocą curl -6 -v http://[YOUR:IPV6::ADDR]:PORT/ lub przeskanuj go narzędziem nmap -6 YOUR:IPV6::ADDR. Każdy port otwarty w skanie IPv6, a zamknięty w IPv4, stanowi lukę w zabezpieczeniach.

Dlaczego mogę połączyć się z portem kontenera Docker, mimo że UFW twierdzi, iż jest zablokowany?

Docker wstawia własne reguły firewalla przed regułami UFW, gdy publikujesz port za pomocą -p, więc opublikowany port jest dostępny, mimo że ufw status oznacza go jako zablokowany. Dzieje się tak w przypadku IPv4, a także IPv6, gdy obsługa IPv6 w Dockerze jest włączona. Publikuj porty na konkretny adres, taki jak -p 127.0.0.1:8080:80, lub umieść kontener za reverse proxy i publikuj tylko to proxy.

Czy nadal potrzebuję firewalla IPv6, jeśli mój firewall IPv4 jest solidny?

Tak. IPv4 i IPv6 to oddzielne stosy sieciowe z oddzielnymi regułami firewalla. Idealny zestaw reguł IPv4 nie wpływa na ruch IPv6. Jeśli Twój VPS posiada publiczny adres IPv6, a posiada go niemal każdy, to każda usługa nasłuchująca na :: pozostaje dostępna przez IPv6, dopóki nie zablokuje jej reguła firewalla IPv6 lub powiązanie z interfejsem loopback.

Jak sprawić, by usługa nasłuchiwała tylko na IPv4 lub tylko na localhost?

Ustaw adres powiązania usługi w jej własnej konfiguracji. Powiąż usługę z 127.0.0.1, aby ograniczyć ją tylko do IPv4 loopback, lub z 0.0.0.0 dla wszystkich adresów IPv4 bez nasłuchiwania na IPv6. Postgres używa listen_addresses, SSH używa ListenAddress, a większość serwerów aplikacji udostępnia flagę hosta lub bind. Potwierdź wynik za pomocą sudo ss -tlnp i sprawdź, czy Local Address nie wyświetla już [::].