
Docker-geheimen veilig bouwen: ARG en ENV blijven in het image achter

Heeft een Docker-build een API-sleutel of wachtwoord nodig, geef het dan alleen aan de betreffende bouwstap via een secret mount. Heeft de applicatie het geheim na het starten nodig, laat haar een gemount secretbestand lezen. De Docker-documentatie over build secrets waarschuwt dat ARG en ENV ongeschikt zijn voor bouwgeheimen omdat waarden in het uiteindelijke image kunnen achterblijven; een secret mount is slechts beschikbaar tijdens de bouwinstructie die hem gebruikt.
Een lokaal .env-bestand maakt een wachtwoord niet automatisch veilig. Wordt de waarde daaruit als omgevingsvariabele aan een container gegeven, dan is zij nog steeds onderdeel van de containeromgeving. De OWASP-richtlijn voor geheimen raadt omgevingsvariabelen alleen aan wanneer betere methoden niet mogelijk zijn: processen kunnen erbij en waarden kunnen in logs of systeemdumps belanden.
Kies de overdracht op het moment van gebruik
- Alleen tijdens de build: geef de builder een geheim met --secret en koppel het in de benodigde RUN-instructie aan --mount=type=secret. Gebruik voor die waarde geen ARG, ENV of COPY.
- Tijdens runtime: geef uitsluitend de service die het geheim nodig heeft toegang tot een gemount secretbestand. De applicatie moet dat bestand kunnen lezen, rechtstreeks of via een ondersteunde variabele die alleen het bestandspad bevat.
- Bij lokale ontwikkeling: gebruik .env voor gewone configuratie, zoals een poort of servicenaam. Bewaar tokens en wachtwoorden in afzonderlijke, niet ingecheckte bestanden en bied die als build- of runtime secret aan.
Het verschil zit in wat na afloop zichtbaar blijft. Een ARG is geen omgevingsvariabele van de uiteindelijke container, maar de waarde kan in imagegeschiedenis of buildmetadata terechtkomen. Een ENV-instructie voegt een waarde toe aan de imageconfiguratie en maakt haar beschikbaar in containers die uit dat image starten. Een secretbestand is evenmin onzichtbaar voor iedereen: processen met toegang tot de mount kunnen het tijdens het gebruik lezen.
Buildtijd: gebruik een tijdelijke secret mount

Neem als voorwaardelijk voorbeeld een build die een privéafhankelijkheid ophaalt. Bewaar het token lokaal in ./secrets/repo_token en bouw met docker buildx build --secret id=repo_token,src=./secrets/repo_token -t mijnapp:controle . De relevante Dockerfile-instructie kan luiden: RUN --mount=type=secret,id=repo_token,required=true ./haal-private-afhankelijkheid-op.sh. Het voorbeeldscript moet /run/secrets/repo_token zelf lezen. Met required=true stopt deze stap wanneer het geheim ontbreekt.
De mount bestaat voor die RUN-instructie en wordt niet als bestand in de imagelaag gekopieerd. Een bouwtool die uitsluitend een omgevingsvariabele accepteert, kan binnen dezelfde RUN-instructie via de optie env=REPO_TOKEN een tijdelijk aangeboden waarde krijgen. Dat is iets anders dan ENV in de Dockerfile: de tool krijgt de waarde tijdens de stap, terwijl er geen blijvende ENV-instructie voor wordt aangemaakt. Laat de opdracht het token wel in een log of gegenereerd bestand schrijven, dan kan het alsnog uitlekken.
Ook de volgorde van Dockerfile-instructies telt. Wie eerst een geheim met COPY in een laag opneemt en het in een latere RUN-instructie verwijdert, heeft de oorspronkelijke laag daarmee niet gewist. Hetzelfde geldt voor een eerder gepubliceerd image wanneer de Dockerfile achteraf wordt aangepast. Bij een vermoedelijk blootgesteld token hoort daarom behalve een nieuwe build ook intrekking of rotatie van dat token; controleer eveneens waar oude images en buildlogs zijn opgeslagen.
Runtime: geef de service een bestand

Voor een geheim dat de applicatie na het starten nodig heeft, kan Docker Compose het als bestand beschikbaar maken. De instructie voor Compose-secrets beschrijft dat een service expliciet toegang krijgt via services.app.secrets en dat het geheim in een Linux-container onder /run/secrets/<naam> wordt gemount. Definieer bijvoorbeeld bovenaan een secret api_token met file: ./secrets/api_token, geef app toegang tot api_token en laat de applicatie /run/secrets/api_token lezen.
Zo hoeft de geheime waarde niet in de imageconfiguratie of als omgevingsvariabele in de container te staan. Het bronbestand blijft op de host aanwezig; in de container kunnen processen met voldoende bestandsrechten de mount lezen. Beperk daarom de toegang per service en let op de rechten binnen die service. Sommige images, waaronder bepaalde officiële database-images, ondersteunen een variabele met het achtervoegsel _FILE. Daarin staat het pad naar het secretbestand. Controleer of de gebruikte applicatie deze conventie werkelijk ondersteunt voordat je erop vertrouwt.
Lokale ontwikkeling: houd .env en geheimen uit elkaar
Docker Compose gebruikt .env voor interpolatie van waarden in een Compose-bestand. Staat daarin een wachtwoord dat onder environment aan een service wordt doorgegeven, dan eindigt het alsnog als runtimevariabele. De keuze voor de bestandsnaam .env verandert die overdracht niet. Gebruik het bestand daarom voor niet-geheime ontwikkelinstellingen en koppel een afzonderlijk geheim bestand aan een Compose-secret of een build secret, afhankelijk van het gebruiksmoment.
Zet ./secrets/ in .gitignore om nieuwe geheime bestanden niet per ongeluk te committen. Zet dezelfde map ook in .dockerignore, zodat zij niet in de buildcontext belandt en een brede COPY-instructie haar niet in het image kan opnemen. Die bestanden hebben verschillende taken: .gitignore regelt wat Git standaard negeert, terwijl .dockerignore bepaalt wat de builder uit de context ontvangt. Een bestand dat al eerder is ingecheckt, verdwijnt niet uit de Git-geschiedenis door het alsnog aan .gitignore toe te voegen.
Wees ook voorzichtig met uitvoer van docker compose config. Als een wachtwoord via interpolatie in een environment-veld terechtkomt, kan de uitgewerkte configuratie die waarde tonen. Deel of bewaar die uitvoer pas nadat je hebt gecontroleerd wat erin staat.
Controleer Dockerfile, image en uitvoer
Begin met docker build --check .. De Dockerfile-controle kan waarschuwen voor namen van ARG- of ENV-variabelen die op geheimen wijzen. Zoek daarnaast zelf naar letterlijk ingevulde sleutels, wachtwoorden, tokens en COPY-instructies die geheime bestanden meenemen. Een controle die op verdachte namen let, kan een anders genoemde of indirect gebruikte waarde missen.
Bekijk vervolgens het gebouwde image met docker image history --no-trunc mijnapp:controle. De optie --no-trunc toont de bouwgeschiedenis zonder de gebruikelijke inkorting; let op argumenten en opdrachtregels waarin een waarde kan zijn verwerkt. Gebruik docker image inspect mijnapp:controle om onder Config.Env te zien welke omgevingswaarden het image bevat. Bij een draaiende container geeft docker container inspect naam inzicht in de runtimeconfiguratie. Behandel de uitvoer van deze commando’s zelf als gevoelig zolang je een lek onderzoekt.
Geen treffer in de geschiedenis of Config.Env betekent alleen dat je daar niets hebt gevonden. Een eerder gekopieerd bestand kan nog in een imagelaag zitten; daarnaast kunnen buildlogs of gedeelde artefacten een waarde bevatten. Onderzoek die plaatsen wanneer je weet dat een geheim ooit tijdens de build is gebruikt. Roteer een mogelijk blootgestelde sleutel voordat je het oude image en de bijbehorende artefacten opruimt.
Lees ook:
Gerelateerde artikelen


Een SBOM maken in GitHub Actions: scan na de installatiestap

Van reCAPTCHA naar Turnstile: v3 past niet in de compatibiliteitsmodus

GitHub Push Protection instellen: grote pushes kunnen de scan ontwijken

Plausible zelf hosten: gratis software maakt jou verantwoordelijk voor updates

Terraform of OpenTofu: stateversleuteling maakt terugkeren lastiger
Abonneer je op onze nieuwsbrief
Ontvang het laatste nieuws over Web3, AI en crypto rechtstreeks in je inbox.