UFW i IPv6 w VPS jak zabezpieczyć ruch
Brak reguł IPv6 w UFW lub firewallu chmurowym otwiera porty na świat. Sprawdź, dlaczego usługi nasłuchują na publicznym IPv6 mimo zabezpieczeń IPv4.
Pułapka IPv6 w firewallu w jednym zdaniu
Firewall chroni ruch IPv4. Serwer VPS niemal na pewno posiada również publiczny adres IPv6, a wiele usług domyślnie nasłuchuje na tym adresie. Jeśli firewall obejmuje tylko IPv4 lub jeśli korzystasz z firewalli chmurowych filtrujących wyłącznie IPv4, każda z tych usług pozostaje dostępna z całego internetu przez IPv6, mimo że strona IPv4 wydaje się zabezpieczona. Testowanie portu za pomocą curl zwraca błąd połączenia, co daje fałszywe poczucie bezpieczeństwa. Atakujący może połączyć się z tym samym portem przez IPv6 i uzyskać dostęp.
Niniejszy poradnik wyjaśnia przyczyny tego stanu rzeczy na standardowym VPS z systemem Ubuntu 24.04, wskazuje metody weryfikacji wystawionych usług oraz sposoby na ich zabezpieczenie. UFW nie jest przyczyną problemu. W nowoczesnych instalacjach Ubuntu UFW obsługuje już IPv6. Luka wynika z warstw zewnętrznych oraz z usług, które nasłuchują bez wiedzy administratora.
Dlaczego VPS posiada adres IPv6
Prawie każdy współczesny VPS posiada publiczny adres IPv6, często w postaci /64, obok adresu IPv4. Należy sprawdzić własny adres:
ip -6 addr show scope global2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
inet6 2001:db8:2a::1/64 scope globalWspomniany 2001:db8:2a::1 jest routowalny z dowolnego miejsca w sieci Internet, analogicznie do adresu IPv4. Następnie należy sprawdzić aktywne usługi:
sudo ss -tlnpState 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-proxyNależy dokładnie przeanalizować kolumnę Local Address. 0.0.0.0:22 oznacza „nasłuchiwanie na każdym adresie IPv4”. [::]:22 oznacza „nasłuchiwanie na każdym adresie IPv6”. 127.0.0.1:5432 jest przypisany do loopback i nie jest adresem publicznym, zatem linia dotycząca Postgres jest bezpieczna. Dwie linie [::] odpowiadają na żądania z całego Internetu przez IPv6, natomiast linia docker-proxy to typ usługi, o której uruchomieniu często się zapomina.
Większość demonów domyślnie wiąże się z ::, ponieważ w systemie Linux gniazdo :: zazwyczaj obsługuje również IPv4. Domyślnym ustawieniem nowego serwera jest zatem „odpowiadanie na obu stosach, wszędzie”. Jedyną barierą jest firewall; dlatego firewall obsługujący tylko jeden stos stanowi istotny problem.
Skąd faktycznie bierze się luka w IPv6
Występują cztery powszechne przyczyny. Na danym serwerze może wystąpić jedna lub kilka z nich jednocześnie.
1. Firewall w chmurze filtrujący tylko IPv4. Wiele firewalli dostawców i produktów typu security-group powstało z myślą o IPv4, przez co ignorują one IPv6 lub wymagają ręcznego dodania oddzielnych reguł IPv6. Jeśli jedynym firewallem jest ten w panelu dostawcy i nie obejmuje on IPv6, usługi [::] są otwarte, niezależnie od ustawień portu 22 dla IPv4. Należy sprawdzić dokumentację firewalli dostawcy pod kątem konkretnie terminu IPv6.
2. Ręcznie skonfigurowane iptables bez ip6tables. Komenda iptables modyfikuje wyłącznie tablice IPv4. IPv6 posiada całkowicie oddzielną komendę, ip6tables, z własnymi regułami. Jeśli skrypt firewalli zawiera linie iptables -A INPUT ..., a nie utworzono odpowiadających im reguł ip6tables, firewall IPv6 pozostaje pusty. Pusta łańcuch INPUT z domyślną polityką ACCEPT zezwala na wszystkie połączenia:
sudo ip6tables -L INPUT -nChain INPUT (policy ACCEPT)
target prot opt source destinationPowyższy wynik obrazuje problem: IPv4 jest filtrowane, natomiast IPv6 akceptuje wszystkie połączenia.
3. Publikowanie portów przez Docker z pominięciem firewalla. Podczas uruchamiania docker run -p 8080:80, Docker wstawia własne reguły przed regułami UFW. Oznacza to, że opublikowany port jest dostępny, nawet jeśli ufw status blokuje ten port. W nowoczesnych wersjach Docker mechanizm ten dotyczy również IPv6. Dlaczego Docker omija UFW i jak poprawnie filtrować porty kontenerów wyjaśnia ten mechanizm oraz metody naprawy. Informacje o deklarowaniu opublikowanych portów znajdują się w podstawach Docker Compose na VPS.
4. UFW z wyłączonym IPv6. UFW obsługuje IPv6, ale tylko po otrzymaniu stosowcego polecenia. Należy sprawdzić ustawienie:
grep IPV6 /etc/default/ufwNowoczesne wersje Ubuntu dostarczają IPV6=yes, więc UFW stosuje każdą regułę do obu stosów. Jeśli w starszych obrazach lub poradnikach widnieje IPV6=no, każda napisana reguła UFW dotyczy wyłącznie IPv4, a IPv6 pozostaje niezarządzane.
Dokładna weryfikacja zakresu ekspozycji
Nie należy polegać na przypuszczeniach. Należy dokonać pomiaru z zewnątrz. W pierwszej kolejności należy wymienić wszystkie nasłuchujące procesy (listeners) i odnotować te przypisane do :::
sudo ss -tlnp | grep '::'Następnie, z innej maszyny, należy połączyć się z publicznym adresem IPv6 serwera i spróbować nawiązać połączenie na porcie, który jest uznawany za zamknięty:
curl -6 -v http://[2001:db8:2a::1]:8080/Jeśli operacja zwróci stronę lub banner, port jest otwarty w protokole IPv6. Zamknięty port zwraca błąd Connection refused lub przekroczenie czasu oczekiwania (timeout). Aby uzyskać pełny obraz sytuacji, należy wykonać skanowanie adresu IPv6 za pomocą narzędzia nmap z maszyny zewnętrznej:
nmap -6 2001:db8:2a::1Każdy port raportowany przez nmap jako otwarty w IPv6 jest dostępny dla całego internetu, niezależnie od wyników skanowania IPv4. Porównanie skanowań IPv4 i IPv6 pozwala najszybciej wykryć luki: każdy port otwarty na -6, który jest zamknięty w IPv4, stanowi usługę pominiętą przez firewall.
Zamknij luki
Skonfiguruj UFW dla obu stosów i ustaw domyślną odmowę. Po zatwierdzeniu zmian ustaw domyślną politykę deny dla ruchu przychodzącego i zezwalaj tylko na niezbędne połączenia:
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 verboseJeśli UFW było aktywne w momencie zmiany IPV6=yes, zmiany zostaną wprowadzone dopiero po uruchomieniu sudo ufw reload.
ufw status zawiera każdą regułę dwukrotnie: raz w wersji standardowej, a raz z przyrostkiem (v6). Linie zawierające (v6) oznaczają, że UFW filtruje ruch IPv6:
22/tcp ALLOW IN Anywhere
22/tcp (v6) ALLOW IN Anywhere (v6)W przypadku ręcznego zarządzania iptables, należy odzwierciedlić każdą regułę w ip6tables, lub przejść na nftables. Tabele inet w nftables obsługują IPv4 i IPv6 w jednym miejscu, co eliminuje ten rodzaj błędów. Pojedyncza tabela filtrów nftables inet jest najskuteczniejszym rozwiązaniem przy samodzielnym tworzeniu reguł.
Przypisz usługi niewymagające dostępu publicznego do interfejsu loopback. Bazy danych, panele administracyjne lub punkty końcowe metryk rzadko wymagają publicznego adresu. Przypisz je do 127.0.0.1 oraz ::1, aby nie nasłuchiwały na adresach routowalnych. W przypadku Postgres należy ustawić listen_addresses = 'localhost'. W przypadku serwera aplikacji należy przypisać go do 127.0.0.1 i umieścić przed nim reverse proxy. Zamknięcie nasłuchiwania jest skuteczniejsze niż filtrowanie przez firewall, ponieważ eliminuje możliwość nawiązania połączenia.
Nie polegaj na UFW w zakresie ochrony portów publikowanych przez Docker. Publikuj porty kontenerów na konkretnym adresie zamiast na wszystkich interfejsach, na przykład -p 127.0.0.1:8080:80. Dzięki temu port będzie dostępny tylko z hosta oraz z usług, które celowo przekierujesz do kontenera. Gdy kontener musi być publiczny, umieść go za reverse proxy Traefik i publikuj tylko proxy, a nie każdą aplikację z osobna.
Dodaj reguły IPv6 w firewallu dostawcy, lub zaakceptuj, że nie jest to Twój firewall dla IPv6, i pozwól na to zadanie UFW lub nftables działającym na hoście.
Verify you are actually closed
Ponownie przeprowadź ten sam test zewnętrzny po wprowadzeniu zmian:
curl -6 -v http://[2001:db8:2a::1]:8080/
nmap -6 2001:db8:2a::1Port, który wcześniej odpowiadał, powinien teraz odrzucać połączenie lub wywoływać przekroczenie czasu oczekiwania (timeout). Narzędzie nmap powinno zgłosić stan filtered lub closed. Jeśli port nadal pozostaje otwarty, sprawdź ponownie cztery powyższe źródła: usługa nadal przypisana do :: bez reguły blokującej, reguła Docker działająca przed UFW lub firewall dostawcy, który nie obsługuje IPv6.
Całkowite odcięcie wrażliwych usług od publicznego internetu zapewnia większe bezpieczeństwo. Umieść SSH i panele administracyjne za WireGuard VPN i zablokuj ich porty w firewallu, aby odpowiadały wyłącznie w tunelu; wówczas kwestia ekspozycji IPv6 przestaje ich dotyczyć. Aby spowolnić skanowanie brute-force wymierzone w publiczne usługi, należy zainstalować Fail2ban przed SSH na firewallu ustawionym na tryb default-deny.
Jeśli pojęcia portów są nowe, należy najpierw przeczytać artykuł czym są porty i jak usługi nasłuchują.
FAQ
Czy UFW domyślnie blokuje IPv6?
W nowoczesnych instalacjach Ubuntu 24.04 tak. UFW odczytuje IPV6=yes z /etc/default/ufw i stosuje każdą regułę zarówno dla IPv4, jak i IPv6, a ufw status wyświetla reguły IPv6 z przyrostkiem (v6). Problem występuje, gdy używa się IPV6=no (ze starego obrazu lub starego poradnika), gdy polega się na firewallu dostawcy obsługującym tylko IPv4, lub gdy Docker publikuje port poza UFW. Sprawdź ustawienie za pomocą grep IPV6 /etc/default/ufw.
Jak sprawdzić, co mój VPS udostępnia przez IPv6?
Uruchom sudo ss -tlnp i odnotuj każdy proces nasłuchujący, którego adres lokalny zaczyna się od [::], co oznacza nasłuchiwanie na każdym interfejsie IPv6. Następnie, z innego urządzenia, przetestuj publiczny adres IPv6 serwera bezpośrednio za pomocą curl -6 -v http://[YOUR:IPV6::ADDR]:PORT/ lub przeskanuj go za pomocą nmap -6 YOUR:IPV6::ADDR. Każdy port otwarty w skanowaniu IPv6, a zamknięty w IPv4, stanowi lukę w zabezpieczeniach.
Dlaczego mogę uzyskać dostęp do portu kontenera Docker, mimo że UFW zgłasza blokadę?
Docker wstawia własne reguły firewall przed regułami UFW podczas publikowania portu za pomocą -p, przez co opublikowany port jest dostępny, mimo że ufw status wymienia go jako zablokowany. Dzieje się tak w IPv4 oraz w IPv6, gdy włączona jest obsługa IPv6 w Dockerze. Publikuj port na konkretnym adresie, np. -p 127.0.0.1:8080:80, lub umieść kontener za reverse proxy i publikuj tylko proxy.
Czy nadal potrzebuję firewalla IPv6, jeśli mój firewall IPv4 jest poprawnie skonfigurowany?
Tak. IPv4 i IPv6 to oddzielne stosy sieciowe z oddzielnymi regułami firewall. Kompletny zestaw reguł IPv4 nie ma wpływu na ruch IPv6. Jeśli VPS posiada publiczny adres IPv6 (a posiada prawie każdy), to każda usługa nasłuchująca na :: pozostaje dostępna przez IPv6, dopóki nie zablokuje jej reguła firewall IPv6 lub wiązanie loopback.
Jak sprawić, aby usługa nasłuchiwała tylko na IPv4 lub tylko na localhost?
Ustaw adres wiązania (bind address) usługi w jej własnej konfiguracji. Użyj 127.0.0.1 dla samego pętli IPv4 (loopback) lub 0.0.0.0 dla wszystkich adresów IPv4 bez nasłuchiwania IPv6. Postgres używa listen_addresses, SSH używa ListenAddress, a większość serwerów aplikacji posiada flagę host lub bind. Potwierdź wynik za pomocą sudo ss -tlnp i sprawdź, czy Local Address nie wyświetla już [::].