Direct naar de inhoud

Cookie-instellingen

Kies per soort welke cookies je toestaat. Je kunt je keuze altijd wijzigen via Cookie-instellingen onderaan elke pagina. Privacyverklaring

Noodzakelijk

Altijd aan

Nodig om in te loggen, je winkelwagen te bewaren en je keuzes te onthouden. Deze staan altijd aan.

Welke cookies?
  • weboke_sessie, weboke_klant, weboke_remember: je sessie en ingelogd blijven
  • weboke_basket: je winkelwagen
  • wkBtwIncl: prijzen met of zonder btw tonen
  • wok_cookies: je cookiekeuze, 12 maanden

Meet welke advertentie tot een bezoek of bestelling leidt, zodat we niet betalen voor advertenties die niets doen.

Welke cookies?
  • Google Ads via Google Tag Manager (_gcl_au, _gcl_aw), tot 90 dagen
  • OpenAI Ads, advertenties in ChatGPT
  • wok_aff: via welke partner je bij ons kwam (affiliateprogramma), 60 dagen

4,6

Land en taal

Elk land heeft zijn eigen site, met prijzen en btw van dat land.

Bekijken

Module 9 · 9.3

Docker compose en de firewall

Module 9: Docker en docker compose · Leestijd: 12 minuten

Leerdoel: Na deze les kun je diensten beschrijven in een compose-bestand, ze starten, bijwerken en stoppen, en leg je uit waarom gepubliceerde poorten van Docker langs ufw heen gaan en hoe je dat voorkomt.

Kernbegrippen: compose.yaml, services, image, ports, volumes, environment, .env, restart, networks, depends_on, healthcheck, docker compose up -d, pull, logs, down, vervallen version:, Docker en ufw, DOCKER-USER, publiceren op 127.0.0.1, geheimen, docker.sock

Waarom compose?

Met docker run (les 9.2) start je één container met een lange regel opties. Een echte toepassing bestaat vaak uit meerdere containers: een programma, een database, een reverse proxy. Die regels onthouden en na een herstart of update opnieuw intypen, gaat een keer mis.

Docker Compose beschrijft alle containers van een toepassing in één bestand: compose.yaml. Je zet dat bestand in een eigen map, bijvoorbeeld /srv/status/compose.yaml, en beheert alles met docker compose. Het bestand is meteen je documentatie: wat draait er, welk image, welke poorten, waar staan de gegevens. Zet die map in je back-up (les 8.1), en je kunt de toepassing op een nieuwe server in een paar minuten terugzetten.

De plug-in heet docker compose (met een spatie, versie 2). Het oude losse programma docker-compose (met streepje) is vervangen; je ziet het nog in oude handleidingen. Ook het veld version: "3.8" bovenaan oude bestanden is vervallen: Compose gebruikt altijd het nieuwste schema en geeft bij version: alleen een waarschuwing.

Een compose-bestand lezen

Een voorbeeld met een programma en een PostgreSQL-database:

# /srv/notities/compose.yaml
services:
  app:
    image: ghcr.io/example/notities:1.4
    restart: unless-stopped
    ports:
      - "127.0.0.1:8081:8080"
    environment:
      DB_HOST: db
      DB_USER: notities
      DB_PASSWORD: ${DB_PASSWORD}
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:17
    restart: unless-stopped
    environment:
      POSTGRES_USER: notities
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U notities"]
      interval: 10s
      retries: 5

volumes:
  db-data:

Stap voor stap:

  • services: elke dienst wordt een container. De naam (app, db) is ook de hostnaam waarmee de containers elkaar vinden. Compose maakt voor de toepassing een eigen netwerk, dus app verbindt met de database op db:5432.
  • image: altijd met een versie-tag (les 9.1).
  • restart: wat Docker doet als de container stopt of de server herstart. unless-stopped start hem weer, behalve als jij hem bewust stopte. Zonder restart-beleid komt je toepassing na een herstart (les 4.2) niet terug.
  • ports: alleen de app publiceert een poort, en alleen op 127.0.0.1. De database publiceert niets: de app bereikt hem via het interne netwerk. Een database met ports: - "5432:5432" is een van de meest gemaakte fouten (zie hieronder).
  • environment: instellingen voor het programma. Het wachtwoord staat er niet letterlijk in, maar als ${DB_PASSWORD}.
  • volumes: het benoemde volume db-data houdt de database vast als de container vervangen wordt.
  • depends_on met healthcheck: de app start pas als de database echt klaar is, niet alleen gestart.

Geheimen in een .env-bestand

Compose leest variabelen als ${DB_PASSWORD} uit een bestand .env in dezelfde map:

# /srv/notities/.env
DB_PASSWORD=Kies-hier-een-lang-willekeurig-wachtwoord
anna@web1:/srv/notities$ sudo chmod 600 .env

Zo staat het wachtwoord niet in het compose-bestand dat je misschien in Git bewaart of op een forum plakt om hulp te vragen. Zet .env in .gitignore. Maak het wachtwoord met een wachtwoordmanager of met openssl rand -base64 24.

Dagelijks gebruik

Alle commando's voer je uit in de map met het compose-bestand:

anna@web1:/srv/notities$ sudo docker compose config     # bestand controleren, variabelen ingevuld
anna@web1:/srv/notities$ sudo docker compose up -d      # alles starten (of bijwerken naar het bestand)
anna@web1:/srv/notities$ sudo docker compose ps         # status per dienst
anna@web1:/srv/notities$ sudo docker compose logs -f app
anna@web1:/srv/notities$ sudo docker compose pull       # nieuwe images ophalen
anna@web1:/srv/notities$ sudo docker compose up -d      # containers vervangen door de nieuwe images
anna@web1:/srv/notities$ sudo docker compose down       # stoppen en containers verwijderen

down laat je volumes staan. down -v verwijdert ook de volumes van de toepassing, en daarmee je database. Typ dat alleen als je echt opnieuw wilt beginnen.

docker compose config is het equivalent van nginx -t en sshd -t: het laat zien hoe Compose je bestand begrijpt, met alle variabelen ingevuld, voordat je iets start. Let op dat je de uitvoer dan niet ergens plakt, want de wachtwoorden staan erin.

Docker gaat langs ufw heen

Nu het belangrijkste veiligheidspunt van deze module. In les 5.2 zette je ufw op "alles weigeren, behalve SSH, 80 en 443". Je zou verwachten dat een container met ports: - "5432:5432" dan niet bereikbaar is. Dat is niet zo.

De Docker-documentatie zegt het letterlijk: Docker en ufw gebruiken firewallregels op een manier die niet met elkaar samengaat. Als je een poort publiceert, zet Docker zelf regels in de firewall van de kernel. Die regels sturen het verkeer door naar de container (adresvertaling, DNAT) voordat de regels van ufw worden toegepast. Het pakketje gaat dus om ufw heen. ufw status toont niets bijzonders, terwijl de poort van heel internet bereikbaar is. Hetzelfde geldt voor firewalld.

Bovendien publiceert Docker zonder adres op 0.0.0.0 en [::], dus op IPv4 én IPv6 (les 9.1).

Drie manieren om het goed te doen, van eenvoudig naar ingewikkeld:

  1. Publiceer niets wat niet publiek hoeft. Databases, caches en interne diensten krijgen geen ports:. Containers in dezelfde compose-toepassing bereiken elkaar via het interne netwerk.
  2. Publiceer op loopback. "127.0.0.1:8081:8080". Dan kan alleen de server zelf erbij, en zet je een reverse proxy (nginx of Caddy, module 7) ervoor die op 443 luistert. Docker kan dit ook als standaard krijgen: een daemon-instelling laat alle gepubliceerde poorten zonder adres op 127.0.0.1 landen.
  3. Regels in de keten DOCKER-USER. Docker maakt een lege keten DOCKER-USER die vóór zijn eigen regels wordt doorlopen. Daar kun je zelf regels zetten, bijvoorbeeld "alleen dit IP-adres mag naar de gepubliceerde poorten". Dat is gevorderd werk met iptables of nftables, en makkelijk fout te doen. Voor de meeste kleine servers zijn 1 en 2 genoeg.

Controleer altijd van buitenaf, zoals in les 5.1: nc -vz -w 3 <ip> 5432 vanaf je laptop, of de cursuscontrole van opdracht Z5.1 en Z10.4.

Nog twee dingen die je niet doet

  • Geen /var/run/docker.sock in een container mounten, tenzij je precies weet waarom. Een container met toegang tot de Docker-socket kan zelf containers starten, ook een met de hele schijf van de host (les 9.2). Sommige hulpprogramma's (dashboards, automatische updaters) vragen erom; dat is in feite root geven aan dat programma.
  • Geen privileged: true of network_mode: host omdat een handleiding het zegt. Beide halen de afscherming van de container grotendeels weg. Alleen gebruiken als de maker uitlegt waarom het nodig is.

Samenvatting

  • Een compose-bestand (compose.yaml) beschrijft alle containers van een toepassing; het is meteen documentatie en hoort in je back-up.
  • Gebruik versie-tags, een restart-beleid, benoemde volumes, een healthcheck waar het kan, en geheimen in een .env met rechten 600.
  • docker compose config, up -d, ps, logs, pull, down; down -v verwijdert ook de volumes.
  • Gepubliceerde poorten gaan langs ufw en firewalld heen. Publiceer alleen wat publiek moet, en anders op 127.0.0.1 met een reverse proxy ervoor.
  • Geen docker.sock, privileged of network_mode: host zonder goede reden.

Quiz

1. ufw staat op "alles weigeren behalve 22, 80 en 443". Je compose-bestand heeft bij de database ports: - "5432:5432". Is de database van internet bereikbaar?



2. Hoe bereikt de container app de database in dezelfde compose-toepassing, als de database geen poort publiceert?



3. Na een herstart van de server draaien je containers niet meer. Wat ontbreekt waarschijnlijk in het compose-bestand?



4. Wat is het verschil tussen docker compose down en docker compose down -v?



5. Een dashboardprogramma vraagt in zijn handleiding om - /var/run/docker.sock:/var/run/docker.sock. Wat betekent dat voor de veiligheid?



Opdracht

Opdracht Z9.3: Een compose-bestand nakijken. In het voorbeeldbestand compose-nakijken.txt staat (als tekstbestand) het compose-bestand van een webwinkeltje met een app, een database, een cache en een beheertool. Er zitten zes fouten in die in deze les of in les 9.1 en 9.2 behandeld zijn. Geef per genummerd regelblok aan of het goed of fout is, en kies bij elke fout de juiste verbetering. Het programma vergelijkt met de vaste sleutel. Heb je zelf een compose-bestand, loop het daarna langs dezelfde punten.

Opdracht Z9.3: Een compose-bestand nakijken

Bestand bij deze opdracht: compose-nakijken.txt





















Wat je invult en wat we vinden, slaan we niet op. Je browser onthoudt je antwoorden en of je de opdracht hebt gehaald. Ben je aangemeld, dan bewaren we alleen dat je de opdracht (bijna) goed had in Mijn cursussen; daar kun je het ook verwijderen.

Verder lezen

Even geduld…

Stel je domeinen in

Regel per domein de nameservers, de koppeling aan je hosting en wie de eigenaar wordt.

Je domeinen laden…