U kunt Docker maandenlang in productie laten draaien zonder zichtbaar probleem. Containers starten, apps reageren, er gaat niets kapot. Vervolgens creëert één blootgestelde poort of één verkeerd geconfigureerde toestemming een voet aan de grond die een aanvaller niet hoefde te verdienen. De meeste beveiligingsfouten in Docker zien er pas uit als er iets misgaat.
Dit artikel behandelt de specifieke configuraties die containeromgevingen in gevaar brengen, wat elke configuratie voor een aanvaller mogelijk maakt, en sluit af met een checklist die u vandaag nog kunt uitvoeren op uw eigen configuratie.
Waarom Docker-beveiliging moeilijker is dan het lijkt
Containers voelen zich geïsoleerd. Je start er een, deze beheert zijn eigen procesruimte en van binnenuit bestaat de volgende container niet. Je krijgt wel isolatie, maar het is slechts gedeeltelijk. Containers delen de kernel van de host, wat betekent dat een proces binnen een container, onder specifieke omstandigheden, het hostsysteem volledig kan bereiken.
Dockerschepen zijn geconfigureerd voor het gemak van ontwikkelaars, niet voor productieharding. Root-toegang ingeschakeld. Alle poorten zijn bindbaar aan alle interfaces. Geen runtime-monitoring. De meeste ontwikkelaars accepteren deze instellingen, verzenden de container en gaan verder. Dat is een redelijke aanpak om aan de slag te gaan; het is geen voltooide beveiligingshouding.
Volgens Red Hat’s State of Kubernetes Security-rapport uit 202467% van de organisaties heeft de implementatie van applicaties uitgesteld of vertraagd vanwege beveiligingsproblemen met containers of Kubernetes. Die wrijving komt meestal niet door aanvallen. Het is afkomstig van teams die ontdekten dat hun containeropstellingen verharding nodig hadden en die ze niet hadden ingebouwd.
Vaak zien we containers in productie draaien met dezelfde configuratie als op de lokale machine van een ontwikkelaar. Dat is waar Docker-beveiligingsfouten de neiging hebben zich stilletjes te verergeren, zonder zichtbare symptomen totdat iets wordt gecontroleerd of mislukt.
De fouten die deze hiaten creëren zijn specifiek, voorspelbaar en meestal vermijdbaar, te beginnen op configuratieniveau.
Veelvoorkomende Docker-configuratiefouten
De meeste containerinbreuken beginnen niet met een zero-day exploit. Ze beginnen met een configuratie die op de eerste dag is ingesteld, zonder veel na te denken over netwerkblootstelling of privilegebereik.
Standaard Docker-instellingen zijn gebouwd om te werken. In de kloof tussen functioneel en veilig stapelen de beveiligingsrisico's van Docker-containers zich op, vooral in zelf-gehoste opstellingen die worden ingezet en nooit meer worden herzien.
We zien dit patroon vaak: containers op openbare IP-servers met poortbindingen, gebruikersinstellingen en netwerkconfiguraties precies zoals ze waren bij de eerste implementatie.
Containers uitvoeren als root
Wanneer u een Docker-container start zonder een gebruiker op te geven, wordt deze als root uitgevoerd. Dat betekent dat elk proces in de container, inclusief uw applicatie, rechten op rootniveau heeft binnen de naamruimte van de container.

Rooten in een container is niet hetzelfde als rooten op de host, maar de scheiding is niet absoluut. Exploitaties van privilege-escalatie die zich richten op de runtime, zoals de goed gedocumenteerde runc CVE-2019-5736 en soortgelijke runtimefouten, vereisen vaak een rootcontainerproces om te slagen.
Niet-rootcontainers verwijderen de rootprocesvereiste waarvan deze exploits afhankelijk zijn, waardoor het aanvalsoppervlak voor die klasse van kwetsbaarheden aanzienlijk wordt verkleind, hoewel ze het ontsnappingsrisico van containers niet volledig elimineren.
Het toevoegen van een USER-instructie aan uw Dockerfile lost dit op. Sommige officiële afbeeldingen worden geleverd met een gebruiker zonder rechten die u kunt activeren met een GEBRUIKER-instructie, maar veel afbeeldingen gebruiken nog steeds standaard root zonder dat er een kant-en-klare app-gebruiker is. In die gevallen maakt u de gebruiker aan in het Dockerbestand voordat u ernaar overschakelt. Voor de meeste zelfgehoste configuraties elimineert deze enkele wijziging een hele categorie escalatierisico's.
Te veel poorten blootstellen aan het openbare internet
Wanneer u een port publiceert met Docker, schrijft Docker rechtstreeks zijn eigen iptables-regels. Deze regels worden uitgevoerd vóór firewallregels op hostniveau. Dit is een bekend gedrag gerapporteerd door de gemeenschap En gedocumenteerd in de pakketfiltergids van Docker, geen verkeerde configuratie, en het betekent dat UFW en soortgelijke tools niet blokkeren wat Docker al heeft geopend.

Docker schrijft rechtstreeks naar iptables en omzeilt de standaardinstellingen van UFW en firewalld op veel Linux-hosts. Dat betekent dat een poort gebonden aan 0.0.0.0 openbaar bereikbaar kan zijn, zelfs als uw firewall geconfigureerd lijkt. Cloudbeveiligingsgroepen en DOCKER-USER-ketenregels kunnen dat verkeer nog steeds blokkeren, dus de daadwerkelijke blootstelling hangt af van uw specifieke netwerkconfiguratie.
Bind services waar mogelijk aan 127.0.0.1, leid openbaar verkeer door een reverse proxy en publiceer alleen datgene wat werkelijk externe toegang vereist. Een reverse proxy is de meest betrouwbare manier om te controleren wat er van buiten de host wordt getoond.
Netwerkisolatie tussen containers negeren
Elke container op dat netwerk kan zonder beperking elke andere container op het netwerk bereiken. De standaardbridge past geen verkeersfiltering toe tussen containers die deze delen, en de meeste instellingen veranderen die configuratie nooit.

Als één container in gevaar komt, wordt die open communicatie een zijwaarts bewegingspad. Een frontend-container kan een database, een interne API of iets anders op hetzelfde standaard bridge-netwerk bereiken, zelfs als die toegang nooit de bedoeling was.
Door de gebruiker gedefinieerde netwerken geven u expliciete controle over welke containers kunnen communiceren, maar één aangepast netwerk dat door al uw services wordt gedeeld, maakt nog steeds gratis verkeer tussen containers mogelijk. Echte isolatie vereist dat diensten die niet met elkaar mogen praten, op afzonderlijke netwerken worden geplaatst. Het uitschakelen van de standaardbrug is het startpunt, niet de finishlijn.
Met uitzicht op de Docker Socket
De Docker-socket op /var/run/docker.sock is de besturingsinterface voor de gehele Docker-engine. Door het in een container te monteren, krijgt die container directe API-toegang tot de daemon die op de host draait.

Met die toegang kan een container nieuwe containers starten, hostmappen aankoppelen, actieve containers inspecteren en wijzigen en de hostmachine effectief besturen. Het aanvalsoppervlak is gelijk aan rooten op de host, en daarom verdient elke tool die sockettoegang vereist een zorgvuldige evaluatie.
Voor de meeste gebruiksscenario's zijn er veiliger alternatieven: scoped API's of Docker-beheertools waarvoor geen sockettoegang nodig is. Docker-in-Docker heeft zijn eigen beveiligings- en operationele afwegingen en is geen eenvoudig alternatief.
Configuratiefouten zorgen voor de eerste blootstelling. Imago- en afhankelijkheidskeuzes bepalen hoe die blootstelling in de loop van de tijd toeneemt.
Imago en geheimen Fouten die langer meegaan dan de container
Wanneer u een container stopt, stoppen de configuratiefouten daarin ook. Wanneer u opnieuw opbouwt op basis van een image die een kwetsbaarheid of een hardgecodeerde referentie bevat, begint het probleem opnieuw met de container. Fouten op afbeeldingsniveau worden tussen implementaties niet gereset.
Ze reizen met de image mee naar elke omgeving die de image ophaalt, elk register waarin de image is opgeslagen en elk teamlid dat de image beheert. Die volharding maakt het beheer van imago's en geheimen tot een aparte risicocategorie, die de moeite waard is om los van de configuratie te controleren.
We zien dit patroon vaak: een beeld dat zorgvuldig is gekozen bij de start van het project en sindsdien nooit meer opnieuw is opgebouwd, langzaam afdrijvend van de beveiligingsbasislijn die het aanvankelijk vertegenwoordigde.
Gebruik van niet-vertrouwde of verouderde afbeeldingen
Openbare registers zijn voor iedereen toegankelijk. Via Docker Hub zijn kwaadaardige afbeeldingen verspreid met cryptominers en backdoors ingebed in de laaggeschiedenis die blijven bestaan bij het opnieuw opstarten van containers. Verificatie voordat zaken worden opgehaald, vooral voor afbeeldingen van niet-officiële of onbekende uitgevers.

Het afzonderlijke probleem is oudheid. Een officieel image dat je zes maanden geleden hebt gemaakt en sindsdien nooit meer opnieuw hebt opgebouwd, heeft niet-gepatchte Docker-kwetsbaarheden verzameld bij elke CVE die tegen zijn pakketten werd onthuld. Het beeld is niet gebroken. Het is gewoon niet meer actueel.
Sonatype’s State of the Software Supply Chain-rapport 2024 ontdekte dat 95% van de tijd een kwetsbaar onderdeel wordt gebruikt, dat er al een vaste versie beschikbaar is en dat 80% van de applicatie-afhankelijkheden meer dan een jaar lang niet zijn geüpgraded. Dat patroon is ook relevant voor Docker-basisimages, omdat ze afhankelijk zijn van dezelfde open-sourcepakketten.
Gebruik officiële afbeeldingen van geverifieerde uitgevers en zet specifieke versietags vast in plaats van te vertrouwen op ‘nieuwste’. Bouw een regelmatige herbouwfrequentie op om uw afbeeldingen actueel te houden.
Hardcoding-geheimen in Dockerfiles en Compose Files
Inloggegevens die zijn geschreven in een Dockerfile ENV- of ARG-instructie, hardgecodeerd in een Compose-omgevingsblok, doorgegeven als build-argumenten of opgeslagen in een .env-bestand dat is vastgelegd voor versiebeheer, verdwijnen niet wanneer u de container stopt. Ze blijven in de geschiedenis van de afbeeldingslaag of in het bronbeheer, toegankelijk voor iedereen die beide kan bereiken.

Dit is een van de meest over het hoofd geziene Docker-beveiligingsfouten, omdat het tijdens de ontwikkeling geen zichtbare problemen veroorzaakt. Een API-sleutel in een ENV-instructie werkt correct. Het bevindt zich ook in uw opslagplaats, is in uw afbeelding ingebakken en wordt overal verspreid waar de afbeelding naartoe gaat.
Modern Docker Compose ondersteunt een systeemeigen geheimenmechanisme dat tijdens runtime inloggegevens koppelt zonder deze in de afbeelding te bakken. Docker's Secrets API en externe geheimmanagers volgen hetzelfde principe. Dit zijn de opties die de inloggegevens volledig buiten de build-artefacten en vastgelegde bestanden houden.
Runtime-omgevingsvariabelen zijn een verbetering ten opzichte van hardgecodeerde inloggegevens, maar ze zijn nog steeds zichtbaar via Docker-inspectie-uitvoer, logboeken en crashdumps. Ze zijn een stapje hoger dan ingebakken geheimen, en geen voltooide oplossing.
Containerimages niet regelmatig bijwerken
Maandenlang dezelfde afbeelding gebruiken is een veel voorkomende gewoonte. Elke dag die verstrijkt nadat een nieuwe kwetsbaarheid is onthuld, maar voordat u deze opnieuw opbouwt, hebben uw containers een blootstellingsvenster dat groeit zonder enige zichtbare verandering.
Stel een consistent herbouwschema op. Automatiseer dat proces waar mogelijk en voer periodiek een kwetsbaarheidsscanner uit op uw huidige afbeeldingen. Het doel is niet perfectie. Het verkleint de tijd tussen het uitbrengen van een patch en het implementeren ervan.
Toegangscontrole en monitoring kunnen bij snelle implementaties minder prioriteit krijgen. Het zijn ook de categorieën waarin incidenten het langst onopgemerkt blijven.
Toegangscontrole en zichtbaarheidslacunes
Nadat een container draait met een solide configuratie en huidige images, blijven er twee categorieën van mislukkingen over. Beide zijn van nature onzichtbaar: u zult een zwak probleem met de toegangscontrole pas opmerken totdat iemand er gebruik van maakt, en u zult geen toezichtsgat opmerken totdat u activiteiten moet onderzoeken die nooit zijn geregistreerd.
Hetzelfde Red Hat 2024-onderzoek ontdekte dat 42% van de teams niet over voldoende capaciteiten beschikte om de containerbeveiliging en de daarmee samenhangende bedreigingen aan te pakken.
We constateren dat lacunes in de monitoring doorgaans aan het licht komen tijdens incidentonderzoeken, en niet eerder. Tegen de tijd dat zichtbaarheid een prioriteit wordt, is het vaak een reactie op iets in plaats van het voorkomen ervan.
Zwakke authenticatie en zichtbare beheerdashboards
Voor een containerbeheerdashboard op een openbaar IP-adres zonder authenticatie is geen geavanceerde aanvaller nodig. Het vereist dat ze het adres weten. Dat is een lagere lat dan de meeste teams zich realiseren.

Zelfgehoste monitoring- en beheertools worden doorgaans geleverd met een webinterface die toegankelijk is op alle netwerkinterfaces. Deze op een openbaar IP-adres laten staan zonder authentificatie ervoor is het container-equivalent van het ontgrendeld laten van een beheerderspaneel.
Authenticatie, een reverse proxy en plaatsing op een privénetwerk vormen de basis. Toegangscontrole is een configuratiestap die u toevoegt aan elke beheerinterface, en niet iets dat ingeschakeld wordt geleverd.
Hetzelfde principe geldt voor Docker CLI en GUI-beheer; Toegang op beheerdersniveau tot de daemon brengt hetzelfde risico met zich mee, ongeacht de interface.
Houdt niet in de gaten wat uw containers doen
Als een container wordt gecompromitteerd, creëert de activiteit van de aanvaller een spoor: veranderingen in procesgedrag, ongebruikelijke netwerkverbindingen en onverwachte bestandswijzigingen. Als er geen logverzameling plaatsvindt, bestaat dat spoor niet in een vorm waarop u actie kunt ondernemen.
Gecentraliseerde logboekverzameling, containerauditregistratie en runtime-monitoringtools geven u de gegevens om abnormale activiteit te detecteren voordat deze zich vermenigvuldigt. Het doel is niet om elke regel te analyseren. Het betekent dat u over de gegevens beschikt wanneer u onderzoek moet doen.
Containerconfiguraties die stil in productie draaien, zonder logboekpijplijn en zonder waarschuwingen, zijn niet onderhoudsarm. Ze zijn niet geïnspecteerd. Dat zijn twee verschillende operationele toestanden.
Waarom de infrastructuuromgeving er ook toe doet
Containerbeveiliging begint met configuratie, maar de configuratie draait bovenop de infrastructuur. Een host met verkeerd geconfigureerde netwerken, gedeelde bronnen of geen filtering op netwerkniveau creëert omstandigheden die van invloed zijn op elke container erboven. Het correct instellen van de container en de juiste serverconfiguratie zijn twee afzonderlijke taken.
Veel beveiligingslekken in Docker worden versterkt door omstandigheden die de containers zelf erven:
- Een server met gedeelde tenant zonder hardware-isolatie tussen tenants
- Een hostkernel die zonder patch wordt uitgevoerd
- Een host zonder ingebouwde filtering op netwerkniveau
Dit neemt de noodzaak van de bovenstaande configuratiestappen niet weg, omdat een goede containerverharding van belang is, ongeacht de infrastructuurlaag. Door te beginnen met geïsoleerde infrastructuur wordt een zorgpunt uit de vergelijking weggenomen.
Bij Cloudzy bieden we twee paden aan, afhankelijk van wat uw installatie vereist:
- Linux-VPS: een schone omgeving om Docker zelf te implementeren en de verhardingsstappen in dit artikel toe te passen
- Portainer VPS: een optie met één klik waarbij Portainer vooraf is geïnstalleerd; de server start op en u bevindt zich al in het dashboard
Beide opties draaien op dezelfde infrastructuur: KVM-virtualisatie, AMD Ryzen 9 CPU's met een boostkloksnelheid tot 5,7 GHz, DDR5-geheugen, NVMe SSD-opslag, een netwerk tot 40 Gbps en gratis DDoS-bescherming via BuyVM-filtering, op 12 wereldwijde locaties met een uptime SLA van 99,95%.
Voor een diepere blik op het draaien van Portainer op een VPS, behandelen we dit in een speciaal artikel.
Een praktische beveiligingschecklist voor Docker-implementaties
De bovenstaande Docker-beveiligingsfouten komen meestal voort uit afzonderlijke configuratiebeslissingen die één keer zijn genomen en nooit opnieuw zijn bekeken. Door deze checklist uit te voeren op een bestaande opstelling, worden deze hiaten opgevangen. Het werkt als een audit, niet als een implementatiegids.
Deze best practices voor Docker-beveiliging beschrijven hoe Docker-containers kunnen worden beveiligd tegen de meest voorkomende configuratiefouten die hierboven zijn beschreven.
Snelle referentie: alle 9 fouten
| Fout | Categorie | Oplossing met één regel |
| Wordt uitgevoerd als root | Configuratie | Toevoegen GEBRUIKER richtlijn naar uw Dockerfile |
| Poorten gebonden aan 0.0.0.0 | Configuratie | Bind aan 127.0.0.1 en routeer via een omgekeerde proxy |
| Geen netwerkisolatie | Configuratie | Splits services over afzonderlijke, door de gebruiker gedefinieerde netwerken op basis van toegangsbehoeften. |
| Docker-aansluiting gemonteerd | Configuratie | Verwijder de houder; gebruik scoped API's of alternatieven |
| Niet-vertrouwde of verouderde afbeeldingen | Afbeelding | Gebruik officiële afbeeldingen met vastgezette versietags |
| Hardgecodeerde geheimen | Afbeelding | Verplaats inloggegevens naar runtime env vars of een geheimenmanager |
| Geen schema voor het opnieuw opbouwen van afbeeldingen | Afbeelding | Stel een maandelijkse herbouwfrequentie in; automatiseren waar mogelijk |
| Niet-geverifieerde dashboards | Toegang | Voeg verificatie toe en verplaats beheer-UI's naar privénetwerken |
| Geen containerlogboekverzameling | Toegang | Stel gecentraliseerde logboekregistratie en runtime-monitoring in |
We raden aan om het eerst tegen bestaande opstellingen uit te voeren, omdat daar de gaten waarschijnlijk al aanwezig zijn.
Containers die als niet-root draaien: Controleer uw Dockerfiles op een USER-instructie. Als er geen bestaat, wordt de container als root uitgevoerd.
Poortbindingen beperkt tot localhost of proxy: Voer docker ps uit en bekijk de poortbindingen. Een 0.0.0.0:PORT-invoer kan openbaar bereikbaar zijn op hosts waar geen upstream-beveiligingsgroep, externe firewall of DOCKER-USER-ketenregel deze blokkeert.
Aangepaste bridge-netwerken in gebruik: Containers op de standaardbrug van Docker kunnen elkaar vrij bereiken. Containers op dezelfde door de gebruiker gedefinieerde bridge kunnen nog steeds met elkaar communiceren, dus splits services over afzonderlijke netwerken op basis van een vertrouwensgrens voor daadwerkelijke isolatie.
Docker-aansluiting niet gemonteerd in containers: Vink Bestanden samenstellen en argumenten uitvoeren aan. Als /var/run/docker.sock als volume verschijnt, bevestig dan dat dit vereist en opzettelijk is.
Basisafbeeldingen van geverifieerde uitgevers met vastgezette versies: A FROM ubuntu:latest haalt een niet-gespecificeerde, mogelijk verouderde versie op. Vastmaken aan een specifieke release.
Geen geheimen in Dockerfiles, Compose-bestanden of build-argumenten: De geschiedenis van de afbeeldingslaag blijft na het verwijderen van de container inloggegevens behouden. Gebruik Geheimen samenstellen, Zwermgeheimen, geheime mounts bouwen of een externe geheimenmanager. Runtime-omgevingsvariabelen zijn beter dan hardgecodeerde waarden, maar verschijnen nog steeds in de inspectie-uitvoer en logboeken.
Schema voor het opnieuw opbouwen van afbeeldingen gedefinieerd: Oude afbeeldingen accumuleren kwetsbaarheden. Een maandelijkse herbouwfrequentie zorgt ervoor dat het belichtingsvenster voor de meeste opstellingen beheersbaar blijft.
Beheerinterfaces achter authenticatie: Elk dashboard op een openbaar IP-adres zonder verificatie is een open toegangspunt. Waar mogelijk verdient plaatsing in een particulier netwerk de voorkeur.
Containerlogboeken die worden verzameld: Zonder een logboekpijplijn is de detectie van incidenten afhankelijk van de zichtbare systeemimpact. Dat is een laat signaal om actie op te ondernemen.
Conclusie
De standaardconfiguratie van Docker is gebouwd voor gemak, niet voor beveiliging. De meeste fouten die in dit artikel worden behandeld, zijn terug te voeren op instellingen die na de eerste implementatie nooit zijn gewijzigd, en niet op geavanceerde aanvallen.
De oplossingen zijn meestal eenmalige configuratiebeslissingen: een USER-instructie, een poortbindingswijziging, een aangepast netwerk, een herbouwschema. Geen van deze vereist nieuwe gereedschappen voor de meeste opstellingen.
De containerconfiguratie goed krijgen is de eerste taak. De infrastructuur waarop het draait is de tweede. Beide zijn belangrijk, en de een vervangt de ander niet.