Module 9 · 9.4
Recepten: Uptime Kuma en Vaultwarden
Module 9: Docker en docker compose · Leestijd: 13 minuten
Leerdoel: Na deze les kun je Uptime Kuma en Vaultwarden met docker compose achter Caddy draaien, met https, dichte registratie, back-up van de volumes en een vaste manier van bijwerken.
Kernbegrippen: reverse proxy-container, Caddyfile, automatische https, Uptime Kuma, Vaultwarden, DOMAIN, SIGNUPS_ALLOWED, ADMIN_TOKEN, Argon2, .env, websockets, volumes back-uppen, docker compose pull, eigen header
Wat we bouwen
Twee handige diensten op je eigen server, elk op een eigen subdomein:
- Uptime Kuma op
status.example.com: een bewakingsprogramma met een webinterface dat je websites, poorten en certificaten in de gaten houdt en je een melding stuurt als er iets uitvalt (les 8.3). Let op: een bewaking die op dezelfde server draait als wat hij bewaakt, ziet niet dat die hele server uitvalt. Gebruik hem dus voor je andere sites en diensten, of zet hem op een tweede server. - Vaultwarden op
kluis.example.com: een lichte, onafhankelijke serverversie die werkt met de apps en browserextensies van Bitwarden. Een wachtwoordkluis is gevoelige software: alleen doen als je bereid bent hem goed bij te houden en te back-uppen. Het is ook prima om gewoon een wachtwoordmanager van een aanbieder te gebruiken.
Ervoor zetten we Caddy in een container: die neemt poort 80 en 443 aan, regelt zelf de certificaten van Let's Encrypt (les 7.3 en 7.4) en stuurt het verkeer door naar de juiste container. Uptime Kuma en Vaultwarden publiceren zelf geen poorten (les 9.3).
internet ──443──> caddy ──> uptime-kuma:3001
└─> vaultwarden:80Vooraf
- Docker Engine met de compose-plug-in (les 9.2).
- Twee DNS-records die naar je server wijzen, A en AAAA (les 7.1):
status.example.comenkluis.example.com. - Poort 80 en 443 vrij op de server. Draait er al een nginx of Caddy van het systeem op die poorten, dan kan de Caddy-container ze niet gebruiken. Kies dan: of je stopt de systeemwebserver, of je laat die het proxywerk doen en publiceert de containers op
127.0.0.1(les 9.3). In dit recept doet de Caddy-container het. - In ufw 80/tcp, 443/tcp en voor HTTP/3 ook 443/udp open (les 5.2). Strikt genomen gaat Docker langs ufw heen, maar zo klopt je firewalloverzicht met de werkelijkheid.
De bestanden
Alles komt in één map, /srv/diensten/:
/srv/diensten/
├── compose.yaml
├── Caddyfile
└── .envcompose.yaml
services:
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
uptime-kuma:
image: louislam/uptime-kuma:2
restart: unless-stopped
volumes:
- uptime-kuma:/app/data
vaultwarden:
image: vaultwarden/server:1.37.5
restart: unless-stopped
environment:
DOMAIN: "https://kluis.example.com"
SIGNUPS_ALLOWED: "true"
ADMIN_TOKEN: ${VW_ADMIN_TOKEN}
volumes:
- ./vw-data:/data
volumes:
caddy-data:
caddy-config:
uptime-kuma:Kijk vóór je begint welke versie van Vaultwarden de nieuwste is (de releases op GitHub) en zet die in de tag. Alleen Caddy publiceert poorten; die moeten publiek zijn. De volumes caddy-data (certificaten), uptime-kuma en de map vw-data bevatten alles wat je moet bewaren.
Caddyfile
{
email beheer@example.com
}
status.example.com {
reverse_proxy uptime-kuma:3001
header {
X-Weboke-Cursus "jouw-code-uit-de-opdracht"
defer
}
}
kluis.example.com {
reverse_proxy vaultwarden:80
}Caddy haalt voor beide namen vanzelf een certificaat en stuurt http door naar https. uptime-kuma en vaultwarden zijn de servicenamen uit het compose-bestand: Caddy vindt ze op het interne netwerk. Het header-blok zet een extra kopregel in elk antwoord van status.example.com; die gebruik je in de opdracht als bewijs dat jouw proxy-container draait. Met defer wordt de header gezet nadat de achterliggende dienst zijn antwoord heeft gegeven. Wil je ook HSTS en de andere beveiligingsheaders uit les 7.4, zet ze dan in hetzelfde blok.
Vaultwarden veilig instellen
Vier dingen maken het verschil tussen een veilige en een gevaarlijke kluis.
Eén: alleen via https. De webkluis van Bitwarden gebruikt de cryptografie van de browser, en browsers geven die alleen vrij op https-pagina's. Zonder https werkt het dus niet goed, en dat is maar goed ook. De Vaultwarden-documentatie raadt een reverse proxy aan voor https, zoals Caddy hier. Zet DOMAIN op het volledige https-adres.
Twee: registratie dicht. Met SIGNUPS_ALLOWED: "true" kan iedereen die de pagina vindt een account maken op jouw server. Start dus, maak meteen je eigen account(s), zet de waarde op "false" en voer docker compose up -d opnieuw uit. Wil je later iemand toevoegen, gebruik dan de uitnodigingsfunctie in het beheerscherm.
Drie: een gehasht beheertoken. Het beheerscherm (/admin) is afgeschermd met ADMIN_TOKEN. Zet daar geen leesbaar wachtwoord in, maar een Argon2-hash. Die maak je met Vaultwarden zelf:
anna@web1:/srv/diensten$ sudo docker compose exec vaultwarden /vaultwarden hash
Password:
Confirm Password:
ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$...'Zet die regel in .env (dus VW_ADMIN_TOKEN='$argon2id$...', met enkele aanhalingstekens) en maak het bestand alleen voor root leesbaar: sudo chmod 600 .env. In een .env-bestand hoef je de $-tekens niet te verdubbelen; zet je de hash direct onder environment: in het compose-bestand, dan wel (elke $ wordt $$), anders probeert Compose ze als variabelen in te vullen. Inloggen op /admin doe je met het wachtwoord dat je typte, niet met de hash. Let op: instellingen die je in het beheerscherm opslaat, komen in vw-data/config.json en gaan dan vóór de omgevingsvariabelen.
Vier: back-up en herstel. Alles staat in vw-data: de database, bijlagen en sleutels. Neem die map mee in je restic-back-up (les 8.2), en volg voor de database de back-upaanwijzingen in de Vaultwarden-wiki, zodat je geen half geschreven bestand kopieert. Test het terugzetten (opdracht O8.2): een kluis zonder werkende back-up is een risico voor al je wachtwoorden.
Meldingen tussen apparaten (websockets) werken bij recente versies van Vaultwarden over dezelfde poort; Caddy geeft ze zonder extra instelling door.
Uptime Kuma instellen
Na docker compose up -d open je https://status.example.com. Bij het eerste bezoek vraagt Uptime Kuma je een beheerdersaccount aan te maken. Doe dat meteen: zolang dat account er niet is, kan iedereen die de pagina vindt het aanmaken. Wil je dat risico helemaal niet, laat het status-blok dan eerst weg uit de Caddyfile, maak het account via een SSH-tunnel (les 2.3) en zet het blok daarna terug.
Daarna voeg je monitors toe: een HTTP-monitor voor je website, een TCP-monitor voor een poort, een certificaatwaarschuwing, en een meldkanaal (mail, een chatprogramma of een pushdienst). Zet ook tweestapsverificatie aan in de instellingen van Uptime Kuma.
Starten en controleren
anna@web1:/srv/diensten$ sudo docker compose up -d
anna@web1:/srv/diensten$ sudo docker compose ps
NAME SERVICE STATUS PORTS
diensten-caddy-1 caddy Up 2 minutes 0.0.0.0:80->80/tcp, [::]:80->80/tcp, 0.0.0.0:443->443/tcp, ...
diensten-uptime-kuma-1 uptime-kuma Up 2 minutes (healthy)
diensten-vaultwarden-1 vaultwarden Up 2 minutes (healthy)
anna@web1:/srv/diensten$ sudo docker compose logs caddy | grep -i "certificate obtained"
anna@web1:~$ curl -sI https://status.example.com | grep -i x-weboke
x-weboke-cursus: jouw-code-uit-de-opdrachtAlleen Caddy heeft poorten. Lukt het certificaat niet, dan staat in de logs van Caddy waarom: meestal wijst de DNS nog niet naar de server, of is poort 80 niet bereikbaar (les 7.3).
Bijwerken
Een container wordt niet door unattended-upgrades bijgewerkt (les 4.2). Maak er een vast moment van, bijvoorbeeld elke twee weken:
anna@web1:/srv/diensten$ sudo docker compose pull
anna@web1:/srv/diensten$ sudo docker compose up -d
anna@web1:/srv/diensten$ sudo docker image prunepull haalt de nieuwste images binnen de tag die je gekozen hebt (dus caddy:2 blijft binnen versie 2). Voor Vaultwarden met een versienummer pas je eerst de tag aan na het lezen van de release notes. Lees bij een nieuwe hoofdversie altijd de release notes: soms moet er iets aan je instellingen veranderen. Maak vóór een grote update een back-up van de volumes.
Volg ook de beveiligingsmeldingen van de makers. Een kwetsbaarheid in Vaultwarden of Caddy wordt alleen opgelost als jij de nieuwe versie ophaalt.
In de praktijk
Dit recept werkt op elke VPS met Docker, bij WebOké of bij een andere aanbieder. Het is zelfhulp: de aanbieder van je VPS beheert niets binnen je containers. Maak een snapshot voordat je het voor het eerst opzet, dan kun je met een gerust hart experimenteren.
Samenvatting
- Eén compose-bestand met Caddy als reverse proxy-container; alleen Caddy publiceert poort 80 en 443, de diensten niets.
- Caddy regelt certificaten en https automatisch en kan met
headereigen kopregels zetten. - Vaultwarden: https verplicht,
DOMAINop het https-adres, registratie dicht na je eigen account,ADMIN_TOKENals Argon2-hash in een.envmet rechten 600. - Uptime Kuma: meteen het beheerdersaccount aanmaken en tweestapsverificatie aanzetten; bewaak er vooral andere servers en sites mee.
- Back-up de volumes en de map
vw-data, en werk bij metdocker compose pullenup -dop een vast moment.
Quiz
Opdracht
Opdracht Z9.4: Bewijs dat je container draait. Haal in de opdracht je persoonlijke code op en zet die in de Caddyfile van je Caddy-container als header X-Weboke-Cursus voor een subdomein van jou (zoals status.<jouwdomein>). Herlaad Caddy en vul de hostnaam in. Het programma haalt https://<hostnaam>/ op en controleert dat het certificaat geldig is en dat de header precies jouw code bevat. Plak daarnaast de uitvoer van docker compose ps en beantwoord welke dienst als enige poorten publiceert.
Verder lezen
- Wachtwoordmanager en passkeys: zo regel je je inloggen veilig
- Uptime Kuma (installatie met Docker): https://github.com/louislam/uptime-kuma
- Vaultwarden-wiki, Using Docker Compose: https://github.com/dani-garcia/vaultwarden/wiki/Using-Docker-Compose
- Vaultwarden-wiki, Enabling admin page: https://github.com/dani-garcia/vaultwarden/wiki/Enabling-admin-page
- Vaultwarden-wiki, Enabling HTTPS: https://github.com/dani-garcia/vaultwarden/wiki/Enabling-HTTPS
- Caddy-documentatie, header: https://caddyserver.com/docs/caddyfile/directives/header
