Docker omzeilt UFW: 4 geteste oplossingen voor je VPS
Docker manipuleert iptables rechtstreeks en negeert UFW-regels. Je containerpoorten zijn blootgesteld aan het internet, zelfs met ufw deny actief. Hier zijn vier oplossingen met hun afwegingen, elk geverifieerd door een scan vanaf een externe host.
Je UFW-firewall liegt tegen je. Als je Docker draait op een VPS met UFW ingeschakeld, staat elke gepubliceerde containerpoort wagenwijd open op het internet. ufw deny 8080 uitvoeren doet niets. Docker omzeilt UFW volledig.
Deze tutorial toont het probleem in actie en doorloopt vier oplossingen met verschillende afwegingen. Elke oplossing bevat een verificatiestap: de poort scannen vanaf een externe host om te bevestigen dat deze daadwerkelijk geblokkeerd is. Niet alleen ufw status controleren.
Deze handleiding werkt op Ubuntu 24.04 en Debian 12. Beide gebruiken standaard de iptables-compatibiliteitslaag, dus dezelfde fixes zijn van toepassing. Debian 12 gebruikt nftables als standaard backend, maar UFW en Docker communiceren via de iptables-interface, die transparant naar nftables mapt.
Vereisten: Een VPS met Docker geïnstalleerd, UFW ingeschakeld en SSH-toegang. Een tweede machine (je laptop of een andere server) voor externe portscans.
Waarom omzeilt Docker de UFW-firewallregels?
Docker schrijft iptables-regels in de nat- en filter-tabellen om verkeer naar containers te routeren. UFW beheert alleen de INPUT-keten. Wanneer een pakket arriveert voor een gepubliceerde containerpoort, leiden Dockers NAT-regels het om via de FORWARD-keten voordat UFW's INPUT-regels het kunnen zien. Het pakket bereikt UFW nooit. Dit betekent dat ufw deny geen enkel effect heeft op door Docker gepubliceerde poorten.
Hier is de pakketstroom voor een verzoek aan poort 8080 op je VPS, waar een container is gepubliceerd met -p 8080:80:
- Het pakket arriveert op de netwerkinterface
- Het gaat de
PREROUTING-keten van denat-tabel in - Dockers DNAT-regel herschrijft de bestemming naar het container-IP (bijv.
172.17.0.2:80) - Het pakket gaat naar de
FORWARD-keten van defilter-tabel (nietINPUT) - Dockers
DOCKER-keten accepteert het doorgestuurde pakket - Het pakket bereikt de container
UFW ziet het nooit omdat UFW alleen de INPUT-keten bewaakt. Het pakket neemt het FORWARD-pad.
Dit is geen bug. Docker heeft iptables-controle nodig om containernetwerking te laten werken. Maar het creëert een serieus beveiligingslek als je ervan uitging dat UFW je server beschermde.
Hoe verifieer ik dat Docker mijn UFW-firewall omzeilt?
Voordat je een fix toepast, bekijk het probleem zelf. Start een testcontainer die HTTP serveert op poort 8080:
docker run -d --name ufw-test -p 8080:80 nginx:alpine
Controleer dat UFW actief is en inkomend verkeer standaard weigert:
sudo ufw status verbose
Je zou Default: deny (incoming) in de uitvoer moeten zien. Weiger nu expliciet poort 8080:
sudo ufw deny 8080
Controleer of UFW de poort als geblokkeerd toont:
sudo ufw status | grep 8080
8080 DENY Anywhere
8080 (v6) DENY Anywhere (v6)
UFW meldt de poort als geweigerd. Test nu vanaf je lokale machine (niet de server):
curl -s -o /dev/null -w "%{http_code}" http://YOUR_SERVER_IP:8080
200
Het antwoord is 200. De container is volledig toegankelijk vanaf het internet ondanks dat UFW de poort weigert. Als je nmap geïnstalleerd hebt op je lokale machine:
nmap -p 8080 YOUR_SERVER_IP
PORT STATE SERVICE
8080/tcp open http-proxy
De poort is open. UFW beschermt hem niet.
Verwijder de deny-regel (je past hierna een echte fix toe):
sudo ufw delete deny 8080
Laat de testcontainer draaien. Je gebruikt hem om elke oplossing te verifiëren.
Welke Docker UFW-fix moet ik gebruiken?
Er bestaan vier oplossingen. Elk heeft andere afwegingen. Kies degene die bij je setup past.
| Oplossing | Netwerkimpact | Overleeft herstart | Compose-compatibel | Complexiteit | Geschikt voor |
|---|---|---|---|---|---|
| DOCKER-USER-keten | Geen | Ja | Ja | Gemiddeld | Volledige controle, meerdere publieke poorten |
| ufw-docker-tool | Geen | Ja | Ja | Laag | Geautomatiseerd beheer, dynamische containers |
| iptables=false | Verbreekt inter-containernetwerking, geen internet vanuit containers | Ja | Ja | Laag | Geïsoleerde enkele containers (zeldzaam) |
| Bind op 127.0.0.1 | Geen | Ja | Ja | Laag | Services achter een reverse proxy |
Snelle beslissing:
- Staan al je containers achter een reverse proxy (Nginx, Traefik, Caddy)? Gebruik 127.0.0.1-binding. Dit is het eenvoudigst.
- Heb je containers nodig die publiek toegankelijk zijn op specifieke poorten? Gebruik de DOCKER-USER-keten voor handmatige controle of ufw-docker voor geautomatiseerd beheer.
- Draai je containers die nooit netwerktoegang mogen hebben? Gebruik iptables=false, maar begrijp de afwegingen.
Hoe fix ik Docker dat UFW omzeilt met de DOCKER-USER-keten?
De DOCKER-USER-keten is een tijdelijke aanduiding die Docker leeg laat voor beheerders. Regels die je hier toevoegt, worden geëvalueerd vóór Dockers eigen doorstuurregels. Door UFW-integratieregels in deze keten in te voegen via /etc/ufw/after.rules, laat je Docker-verkeer door UFW gaan.
Deze methode geeft je volledige controle. Het werkt met zowel docker run als Docker Compose. Het overleeft herstarts omdat de regels worden geladen wanneer UFW start.
Open /etc/ufw/after.rules:
sudo cp /etc/ufw/after.rules /etc/ufw/after.rules.bak
sudo nano /etc/ufw/after.rules
Voeg het volgende blok toe aan het einde van het bestand, na de bestaande COMMIT-regel:
# BEGIN UFW AND DOCKER
*filter
:ufw-user-forward - [0:0]
:ufw-docker-logging-deny - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j ufw-user-forward
-A DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j RETURN
-A DOCKER-USER -m conntrack --ctstate INVALID -j DROP
-A DOCKER-USER -i docker0 -o docker0 -j ACCEPT
-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16
-A DOCKER-USER -j ufw-docker-logging-deny -m conntrack --ctstate NEW -d 10.0.0.0/8
-A DOCKER-USER -j ufw-docker-logging-deny -m conntrack --ctstate NEW -d 172.16.0.0/12
-A DOCKER-USER -j ufw-docker-logging-deny -m conntrack --ctstate NEW -d 192.168.0.0/16
-A DOCKER-USER -j RETURN
-A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "
-A ufw-docker-logging-deny -j DROP
COMMIT
# END UFW AND DOCKER
Wat dit blok doet:
-A DOCKER-USER -j ufw-user-forward: stuurt Docker-verkeer eerst door UFW's doorstuurregelsconntrack --ctstate RELATED,ESTABLISHED: staat retourverkeer voor bestaande verbindingen toe (houdt bestaande verbindingen in stand na regelwijzigingen)conntrack --ctstate INVALID: verwerpt misvormde pakketten-i docker0 -o docker0: staat container-naar-container-communicatie op hetzelfde bridge-netwerk toe- De
-j RETURN -s-regels staan verkeer toe vanuit privésubnetten - Nieuwe verbindingen (
ctstate NEW) naar Docker-subnetten van buitenaf worden gelogd en verworpen - De loggingregel beperkt tot 3 berichten per minuut om logoverstroming te voorkomen
Herlaad UFW om de nieuwe regels toe te passen:
sudo ufw reload
Verifieer dat de regels zijn geladen:
sudo iptables -L DOCKER-USER -n -v
Je zou je nieuwe regels in de ketenuitvoer moeten zien. Als de keten alleen een standaard RETURN-regel toont, is het after.rules-blok niet correct geladen. Controleer de bestandssyntax.
Een specifieke containerpoort toestaan via UFW
Met de DOCKER-USER-keten actief zijn alle containerpoorten standaard geblokkeerd voor externe toegang. Om een specifieke poort toe te staan, moet je het container-IP en de containerpoort refereren, niet de hostpoort. De reden: Dockers DNAT-regel in de PREROUTING-keten herschrijft de bestemming voordat het pakket de FORWARD-keten bereikt. Op het moment dat je regel wordt geëvalueerd, is de bestemming het interne containeradres.
Zoek eerst het container-IP:
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ufw-test
172.17.0.2
Sta dan verkeer naar die container toe:
sudo ufw route allow proto tcp from any to 172.17.0.2 port 80
Verifieer dat UFW de route-regel toont:
sudo ufw status | grep "ALLOW FWD"
172.17.0.2 80/tcp ALLOW FWD Anywhere
Opmerking: omdat de regel een container-IP target dat kan veranderen bij herstart, gebruik het ufw-docker-tool (volgende sectie) voor automatische IP-tracking. De handmatige DOCKER-USER-aanpak is beter wanneer je de container-IPs zelf beheert (statische IPs in Docker Compose of wanneer je regelupdates script).
Verificatie vanaf een externe host
Test vanaf je lokale machine de toegestane poort:
curl -s -o /dev/null -w "%{http_code}" http://YOUR_SERVER_IP:8080
200
De poort is toegankelijk omdat je expliciet doorsturen naar de container hebt toegestaan. Test nu een niet-toegestane poort (bijvoorbeeld als je een container op poort 9090 had):
nmap -p 9090 YOUR_SERVER_IP
PORT STATE SERVICE
9090/tcp filtered unknown
filtered betekent dat de firewall pakketten stil verwerpt. De DOCKER-USER-keten werkt.
Hoe gebruik ik het ufw-docker-tool om container-firewallregels te beheren?
Het chaifeng/ufw-docker-tool automatiseert de DOCKER-USER-keten-setup en biedt commando's om containerpoorten toe te staan of te weigeren. Het wijzigt /etc/ufw/after.rules voor je en voegt een CLI toe voor het beheren van regels per container.
Installatie:
sudo wget -O /usr/local/bin/ufw-docker \
https://github.com/chaifeng/ufw-docker/raw/master/ufw-docker
sudo chmod +x /usr/local/bin/ufw-docker
Bekijk het script voordat je het uitvoert. Het is een shellscript dat je kunt lezen met cat /usr/local/bin/ufw-docker.
Voer de installer uit om /etc/ufw/after.rules te patchen:
sudo ufw-docker install
Dit voegt DOCKER-USER-ketenregels toe vergelijkbaar met de vorige sectie, inclusief conntrack-gebaseerde filtering en logging. Het patcht ook after6.rules voor IPv6. Herlaad UFW:
sudo ufw reload
Verifieer de installatie:
sudo ufw-docker check
Containerpoorten beheren
Externe toegang tot poort 80 van een container genaamd web toestaan:
sudo ufw-docker allow web 80/tcp
Regels voor een container tonen:
sudo ufw-docker list web
Een regel verwijderen:
sudo ufw-docker delete allow web 80/tcp
Alle Docker-firewallregels tonen:
sudo ufw-docker status
Docker Compose-voorbeeld met ufw-docker
services:
web:
image: nginx:alpine
container_name: web
ports:
- "8080:80"
restart: unless-stopped
Start de stack en sta dan de poort toe:
docker compose up -d
sudo ufw-docker allow web 80/tcp
Opmerking: het ufw-docker-commando refereert de containerpoort (80), niet de hostpoort (8080). Het tool lost de mapping automatisch op.
Alternatief: ufw-docker-automated
Het shinebayar-g/ufw-docker-automated-project kiest een andere aanpak. Het draait als Docker-container zelf, luistert naar Docker API-events en maakt automatisch UFW-regels aan wanneer containers starten of stoppen. Dit lost een beperking van het ufw-docker-tool op: wanneer een container herstart en een nieuw IP krijgt, wordt de oude regel ongeldig.
Met ufw-docker-automated voeg je labels toe aan je containers:
services:
web:
image: nginx:alpine
ports:
- "8080:80"
labels:
- "UFW_MANAGED=TRUE"
restart: unless-stopped
Het tool bewaakt containers met het UFW_MANAGED=TRUE-label en maakt automatisch overeenkomstige UFW-regels aan. Het ondersteunt ook UFW_ALLOW_FROM om toegang te beperken tot specifieke IPs of CIDR-bereiken.
Verificatie vanaf een externe host
Na het toestaan van een poort met ufw-docker, bevestig vanaf je lokale machine:
curl -s -o /dev/null -w "%{http_code}" http://YOUR_SERVER_IP:8080
200
Test een niet-vermelde poort om te bevestigen dat deze geblokkeerd is:
nmap -p 9090 YOUR_SERVER_IP
PORT STATE SERVICE
9090/tcp filtered unknown
Wat gebeurt er als ik Dockers iptables-beheer uitschakel?
"iptables": false instellen in Dockers daemon-configuratie stopt Docker met het aanmaken van iptables-regels. Dit is de eenvoudigste fix maar heeft de ernstigste bijwerkingen.
Wat niet meer werkt:
- Containers kunnen geen verbinding maken met het internet (geen masquerading-regels)
- Container-naar-container-communicatie over verschillende netwerken stopt
- Poortpublicatie (
-p) stopt volledig. Je moet alle doorstuurregels zelf beheren.
Deze aanpak is alleen geschikt als je geïsoleerde containers draait die geen internettoegang nodig hebben en je alle netwerking handmatig regelt.
Bewerk of maak de Docker daemon-configuratie:
sudo nano /etc/docker/daemon.json
{
"iptables": false
}
Als het bestand al inhoud heeft, voeg de "iptables": false-sleutel toe aan het bestaande JSON-object. Maak geen tweede JSON-object.
Herstart Docker:
sudo systemctl restart docker
Verifieer dat Docker geen iptables-regels aanmaakt:
sudo iptables -L DOCKER -n 2>/dev/null
Met Docker 28.x kan de DOCKER-keten nog bestaan maar zou alleen een DROP-regel moeten bevatten in plaats van de gebruikelijke doorstuurregels. Geen ACCEPT-regels betekent dat Docker geen verkeer naar containers routeert.
Verificatie vanaf een externe host
Start de testcontainer opnieuw (deze is gestopt bij de Docker-herstart):
docker start ufw-test
Vanaf je lokale machine:
nmap -p 8080 YOUR_SERVER_IP
PORT STATE SERVICE
8080/tcp closed http-proxy
closed (niet filtered) omdat Docker niet langer de NAT-regels aanmaakt om verkeer door te sturen. De poort luistert niet vanuit extern perspectief.
Om terug te draaien, verwijder "iptables": false uit daemon.json en herstart Docker:
sudo systemctl restart docker
Hoe bind ik Docker Compose-poorten alleen aan localhost?
Voor containers achter een reverse proxy (Nginx, Traefik, Caddy) is de eenvoudigste fix om poorten nooit op 0.0.0.0 te publiceren. Bind ze in plaats daarvan aan 127.0.0.1. De reverse proxy maakt verbinding met de container via localhost. Extern verkeer bereikt de reverse proxy op poorten 80/443, die UFW normaal controleert.
Dit vereist geen iptables-wijzigingen, geen extra tools en werkt op elk besturingssysteem.
Wijzig in je docker-compose.yml de poortbinding:
services:
app:
image: your-app:latest
ports:
- "127.0.0.1:8080:80"
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
restart: unless-stopped
De app-service bindt alleen aan 127.0.0.1:8080. Deze is niet toegankelijk van buiten de server. De nginx-service bindt aan 0.0.0.0:80 en 0.0.0.0:443 (de standaard wanneer geen IP is opgegeven), die je met UFW controleert.
Voor docker run, de equivalente syntax:
docker run -d --name app -p 127.0.0.1:8080:80 your-app:latest
Binding verifiëren
Op de server, bevestig dat de poort alleen aan localhost is gebonden:
ss -tlnp | grep 8080
LISTEN 0 4096 127.0.0.1:8080 0.0.0.0:* users:(("docker-proxy",pid=12345,fd=4))
Let goed op: het luisteradres is 127.0.0.1:8080, niet 0.0.0.0:8080. Dit betekent dat alleen lokale verbindingen worden geaccepteerd.
Verificatie vanaf een externe host
Vanaf je lokale machine:
nmap -p 8080 YOUR_SERVER_IP
PORT STATE SERVICE
8080/tcp closed http-proxy
De poort is gesloten voor extern verkeer. Je reverse proxy regelt de publieke toegang op poorten 80 en 443, die UFW normaal beschermt.
Dit is de aanbevolen aanpak voor de meeste VPS-setups waar je webapplicaties achter een reverse proxy draait.
Probleemoplossing
Regels verdwijnen na Docker-herstart
Als je de DOCKER-USER-keten-aanpak hebt gebruikt en regels verdwijnen bij Docker-herstart, controleer dat je regels in /etc/ufw/after.rules staan en niet handmatig zijn toegevoegd met iptables-commando's. Handmatige iptables-regels overleven geen serviceherstarts. Het after.rules-bestand wordt elke keer geladen wanneer UFW start.
sudo ufw reload
sudo iptables -L DOCKER-USER -n -v
Container kan DNS niet resolven na toepassing van DOCKER-USER-regels
Het after.rules-blok bevat een conntrack --ctstate RELATED,ESTABLISHED-regel die DNS-antwoordverkeer terug naar containers toestaat. Als DNS-resolutie faalt in containers, verifieer dat de conntrack-regel is geladen:
sudo iptables -L DOCKER-USER -n -v | grep "RELATED,ESTABLISHED"
Als deze ontbreekt, controleer de syntax van je after.rules-blok en herlaad UFW.
ufw-docker-commando's falen met "container not found"
Het ufw-docker allow-commando vereist dat de container draait. Het resolvet de containernaam naar een IP-adres. Als de container gestopt is, faalt het commando. Start de container eerst, voeg dan de regel toe.
Debian 12 nftables-compatibiliteit
Debian 12 gebruikt nftables als firewall-backend, maar UFW en Docker gebruiken de iptables-compatibiliteitslaag (iptables-nft). De oplossingen in deze handleiding werken identiek op Debian 12 en Ubuntu 24.04. Verifieer dat je het nft-backend gebruikt:
sudo iptables --version
iptables v1.8.10 (nf_tables)
Het (nf_tables)-achtervoegsel bevestigt dat de iptables-nft-compatibiliteitslaag actief is. Alle DOCKER-USER-ketenregels werken via deze laag.
Logs controleren op geblokkeerd verkeer
Als een verbinding onverwacht geblokkeerd is na toepassing van de DOCKER-USER-keten-fix, controleer de UFW Docker-logs:
sudo journalctl -k | grep "UFW DOCKER BLOCK"
De logvermeldingen tonen het bron-IP en de bestemmingspoort van geblokkeerde pakketten.
Testresources opruimen
Verwijder de testcontainer wanneer je klaar bent:
docker rm -f ufw-test
Als je een UFW deny-regel hebt toegevoegd tijdens het testen, verwijder deze:
sudo ufw delete deny 8080
Copyright 2026 Virtua.Cloud. Alle rechten voorbehouden. Deze inhoud is een origineel werk van het Virtua.Cloud-team. Reproductie, herpublicatie of herdistributie zonder schriftelijke toestemming is verboden.
Klaar om het zelf te proberen?
Deploy uw eigen server in seconden. Linux, Windows of FreeBSD.
Bekijk VPS-aanbod