SSD Nodes Learn Hosting plans →
Gidsen Matt ConnorDoor Matt Connor

UFW en IPv6: zo voorkomt u open poorten op uw VPS

UFW ondersteunt IPv6 op Ubuntu 24.04 al. Ontdek waarom uw cloudfirewall of eigen iptables-regels toch alleen IPv4 dekken en hoe u het lek sluit.

De IPv6-firewallval in één zin

Uw firewall beveiligt IPv4. Uw VPS heeft vrijwel zeker ook een publiek IPv6-adres en veel services luisteren hier standaard op. Als uw firewall alleen IPv4 dekt, of als u vertrouwt op een cloud-firewall die alleen IPv4 filtert, is elk van die services via IPv6 bereikbaar vanaf het hele internet, terwijl uw IPv4-kant beveiligd lijkt. U test een poort met curl, ziet een geweigerde verbinding en waant zich veilig. Een aanvaller verbindt met dezelfde poort via IPv6 en krijgt ongehinderd toegang.

Deze handleiding laat zien waar dit gat vandaan komt op een standaard Ubuntu 24.04 VPS, hoe u precies ziet wat u blootstelt en hoe u dit dicht. UFW is hier niet de boosdoener. Op een moderne Ubuntu-installatie verwerkt UFW IPv6 al. De blootstelling ontstaat door de lagen eromheen en door services waarvan u niet wist dat ze luisterden.

Waarom uw VPS überhaupt IPv6 gebruikt

Vrijwel elke VPS wordt tegenwoordig geleverd met een openbaar IPv6-adres, vaak een heel /64, naast het IPv4-adres. Controleer dat van u:

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

De 2001:db8:2a::1 in deze handleiding is een placeholder uit 2001:db8::/32, een prefix die voor documentatie is gereserveerd en nooit op internet wordt gerouteerd. Het werkelijke adres dat uw server op die regel weergeeft, is vanaf elke plek op internet bereikbaar, precies zoals uw IPv4-adres. Bekijk nu wat er luistert:

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

Lees de kolom Local Address zorgvuldig. 0.0.0.0:22 betekent "luisteren op elk IPv4-adres". [::]:22 betekent "luisteren op elk IPv6-adres". 127.0.0.1:5432 is aan loopback gebonden en is helemaal niet openbaar, dus de Postgres-regel is veilig. De twee regels met [::] beantwoorden verbindingen van het hele internet via IPv6. De regel met docker-proxy is het soort service dat u vergeet te hebben gestart.

De meeste daemons binden standaard aan ::, omdat een ::-socket op Linux meestal ook IPv4 accepteert. De standaardconfiguratie van een nieuwe server is daardoor: "op beide stacks en overal antwoorden". Uw firewall is het enige dat daar bescherming tegen biedt. Daarom vormt een firewall die slechts één stack ziet een reëel probleem.

Waar het IPv6-gat werkelijk ontstaat

Er zijn drie veelvoorkomende oorzaken. Op een bepaalde server kan één daarvan of kunnen meerdere daarvan tegelijk aanwezig zijn.

1. Een cloudfirewall die alleen IPv4 filtert. Veel firewalls en security-groupproducten van providers zijn oorspronkelijk voor IPv4 ontwikkeld. Ze negeren IPv6 of vereisen afzonderlijke IPv6-regels die u handmatig moet toevoegen. Als uw enige firewall die in het dashboard van de provider is en deze IPv6 niet ondersteunt, staan uw [::]-services open, ongeacht wat er over poort 22 voor IPv4 wordt vermeld. Lees de documentatie van de firewall van uw provider en zoek specifiek naar het woord IPv6.

2. Handmatig gemaakte iptables-regels zonder ip6tables. De opdracht iptables werkt alleen op de IPv4-tabellen. IPv6 heeft een afzonderlijke opdracht, ip6tables, met een eigen set regels. Als u een firewallscript met veel regels van het type iptables -A INPUT ... hebt geschreven, maar de bijbehorende ip6tables-regels nooit hebt toegevoegd, is uw IPv6-firewall leeg. Een lege INPUT-keten met het standaardbeleid ACCEPT staat al het verkeer toe:

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

Deze uitvoer toont de volledige valkuil op één scherm. IPv4 wordt gefilterd, terwijl IPv6 verkeer van overal accepteert.

3. Docker publiceert poorten rechtstreeks buiten uw firewall om. Wanneer u docker run -p 8080:80 uitvoert, plaatst Docker zijn eigen regels vóór die van UFW. Daardoor is een gepubliceerde poort bereikbaar, ook wanneer ufw status aangeeft dat die poort is geweigerd. Huidige Docker-versies beheren standaard ip6tables-regels. Wanneer IPv6 op het netwerk van de container is ingeschakeld, geldt hetzelfde voor IPv6. Waarom Docker UFW omzeilt en hoe u containerpoorten correct filtert legt het mechanisme en de oplossingen uit. Zie de basis van Docker Compose op een VPS voor informatie over het declareren van deze gepubliceerde poorten.

Een opmerking over de IPv6-schakelaar van UFW. UFW ondersteunt IPv6 en deze instelling staat in één bestand. Controleer de instelling:

grep IPV6 /etc/default/ufw

Moderne Ubuntu-versies leveren IPV6=yes, zodat UFW elke regel op beide protocollen toepast. Als u IPV6=no ziet, bijvoorbeeld door een oude image of een oude handleiding, betekent dit niet dat IPv6 open blijft staan. Met deze instelling blokkeert een actieve UFW al het IPv6-verkeer behalve verkeer via de loopbackinterface. Het probleem werkt dan andersom: alles wat u via IPv6 wilde aanbieden, reageert niet meer. Stel de waarde in op yes als u wilt dat UFW IPv6 filtert met uw regels in plaats van IPv6 volledig te blokkeren.

Controleer exact wat u blootstelt

Gok niet. Meet het vanaf de buitenkant. Maak eerst een lijst van uw listeners en noteer elk exemplaar dat is gebonden aan :::

sudo ss -tlnp | grep '::'

Maak vervolgens vanaf een andere machine verbinding met het publieke IPv6-adres van de server en probeer een poort waarvan u denkt dat deze gesloten is:

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

Als dit een pagina of een banner retourneert, is de poort open op IPv6. Een gesloten poort geeft u Connection refused of een time-out. Deze twee fouten zijn niet hetzelfde signaal, en het verschil tussen geweigerd en time-out vertelt u of de host antwoordde en u wegstuurde, of dat een firewall uw pakket stilletjes heeft gedropt. Scan voor een volledig beeld het IPv6-adres met nmap vanaf een locatie buiten de server:

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

Elke poort die nmap rapporteert als open via IPv6 is een poort die het hele internet kan bereiken, ongeacht wat uw IPv4-scan liet zien. Het naast elkaar leggen van de IPv4- en IPv6-scans is de snelste manier om het gat te vinden: alles wat open staat op -6 maar gesloten is op IPv4, is een service die uw firewall mist.

Het gat dichten

Zorg dat UFW beide stacks dekt en stel standaard weigeren in. Bevestig de wijziging, stel vervolgens een standaardbeleid in om inkomend verkeer te weigeren en sta alleen toe wat u nodig heeft:

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

Als UFW al actief was toen u IPV6=yes omzette, wordt de wijziging pas van kracht nadat u sudo ufw reload uitvoert.

ufw status toont elke regel twee keer: één keer in de basisvorm en één keer met een (v6)-achtervoegsel. Wanneer u de (v6)-regels ziet, filtert UFW IPv6:

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

Als u iptables handmatig beheert, spiegel dan elke regel in ip6tables, of stap over op nftables. De inet-tabellen van nftables dekken IPv4 en IPv6 op één plek en elimineren deze hele categorie fouten. Een enkele nftables inet-filtertabel is de meest overzichtelijke oplossing wanneer u zelf regels schrijft. Als uw VPS op Rocky of AlmaLinux draait in plaats van Ubuntu, is er geen UFW om te configureren en is firewalld de interface die u in plaats daarvan beheert, welke zijn zone-regels tegelijkertijd op beide stacks toepast.

Bind services die niet openbaar hoeven te zijn aan de loopback. Een database, een beheerpaneel of een metrics-endpoint heeft zelden een publiek adres nodig. Bind deze aan 127.0.0.1 en ::1, zodat ze in de eerste plaats nooit op een routeerbaar adres luisteren. Stel voor Postgres listen_addresses = 'localhost' in. Bind een applicatieserver aan 127.0.0.1 en plaats er een reverse proxy voor. Het sluiten van de listener is effectiever dan het firewallen ervan, omdat er dan niets is om te bereiken.

Vertrouw er niet op dat UFW de gepubliceerde poorten van Docker bewaakt. Publiceer containerpoorten naar een specifiek adres in plaats van naar elke interface, bijvoorbeeld -p 127.0.0.1:8080:80, zodat de poort alleen bereikbaar is vanaf de host en alles wat u er bewust naartoe proxied. Wanneer een container echt publiek moet zijn, plaats deze dan achter een Traefik reverse proxy en publiceer alleen de proxy, niet elke app afzonderlijk.

Voeg IPv6-regels toe aan de firewall van uw provider, of accepteer dat dit niet uw firewall voor IPv6 is en laat UFW of nftables op de host die taak uitvoeren.

Controleer of de poort daadwerkelijk gesloten is

Voer na uw wijzigingen dezelfde externe test opnieuw uit:

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

De poort die eerder antwoordde, moet nu weigeren of een time-out geven. nmap moet de poort als gefilterd of gesloten rapporteren. Als een poort nog steeds open is, doorloop dan opnieuw de drie bovenstaande bronnen: een service die nog steeds aan :: is gebonden zonder firewallregel ervoor, een Docker-regel die vóór UFW staat, of een providerfirewall die helemaal geen IPv6-verkeer heeft verwerkt.

Het is nog veiliger om gevoelige services volledig van internet af te houden. Plaats SSH en beheerpaneel achter een WireGuard VPN en firewall hun poorten zo dat ze alleen via de tunnel antwoorden. De vraag naar blootstelling via IPv6 is dan niet meer van toepassing. Om brute-force-scans op de services die wel openbaar blijven te vertragen, kunt u Fail2ban vóór SSH plaatsen boven op een firewall met standaardbeleid deny.

Als poorten nieuw voor u zijn, lees dan eerst wat poorten zijn en hoe services luisteren.

FAQ

Blokkeert UFW IPv6 standaard?

Op een moderne Ubuntu 24.04-installatie: ja. UFW leest IPV6=yes uit /etc/default/ufw en past elke regel toe op zowel IPv4 als IPv6. Met ufw status worden de IPv6-regels weergegeven met een suffix (v6). Het probleem ontstaat wanneer u vertrouwt op een firewall van de provider die alleen IPv4 filtert, wanneer zelfgeschreven iptables-regels geen overeenkomstige ip6tables-regel hebben of wanneer Docker een poort buiten UFW om publiceert. IPV6=no uit een oude image of oude tutorial opent IPv6 niet. Met deze instelling laat een actieve UFW geen IPv6-verkeer toe, behalve op loopback. Controleer deze instelling met grep IPV6 /etc/default/ufw.

Hoe controleer ik welke services mijn VPS via IPv6 beschikbaar maakt?

Voer sudo ss -tlnp uit en noteer elke listener waarvan het lokale adres met [::] begint. Dat betekent dat de service op elke IPv6-interface antwoordt. Test vervolgens vanaf een andere machine het openbare IPv6-adres van de server rechtstreeks met curl -6 -v http://[YOUR:IPV6::ADDR]:PORT/ of scan het met nmap -6 YOUR:IPV6::ADDR. Elke poort die bij de IPv6-scan open is maar via IPv4 gesloten blijft, vormt een beveiligingshiaat.

Waarom kan ik de poort van mijn Docker-container bereiken terwijl UFW aangeeft dat deze is geblokkeerd?

Docker plaatst bij het publiceren van een poort met -p eigen firewallregels vóór de regels van UFW. Daardoor blijft de gepubliceerde poort bereikbaar, ook al vermeldt ufw status deze als geweigerd. Dit gebeurt bij IPv4 en ook bij IPv6 wanneer IPv6-ondersteuning van Docker is ingeschakeld. Publiceer de poort op een specifiek adres, zoals -p 127.0.0.1:8080:80, of plaats de container achter een reverse proxy en publiceer alleen de proxy.

Heb ik nog steeds een IPv6-firewall nodig als mijn IPv4-firewall correct is geconfigureerd?

Ja. IPv4 en IPv6 zijn afzonderlijke netwerkstacks met afzonderlijke firewallregels. Een perfecte set IPv4-regels heeft geen effect op IPv6-verkeer. Als uw VPS een openbaar IPv6-adres heeft, en dat is bij vrijwel alle VPS'en het geval, blijft elke service die op :: luistert via IPv6 bereikbaar totdat een IPv6-firewallregel of een binding op loopback dit voorkomt.

Hoe laat ik een service alleen op IPv4 of alleen op localhost luisteren?

Stel het bindadres van de service in de eigen configuratie in. Bind de service aan 127.0.0.1 voor alleen de IPv4-loopbackinterface, of aan 0.0.0.0 voor alle IPv4-adressen zonder IPv6-listener. Postgres gebruikt listen_addresses, SSH gebruikt ListenAddress en de meeste applicatieservers bieden een host- of bind-optie. Bevestig het resultaat met sudo ss -tlnp en controleer dat Local Address niet langer [::] toont.