DNS locale di sviluppo con dnsmasq e split DNS su macOS

Questo articolo riassume e mette in ordine l'intero percorso di configurazione affrontato in una LAN con: un server Ubuntu usato come DNS locale e un Mac che ospita i container Docker dei progetti di sviluppo. L'obiettivo è risolvere domini di sviluppo (TLD .test) senza modificare il server e senza impostare il DNS locale come primo server globale sul Mac, grazie allo split DNS di macOS.

Scenario e obiettivo

  • Server Ubuntu (DNS): (Ubuntu 24.04.3 LTS)
  • MacOS (Docker): (container e reverse proxy sul Mac)

Il requisito chiave è che la risoluzione DNS di questi domini punti al Mac (dove gira Docker), mentre per il resto di Internet il Mac può continuare a usare i DNS usuali (router, Google, Cloudflare, ecc.).

Perché usare dnsmasq sul server

dnsmasq è un DNS forwarder leggero, ideale per ambienti di sviluppo e reti locali. In questo scenario viene usato come:

  • risolutore di domini interni (override locali e wildcard)
  • forwarder verso DNS pubblici per tutto ciò che non è locale
  • cache DNS (opzionale) per ridurre latenza e query ripetute

Installazione di dnsmasq su Ubuntu 24.04

sudo apt update
sudo apt install dnsmasq
systemctl status dnsmasq

Configurazione di dnsmasq per i domini .test

Best practice: aggiungere un file in /etc/dnsmasq.d/ invece di editare direttamente /etc/dnsmasq.conf.

sudo nano /etc/dnsmasq.d/dev-domains.conf

Configurazione consigliata: ascolto su localhost e sull'IP LAN del server, forwarding verso DNS pubblici e record address= che puntano al Mac (es: 192.168.1.4).

listen-address=127.0.0.1,192.168.1.8
bind-interfaces
no-resolv

server=1.1.1.1
server=8.8.8.8

address=/app.test/192.168.1.4
address=/app2.test/192.168.1.4

Nota: address=/dominio/ si comporta come wildcard per tutti i sottodomini di quel nome; nel caso si voglia una wildcard più ampia si può ridurre il numero di righe (ad esempio usando un dominio base e gestendo i sottodomini lato reverse proxy), ma in questa configurazione si è scelto l'elenco esplicito.

Riavvio e abilitazione:

sudo systemctl restart dnsmasq
sudo systemctl enable dnsmasq

Test sul server (query diretta):

dig @127.0.0.1 app.test
dig @192.168.1.8 app.test

Perché il DNS locale dovrebbe essere “primo” (e perché non vogliamo farlo)

In una configurazione DNS tradizionale, il client tenta il primo DNS e passa ai successivi solo quando il primo non risponde. Se il primo DNS risponde con NXDOMAIN (dominio inesistente), il client di solito non consulta gli altri. Questo rende fragile mettere il DNS locale come secondo.

Tuttavia macOS offre un meccanismo più evoluto: la possibilità di definire resolver per dominio (split DNS). Questo permette di non toccare l'ordine globale dei DNS e di applicare il server locale solo a un TLD o a un dominio specifico.

Split DNS su macOS con /etc/resolver

macOS legge file di configurazione in /etc/resolver/. Ogni file definisce un resolver dedicato a un dominio o a un TLD. Per indirizzare tutto il TLD .test verso il server dnsmasq:

sudo mkdir -p /etc/resolver
sudo nano /etc/resolver/test

Contenuto minimo consigliato:

nameserver 192.168.1.8
port 53
timeout 5

Permessi consigliati (per evitare letture fallite o file non considerati):

sudo chmod 644 /etc/resolver/test
sudo chown root:wheel /etc/resolver/test

Flush della cache DNS di macOS:

sudo dscacheutil -flushcache
sudo killall -HUP mDNSResponder

Verifica lato macOS: lo strumento giusto

La verifica più affidabile per capire quali resolver sono attivi su macOS è:

scutil --dns

Nell'output ci si aspetta di trovare una sezione con dominio test e nameserver 192.168.1.16. Questo indica che lo split DNS è attivo e che tutte le query per .test verranno inviate a dnsmasq, indipendentemente dall'ordine dei DNS impostati nell'interfaccia di rete.

Il punto critico: perché dig può risultare “inaffidabile” su macOS

Durante i test, può capitare di eseguire:

dig app.test

e ottenere una risposta NXDOMAIN da un DNS pubblico (ad esempio 8.8.4.4). Questo succede perché, su macOS, dig non sempre usa lo stesso percorso di risoluzione del sistema (gestito da mDNSResponder) e può quindi ignorare le regole in /etc/resolver.

Per testare dnsmasq in modo certo con dig, bisogna interrogare esplicitamente il nameserver:

dig @192.168.1.8 app.test

Per testare la risoluzione effettiva usata dal sistema operativo, sono più affidabili:

ping app.test
curl http://app.test

Integrazione con Docker sul Mac: cosa succede dopo il DNS

Una volta che i domini .test risolvono verso 192.168.1.4, la gestione del routing HTTP/HTTPS è responsabilità del Mac (reverse proxy) e dei container Docker. In pratica:

  1. Il client risolve app.test in 192.168.1.4 tramite dnsmasq.
  2. La richiesta HTTP/HTTPS arriva al Mac sull'IP LAN.
  3. Un reverse proxy (Traefik, Nginx, Caddy, ecc.) inoltra verso i container in base all'host.

Questo approccio separa nettamente i ruoli: DNS per l'indirizzamento, reverse proxy per la pubblicazione dei servizi, Docker per l'esecuzione delle applicazioni.

Troubleshooting rapido

  • Verificare che macOS veda il resolver:
    scutil --dns
  • Forzare un test puntando al DNS locale:
    dig @192.168.1.8 app.test
  • Verificare l'effettiva risoluzione di sistema:
    ping app.test
    curl -v http://app.test
  • Flush cache DNS macOS:
    sudo dscacheutil -flushcache
    sudo killall -HUP mDNSResponder
  • Log del servizio dnsmasq sul server:
    journalctl -u dnsmasq -n 100

Conclusione

La soluzione finale è composta da due parti indipendenti e pulite:

  • Sul server Ubuntu: dnsmasq risolve i domini di sviluppo .test verso il Mac e inoltra il resto verso DNS pubblici.
  • Sul Mac: lo split DNS tramite /etc/resolver/test invia solo le query per .test a dnsmasq, senza cambiare l'ordine dei DNS globali.

L'elemento che spesso trae in inganno è la verifica con dig: su macOS è preferibile usare scutil --dns e, se si usa dig, specificare sempre il nameserver per testare in modo deterministico.

Torna su