In questo articolo vedremo come configurare un accesso HTTPS con certificato reale per i pannelli di amministrazione di Pi-hole e FritzBox perché ormai la sicurezza mi viene da gestirla anche sul frigo e forno a microonde!
Obiettivo
L'obiettivo è accedere via HTTPS, tramite Let's Encrypt e senza warning nel browser, ai pannelli di amministrazione. Questo pur trattandosi di servizi privi di certificato SSL proprio, accessibili solo dalla LAN locale e senza alcuna esposizione verso internet, ovvero senza port forwarding sul router.
Architettura
Lo schema del flusso di rete:
Client (telefono/notebook)
│ 1. richiesta DNS per <host>.miodominio.duckdns.org
▼
Pi-hole (x.x.x.x) ──▶ override locale: risponde con l'IP del Raspberry stesso
│ 2. connessione HTTPS
▼
Caddy (stesso Raspberry, porte 80/443)
│ termina il TLS con certificato Let's Encrypt reale
├──▶ pihole.miodomionio.duckdns.org → localhost:8080 (webserver interno di Pi-hole)
└──▶ fritz.miodominio.duckdns.org → https://y.y.y.y (FritzBox, self-signed lato backend)
Il certificato viene emesso e rinnovato in autonomia da Caddy tramite challenge DNS-01 verso l'API di DuckDNS. Nessuna porta esposta in ingresso è mai necessaria, nemmeno durante l'emissione o il rinnovo.
Dati di riferimento
- Sottodominio DuckDNS: miodominio.duckdns.org
- Host Pi-hole: pihole.miodominio.duckdns.org
- Host FritzBox: fritz.miodominio.duckdns.org
- IP Raspberry (Pi-hole + Caddy): x.x.x.x
- IP LAN FritzBox: y.y.y.y
- Hardware: Raspberry Pi Zero 2 W, aarch64
- Pi-hole: v6.4.3, installazione bare metal
- Porta webserver Pi-hole (post-modifica): 8080 (liberate 80/443 per Caddy)
Cosa è stato fatto
1. Pi-hole: Verifica porte 80/443
Avendo Pi-hole esposto già sulla 8080 no SSL, non ho dovuto liberare/riconfigurare nulla.
2. Compilazione di Caddy con plugin DuckDNS
Caddy di base non include plugin di terze parti: è stato ricompilato sulla mia workstation Fedora più potente del Pi Zero 2 W e poi trasferito via SCP.
# Su Fedora
sudo dnf install golang -y
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 ~/go/bin/xcaddy build --with github.com/caddy-dns/duckdns
# Trasferimento
scp ./caddy crow2k@<ip-raspberry>:/tmp
Configurazione sul Raspberry:
sudo mv caddy /usr/local/bin/caddy
sudo chown root:root /usr/local/bin/caddy
sudo setcap cap_net_bind_service=+ep /usr/local/bin/caddy
3. Utente e directory dedicate
Creazione dell'ambiente isolato:
sudo groupadd --system caddy
sudo useradd --system --gid caddy --home /var/lib/caddy --shell /usr/sbin/nologin caddy
sudo mkdir -p /etc/caddy /var/lib/caddy
sudo chown -R caddy:caddy /var/lib/caddy
4. File di configurazione
Il file /etc/caddy/Caddyfile è stato configurato con proprietario root:caddy e permessi 640:
(duckdns_tls) {
tls {
dns duckdns {env.DUCKDNS_TOKEN}
resolvers 1.1.1.1
}
}
pihole.miodominio.duckdns.org {
import duckdns_tls
reverse_proxy localhost:8080
}
fritz.miodominio.duckdns.org {
import duckdns_tls
reverse_proxy https://y.y.y.y {
transport http {
tls_insecure_skip_verify
}
}
}
Nota: La voce resolvers 1.1.1.1 è stata aggiunta perché il controllo di propagazione DNS di Caddy contatta direttamente i nameserver autoritativi di duckdns.org, ma un IP specifico risultava in timeout dalla rete locale. Forzare un resolver pubblico affidabile ha risolto il problema.
Il file /etc/caddy/caddy.env è stato configurato con proprietario caddy:caddy e permessi 600:
DUCKDNS_TOKEN=LoTroviUnaVoltaLoggatoSuDuckDNS
5. Servizio systemd
Contenuto di /etc/systemd/system/caddy.service:
[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target
[Service]
Type=notify
User=caddy
Group=caddy
ExecStart=/usr/local/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/local/bin/caddy reload --config /etc/caddy/Caddyfile --force
EnvironmentFile=/etc/caddy/caddy.env
TimeoutStopSec=5s
LimitNOPROC=512
LimitNOFILE=1048576
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
Attivazione del servizio:
sudo systemctl daemon-reload
sudo systemctl enable --now caddy
I certificati emessi vengono salvati e rinnovati automaticamente in:
/var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/<host>/
6. Override DNS locale su Pi-hole
Nel percorso Settings → Local DNS → DNS Records, sono stati aggiunti:
pihole.miodominio.duckdns.org→x.x.x.xfritz.miodominio.duckdns.org→x.x.x.x
Senza questo passaggio i due nomi risolverebbero all'IP pubblico gestito da DuckDNS, non raggiungibile dall'interno senza port forwarding.
Guida operativa: aggiungere un nuovo host
1. Aggiungere il blocco al Caddyfile (lo snippet duckdns_tls è già definito):
nuovoservizio.lulibalan.duckdns.org {
import duckdns_tls
reverse_proxy 192.168.1.xx:porta
}
2. Validare e ricaricare senza downtime:
sudo -u caddy /usr/local/bin/caddy validate --config /etc/caddy/Caddyfile --envfile /etc/caddy/caddy.env
sudo systemctl reload caddy
3. Aggiungere l'override su Pi-hole:
Nel percorso Settings → Local DNS → DNS Records, aggiungere il nuovo host puntando all'IP x.x.x.x.
Troubleshooting incontrato
Falsa pista: DNS4EU (Protective + Child Protection)
Il FritzBox usa i resolver DNS4EU (86.54.11.12), sospettati inizialmente di filtrare duckdns.org. Confrontando le risposte, DNS4EU restituiva gli stessi IP del resolver pubblico 1.1.1.1, escludendo il sinkholing.
Causa reale: DNS-over-HTTPS del browser
In due occasioni (telefono e notebook), i due hostname non risultavano raggiungibili. La causa era il DoH del browser (Chrome/Firefox) che interrogava un resolver interno, bypassando il resolver di sistema e l'override locale su Pi-hole.
Diagnosi bypassando il browser:
# Deve dare l'IP interno
nslookup <host>.miodominio.duckdns.org x.x.x.x
# Deve confermare lo stesso risultato
resolvectl query <host>.miodominio.duckdns.org
Se i comandi funzionano ma il browser no, la causa è il DoH.
Fix Chrome: Andare in chrome://settings/security, sezione "Usa DNS sicuro", e impostare su Automatico per rispettare il resolver di rete.
Fix Firefox: Andare in about:preferences#privacy, sezione "DNS sicuro", e impostare su Off o "Usa la protezione predefinita del provider".
Nota: Questa impostazione va controllata su ogni dispositivo, poiché i profili sincronizzati possono propagare la stessa configurazione DoH su più device.
Persistenza del DNS su Fedora (workstation)
Anche con il DoH del browser corretto, il DNS di sistema della workstation Fedora (x.x.x.x, Pi-hole) risultava corretto solo a runtime, ma non sopravviveva a un riavvio o a una riconnessione della rete Wi-Fi.
Perché si è resa necessaria la modifica: su Fedora, NetworkManager gestisce sia l'interfaccia di rete che la configurazione DNS passata a systemd-resolved. Per ogni connessione (ConnessioneAttiva), a meno di indicazioni esplicite, NetworkManager applica automaticamente i parametri DNS ricevuti via DHCP dal FritzBox, che distribuisce se stesso come server DNS. Se il DNS verso Pi-hole era stato impostato solo a runtime (es. con resolvectl o dalla GUI senza salvare sul profilo), quel valore:
- non veniva scritto nel profilo di connessione persistente, quindi si perdeva ad ogni riavvio o riconnessione, tornando al DNS del FritzBox fornito via DHCP;
- anche se scritto, senza disattivare esplicitamente il DNS automatico, NetworkManager avrebbe comunque unito il valore manuale a quello ricevuto via DHCP invece di sostituirlo, riesponendo la workstation allo stesso problema di risoluzione già visto in precedenza (nomi risolti all'IP pubblico di DuckDNS invece che a quello interno).
Fix — reso persistente nel profilo NetworkManager:
nmcli con mod ConnessioneAttiva ipv4.dns "x.x.x.x"
nmcli con mod ConnessioneAttiva ipv4.ignore-auto-dns yes
nmcli con up ConnessioneAttiva
La configurazione viene scritta in /etc/NetworkManager/system-connections/ConnessioneAttiva.nmconnection e da quel momento sopravvive sia a nmcli con up che a un riavvio completo.
Verifica:
resolvectl status wlp10s0
Il comando deve mostrare x.x.x.x come DNS Server principale.
Commenti
Posta un commento