c’t 12/2026
De gevolgen van artificiële intelligentie voor IT-security
Cover van
HTTPS-certificaten voor je thuisnetwerk instellen

HTTPS-certificaten voor je netwerk instellen: zo doe je dat

Wie zelf diensten host, klikt meestal door certificaatwaarschuwingen heen. Met een eigen domein, Pi-hole en Caddy krijgen al die diensten dankzij een DNS 01-challenge het felbegeerde slotje in de adresbalk.

Lees verder na de advertentie

HTTPS voor je netwerk zonder open poorten

Je kent het wel: “Je verbinding is niet privé” met nog de opmerking over dat kwaadwillende gebruikers mogelijk je gegevens proberen te stelen zoals wachtwoorden, berichten of creditcards. Wie zelf diensten host, is gewend waarschuwingen over zelfondertekende certificaten weg te klikken en maakt zich niet druk om het ontbrekende slotje in de adresbalk. Het is immers allemaal lokaal, dus transportversleuteling is niet per se nodig.

Dat zou je denken, maar het gaat niet altijd op. Verschillende toepassingen vereisen een beveiligde context, dus het overdrachtsprotocol Hypertext Transfer Protocol Secure (HTTPS). Daaronder vallen diensten die bijzonder gevoelige gegevens verwerken, zoals wachtwoordmanager Vaultwarden (een in Rust geschreven implementatie van de Bitwarden-server).

WebAuthn, de webstandaard achter passkeys, werkt alleen wanneer je een pagina via HTTPS opent. Sommige wachtwoordmanagers weigeren bovendien inloggegevens automatisch in te vullen als de verbinding niet versleuteld is.

Het kan ook lastig zijn om anderen uit te leggen waarom ze websites zonder slotje in de adresbalk in het algemeen moeten wantrouwen, terwijl dezelfde waarschuwingen bij hun eigen Nextcloud volstrekt onschuldig zouden zijn. En zou het niet veel netter zijn om diensten te openen via een domein met een geldig certificaat dan met een IP-adres plus een onhandig poortnummer dat je steeds vergeet?

Dankzij certificaatautoriteiten zoals Let’s Encrypt en het ACME-protocol is het tegenwoordig eenvoudiger dan ooit om gratis een TLS-certificaat te verkrijgen.

Wanneer een browser een website via HTTPS bezoekt, onderhandelt hij met de server over een versleutelde TLS-verbinding (Transport Layer Security). Aan de hand van een certificaat controleert de browser of hij met de juiste tegenpartij communiceert. Als het certificaat ontbreekt of ongeldig is, toont de browser een waarschuwing.

We laten zien hoe je al je diensten van een certificaat voorziet zonder poorten op je router te forwarden. Daarvoor heb je alleen een domein nodig bij een provider die een API aanbiedt voor de zogeheten DNS-01-challenge, bijvoorbeeld Cloudflare, en een Linux-systeem. Dat kan een Raspberry Pi, een virtuele machine (VM) of een mini-pc zijn. Voor deze handleiding heb je basiskennis van Linux en Docker nodig.

ACME en challenges

Let’s Encrypt is een CA (certificaatautoriteit) die gratis certificaten uitgeeft. Een client vraagt deze via het ACME-protocol (Automatic Certificate Management Environment) aan. In onze methode verzorgt reverse proxy Caddy dat.

Vaak wordt daarvoor de HTTP-01-challenge gebruikt: je bewijst aan de CA dat je de server beheert doordat Caddy een token opslaat onder example.com/.well-known/acme-challenge/, waarbij je example.com door je werkelijke domein moet vervangen.

Daarvoor moet de server vanaf internet bereikbaar zijn via poort 80. Bij diensten in een netwerk betekent dit het instellen van poortforwarding op de router. Wildcardcertificaten zoals *.example.org zijn op die manier niet mogelijk. Die zijn juist handig als je een hele verzameling diensten van certificaten wilt voorzien.

Bij de DNS-01-challenge bewijs je de CA daarentegen dat je de controle over het domein hebt. Daarvoor plaatst de ACME-client bij de DNS-provider een token als TXT-record onder _acme-challenge.example.com.

Let’s Encrypt controleert de vermelding via de nameservers van de provider. Zodra het certificaat is uitgegeven, wordt het TXT-record verwijderd. Omdat de CA nooit een verbinding met je eigen server opzet, hoeft die niet vanaf internet bereikbaar te zijn. De ACME-client, in ons geval Caddy, en de DNS-provider communiceren via een API. Daarvoor heb je een token nodig dat toestemming geeft om de DNS-zone te wijzigen.

Om clients in je netwerk een dienst onder zijn domeinnaam te laten openen, heb je een lokale DNS-server zoals Pi-hole nodig, die een A-record laat verwijzen naar het privé-IP-adres van de reverse proxy. Heb je een geavanceerde router waarmee je lokale DNS-records kunt instellen, dan heb je Pi-hole niet per se nodig.

Dat geldt ook als je al een andere lokale DNS-server zoals Unbound of Technitium (zie pagina 142) gebruikt. Als voorbeelddomein gebruiken we in dit artikel .home.example.com. Pi-hole vertaalt dit lokaal (split-horizon-DNS). Alle overige verzoeken stuurt Pi-hole door naar een publieke resolver.

Voorbereiding

Je hebt een eigen domein nodig bij een DNS-provider die een API voor het aanpassen van DNS-zones aanbiedt en waarvoor Caddy een module heeft. Wij gebruiken Cloudflare, maar als je je DNS-zone liever niet onderbrengt bij een Amerikaanse aanbieder, kun je bijvoorbeeld mijn.host, OVHcloud, transip of een andere EU-provider kiezen. Alle beschikbare Caddy-modules vind je op GitHub (zie link).

Bij Cloudflare maak je in de webinterface onder API Tokens een API-token aan. Geef voor je domein de rechten Zone read en Zone write. Bewaar het token op een veilige plek en behandel het als een wachtwoord.

Wij hebben Pi-hole en Caddy met Docker geïnstalleerd onder Ubuntu Server 26.04, maar het zou ook op andere distributies met Docker moeten werken. Het systeem moet een vast adres in het LAN hebben en de poorten 53, 80 en 443 moeten bereikbaar zijn. Onder Ubuntu moesten we daarvoor de stubresolver van systemd uitschakelen, zoals in de documentatie van Pi-hole staat beschreven. Voer het volgende commando uit:

Lees dit artikel verder

Lees over tech-trends en achtergronden, nieuwe apparatuur, software en toepassingen voor professioneel gebruik. Met c’t heb je altijd de juiste tech-informatie. Word abonnee en lees onbeperkt alle artikelen.
Bekijk abonnementen Al abonnee? Log in
sudo sh -c 'mkdir -p /etc/systemd/resolved.conf.d && printf "[Resolve]\nDNSStubListener=no\n" | tee /etc/systemd/resolved.conf.d/no-stub.conf'

Maak vervolgens de symlink /etc/resolv.conf aan die naar /run/systemd/resolve/resolv.conf verwijst, zodat de host nog steeds namen kan opzoeken:

sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf

Start daarna de dienst opnieuw met sudo systemctl restart systemd-resolved.

Pi-hole en Caddy installeren

Als eerste moet je Docker installeren. In ons geval van Linux gebruikten we het installatiescript curl -fsSL https://get.docker.com -o get-docker.sh en voeren we het uit via sudo sh get-docker.sh. en voegen we de huidige gebruiker toe aan de groep docker met sudo usermod -aG docker $USER.

Daarna configureer je Pi-hole en Caddy. Om dat eenvoudiger te maken, hebben we in een GitHub-repository een aantal sjablonen verzameld die je kunt bewerken (zie link). Kloon eerst de repository en ga naar de nieuwe map:

git clone https://github.com/ndi-ct/pihole-caddy
cd pihole-caddy

Kopieer daarna het sjabloon .env.example en bewerk het met een teksteditor zoals nano:

cp .env.example .env

Vul in het bestand .env achter CF_API_TOKEN= je Cloudflare-token in. Achter PIHOLE_PASSWORD= stel je een sterk wachtwoord in voor het beheerderspaneel van Pi-hole.

De officiële Caddy-image bevat geen modules voor de DNS-challenge via Cloudflare. Daarom voegt een Dockerfile in de map caddy met het hulpprogramma xcaddy de benodigde module (github.com/caddy-dns/cloudflare) toe:

FROM caddy:2-builder AS builder
RUN xcaddy build --with github.com/caddy-dns/cloudflare

FROM caddy:2
COPY --from=builder /usr/bin/caddy /usr/bin/caddy

Het bestand docker-compose.yaml bevat het bouwplan voor de combinatie van containers:

services:
  pihole:
    image: pihole/pihole:latest
    ports:
      - "53:53/tcp"
      - "53:53/udp"
    environment:
      TZ: Europe/Amsterdam
      FTLCONF_webserver_api_password: ${PIHOLE_PASSWORD}
      FTLCONF_dns_listeningMode: all
      FTLCONF_dns_upstreams: 9.9.9.9;149.112.112.112
      FTLCONF_misc_dnsmasq_lines: address=/home.example.com/192.168.1.160;local=/home.example.com/
    volumes:
      - ./pihole:/etc/pihole
    restart: unless-stopped
  caddy:
    build: ./caddy
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    environment:
      CF_API_TOKEN: ${CF_API_TOKEN}
    volumes:
      - ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
    restart: unless-stopped
volumes:
  caddy_data:

Bewerk ook dit bestand met een teksteditor. Vervang in de regel met de sleutel FTLCONF_misc_dnsmasq_lines: op twee plaatsen het domein home.example.com door je eigen domein. Vervang het voorbeeld-IP-adres 192.168.1.160 door het IP-adres van het systeem waarop Pi-hole en Caddy draaien. Deze omgevingsvariabele definieert het wildcard-DNS-record.

Pi-hole beantwoordt vervolgens aanvragen voor alle subdomeinen van home.example.com, zoals nextcloud.home.example.com, met IP-adres 192.168.1.160. De toevoeging local= markeert het domein als uitsluitend lokaal en voorkomt dat de aanvraag bij de upstreamresolver terechtkomt. Met FTLCONF_dns_upstreams: 9.9.9.9;149.112.112.112 is Quad9 als publieke resolver ingesteld, maar je kunt ook een andere naar keuze gebruiken.

Configureer ten slotte Caddy door het bestand Caddyfile in de map caddy te bewerken:

{
    email [email protected]
}

*.home.example.com {
    tls {
        dns cloudflare {env.CF_API_TOKEN}
        resolvers 9.9.9.9 1.1.1.1
    }
    @pihole host pihole.home.example.com
    handle @pihole {
        redir / /admin/
        reverse_proxy pihole:80
    }
    # @nginx host nginx.home.example.com
    # handle @nginx {
    #     reverse_proxy 192.168.1.150:80
    # }
    handle {
        abort
    }
}

De Caddyfile bevat een blok voor het wildcarddomein *.home.example.com. Vervang dat door je eigen domein. De matchers eronder, zoals @pihole of het voorbeeld @nginx, verdelen inkomende aanvragen over de juiste hosts. Voor Pi-hole werkt dat zonder IP-adres, omdat Pi-hole en Caddy in hetzelfde Docker-netwerk zitten. Voor andere diensten in je netwerk kun je het voorbeeld @nginx gebruiken.

Een aanvraag vanuit het netwerk aan nginx.home.example.com stuurt Caddy door naar 192.168.1.150:80 en biedt de Nginx-webserver via HTTPS aan. Vervang het Nginx-voorbeeld door je eigen diensten door de hekjes te verwijderen en hostnamen, IP-adres en poort aan te passen.

Nu is alles voorbereid. Met docker compose up -d --build bouw je de Caddy-image met de DNS-module. Met docker compose logs -f caddy kun je volgen wat Caddy doet. Na een tot twee minuten zou de reverseproxy certificate obtained successfully moeten melden. Of Pi-hole zoals bedoeld op willekeurige subdomeinen van home.example.com antwoordt, controleer je met dig test.home.example.com @192.168.1.160 +short. Dat commando moet het IP-adres van de Pi-hole-server teruggeven.

Clients omschakelen

Nu moet je een keuze maken. Om de namen in het netwerk te kunnen opzoeken, moeten clients Pi-hole als DNS-server gebruiken. Als jij de enige of voornaamste gebruiker van de diensten in het netwerk bent, volstaat het om Pi-hole op je eigen apparaten als DNS-server in te stellen.

Zijn het veel apparaten of gebruiken anderen de diensten ook, dan is het eenvoudiger om Pi-hole in de routerinstellingen via DHCP als DNS-server aan alle clients bekend te maken. DNS-verzoeken van alle clients in het netwerk komen dan bij Pi-hole terecht.

Voor een Fritzbox is de procedure uitvoerig beschreven in de documentatie van Pi-hole (zie link). Let op: kies je deze route, dan werkt de name resolving in het hele netwerk niet meer wanneer je Pi-hole-server onbereikbaar is.

Klaar

Je hebt nu een certificaat voor *.home.example.com voor alle diensten in je netwerk. Probeer het uit door in je browser https://pihole.home.example.com te openen. De browser zou moeten bevestigen dat de verbinding versleuteld is en een certificaat van Let’s Encrypt tonen. Log in met het wachtwoord dat je in .env hebt ingesteld.

Aan de configuratie hoef je verder niets meer te veranderen. Om aanvullende zelfgehoste diensten in het netwerk van het certificaat te voorzien, voeg je ze eenvoudig toe aan de Caddyfile. Voer daarna in de map pihole-caddy het commando docker compose restart caddy uit, zodat Caddy de nieuwe configuratie inleest.

Niklas Dierking en Alieke van Sommeren

GitHub Caddy-modules

GitHub-repository

Pi-hole documentatie

Inspiratie in je mailbox

Blijf bij op IT-gebied en verbreed je expertise. Ontvang elke week artikelen over de laatste tech-ontwikkelingen, toepassingen, nieuwe hard- en software én ontvang tips en aanbiedingen.

Loginmenu afsluiten