La configurazione DNS rompe ancora la produzione
DNS sembra semplice finché non si rompe. Come aggiornamenti macOS, resolv.conf, mDNS e il TLD .internal causano downtime in produzione che gli sviluppatori non si aspettano.

L'ultimo rilascio di macOS di Apple ha rotto le impostazioni DNS personalizzate di migliaia di sviluppatori. In particolare, ha iniziato a intercettare le query per il TLD .internal — che molte aziende usano per le risorse di rete private — e a instradarle verso mDNS (multicast DNS) invece che verso il resolver DNS configurato. Servizi che funzionavano senza problemi da anni improvvisamente non erano più raggiungibili. La soluzione non era ovvia. I messaggi di errore erano poco utili. E, come sempre con i problemi DNS, i sintomi sembravano tutto tranne DNS.
Questa è una storia sulla configurazione DNS — un argomento che ogni sviluppatore incontra, che pochi capiscono davvero a fondo e che tutti sottovalutano finché non rompe qualcosa in produzione alle 2 di notte.
Il caso del TLD .internal
Molte organizzazioni usano .internal come TLD privato per i servizi interni: api.internal, db.staging.internal, grafana.internal. Sembrava una scelta sicura: non è un TLD pubblico, indica chiaramente 'questo è interno' e viene usato così da decenni.
Poi il nuovo sistema operativo di Apple ha deciso che .internal doveva essere gestito tramite mDNS — lo stesso protocollo che risolve myprinter.local sulla rete di casa. Quando il Mac prova a risolvere api.internal, invece di chiedere al server DNS configurato, invia una query mDNS sulla rete locale. Il tuo server DNS non vede mai la query. La risposta è o nulla (timeout) oppure l'indirizzo sbagliato, se qualcosa sulla rete locale risponde a quel nome.
I sintomi: curl api.internal resta bloccato per 5 secondi e poi fallisce. Le connessioni SSH verso host interni scadono. Gli utenti VPN non riescono a raggiungere le risorse interne. I container Docker non riescono a risolvere i nomi dei servizi. E dato che il fallimento si presenta come un generico errore di connessione, la maggior parte degli sviluppatori inizia a fare debug della VPN, del firewall, dell'applicazione — di tutto tranne che del DNS.
Come funziona davvero la risoluzione DNS sui sistemi moderni
Il modello mentale 'il DNS è semplice' — il computer chiede a un server DNS un IP e il server risponde — non è accurato da anni. I sistemi moderni hanno una pipeline di risoluzione con più stadi, ciascuno dei quali può intercettare, modificare o reindirizzare le query.
DNS resolution pipeline on a typical macOS/Linux system:
Application calls getaddrinfo("api.internal")
↓
1. /etc/hosts — checked first. Static overrides.
↓
2. NSSwitch / resolver configuration
- Linux: /etc/nsswitch.conf controls lookup order
- macOS: /etc/resolver/* for per-domain DNS servers
↓
3. System resolver (systemd-resolved, mDNSResponder)
- May intercept .local, .internal, or other special TLDs
- May apply DNSSEC validation
- May cache responses
↓
4. DNS server (from DHCP, VPN, or manual config)
- Queries sent to configured upstream resolver
- May go through a corporate DNS proxy
↓
5. Recursive resolver (8.8.8.8, 1.1.1.1, corporate)
- Walks the DNS hierarchy: root → TLD → authoritative
↓
Response flows back up the chain
In ogni stadio qualcosa può andare storto. Il resolver di sistema potrebbe intercettare una query prima che raggiunga il server DNS configurato. La VPN potrebbe sovrascrivere le impostazioni DNS. DHCP potrebbe fornire server DNS che hanno la precedenza sulla tua configurazione manuale. E la cache a ogni livello significa che una correzione potrebbe non avere effetto immediato.
La bugia di /etc/resolv.conf
Su Linux, /etc/resolv.conf era storicamente l'unica fonte di verità per la configurazione DNS. Imposti il nameserver lì e il sistema lo usa. Semplice.
Le distribuzioni Linux moderne hanno reso tutto molto più complicato. Se usi systemd-resolved (che Ubuntu, Fedora e la maggior parte delle distro basate su systemd adottano di default), /etc/resolv.conf è un link simbolico a uno stub resolver su 127.0.0.53. La configurazione DNS reale vive nello stato di systemd-resolved, gestito tramite resolvectl. Modificare direttamente /etc/resolv.conf significa che il file viene sovrascritto al primo cambio di rete oppure che lo stub resolver si rompe.
# What you THINK your DNS config is:
$ cat /etc/resolv.conf
nameserver 127.0.0.53 # This is systemd-resolved's stub
# What your DNS config ACTUALLY is:
$ resolvectl status
Global:
DNS Servers: 8.8.8.8 8.8.4.4
DNS Domain: ~.
Link 2 (eth0):
DNS Servers: 10.0.0.1 # From DHCP — this takes precedence
DNS Domain: corp.internal
Link 5 (wg0): # WireGuard VPN
DNS Servers: 10.100.0.1
DNS Domain: ~internal # VPN claims the .internal domain
# Three different DNS servers, each handling different domains.
# /etc/resolv.conf shows none of this.
Docker aggiunge un ulteriore livello. I container Docker ricevono un proprio /etc/resolv.conf, solitamente copiato dalla configurazione dell'host all'avvio del container. Se il DNS dell'host cambia (la VPN si connette, si cambia rete), i container in esecuzione mantengono la vecchia configurazione finché non vengono riavviati. È una fonte comune di bug del tipo 'funziona al riavvio ma dopo un po' smette'.
Split DNS e conflitti con la VPN
Lo split DNS — usare server DNS diversi per domini diversi — è lo standard negli ambienti aziendali. La VPN instrada le query per *.corp.internal al server DNS aziendale, mentre le query pubbliche vanno al resolver normale. Funziona bene quando è configurato correttamente e fallisce in modi confusi quando non lo è.
Modalità di guasto comuni:
- La VPN imposta i server DNS come default globali. Alcuni client VPN configurano il server DNS della VPN come resolver predefinito per tutte le query, non solo per quelle interne. Ora tutte le tue query DNS — compresa la navigazione personale — passano dal server DNS aziendale. Questo è lento (perché il DNS aziendale si trova in un altro data center) e pone un problema di privacy.
- Conflitti nell'ordine dei server DNS. Quando sono configurati più server DNS, il sistema li prova in ordine. Se il primo è lento o irraggiungibile (la VPN è disconnessa ma la configurazione DNS resta), ogni query DNS attende un timeout prima di provare il secondo server. Questo aggiunge ritardi di 5 secondi a ogni connessione.
- I search domain si aggiungono in silenzio. Un search domain
corp.internalfa sì che una query perapiprovi ancheapi.corp.internal. È utile (scrivissh apiinvece dissh api.corp.internal) finché non lo è più — quando un hostname breve entra in collisione con il nome di un servizio interno, le query si risolvono nell'IP sbagliato. - mDNS intercetta i domini interni. Come mostra il caso di
.internal, il resolver di sistema può intercettare le query per certi TLD prima che raggiungano qualsiasi server DNS configurato. È particolarmente insidioso perché l'intercettazione avviene in silenzio — nessun messaggio d'errore, solo un timeout o una risposta sbagliata.
Debug del DNS: il toolkit
Quando il DNS si rompe, ti servono strumenti che mostrino esattamente cosa succede in ogni stadio della pipeline di risoluzione.
# 1. Check what your system actually resolves:
$ dig api.internal
# Shows: query sent to which server, response received
# The 'SERVER' line tells you which DNS server answered
# 2. Query a specific DNS server directly:
$ dig @10.100.0.1 api.internal
# Bypasses the system resolver — goes directly to the specified server
# If this works but 'dig api.internal' doesn't, the problem is
# in the system resolver, not the DNS server
# 3. Check systemd-resolved state (Linux):
$ resolvectl query api.internal
# Shows which interface/server handled the query
# 4. Check macOS DNS routing:
$ scutil --dns
# Shows per-domain DNS configuration on macOS
# Look for your .internal domain — is it being routed correctly?
# 5. Monitor DNS queries in real time:
$ sudo tcpdump -i any port 53
# See actual DNS packets. If no packets go out for your query,
# the system resolver is intercepting it locally.
# 6. Flush DNS cache (when fixing config):
$ sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder # macOS
$ resolvectl flush-caches # Linux with systemd-resolved
Il passo diagnostico più importante: interroga direttamente il server DNS con dig @server hostname. Se funziona ma la risoluzione normale no, il problema sta tra l'applicazione e il server DNS — intercettazione da parte del resolver di sistema, cache o routing. Se anche le query dirette falliscono, il problema è nel server DNS stesso o nella connettività di rete verso di esso.
Scegliere il proprio TLD privato
Il caso di .internal solleva una domanda pratica: quale TLD usare per i servizi interni? Non esiste una risposta universalmente sicura, ma alcune opzioni sono migliori di altre.
- .internal — RFC 6762 lo riserva per uso interno, ma la gestione mDNS di Apple lo rende problematico su macOS. Su Linux probabilmente funziona senza problemi.
- .local — Riservato a mDNS (RFC 6762). Usarlo per servizi risolti via DNS entrerà in conflitto con Bonjour/Avahi. Non usarlo.
- .corp, .home, .mail — Non riservati. L'ICANN potrebbe assegnarli come TLD pubblici in qualsiasi momento, il che romperebbe la risoluzione interna. Non usarli.
- Sottodominio di un dominio di tua proprietà.
internal.tuodominio.com— è l'opzione più sicura. Controlli il dominio padre, quindi non c'è rischio di collisioni. Lo svantaggio: è più lungo da digitare. - .test, .example, .invalid, .localhost — Riservati dalla RFC 6761 e garantiti per non essere mai assegnati come TLD pubblici.
.testè l'opzione più pulita per i servizi interni che non sono in produzione.
La soluzione noiosa ma corretta: usa un sottodominio di un dominio di tua proprietà. api.internal.tuodominio.com è univoco, non entra in collisione con nulla e funziona correttamente su ogni sistema operativo e resolver DNS. È più lungo, ma i problemi DNS alle 2 di notte sono peggio che digitare qualche carattere in più.
Lezioni per i sistemi di produzione
Ogni outage DNS insegna le stesse lezioni, e continuiamo a reimpararle.
- Monitora la risoluzione DNS, non solo i server DNS. Che il tuo server DNS sia sano non significa che la risoluzione funzioni. Monitora l'intero percorso: la tua applicazione riesce davvero a risolvere gli hostname di cui ha bisogno? Un controllo sintetico che risolve gli hostname critici ogni 30 secondi individua i problemi più velocemente del monitoraggio dei server.
- Usa TTL brevi per il DNS interno. Quando devi cambiare un record DNS interno, TTL lunghi significano dati obsoleti in cache. TTL di 60 secondi per i servizi interni garantiscono un failover rapido con un carico minimo sui resolver.
- Testa gli aggiornamenti del sistema operativo con la tua configurazione DNS. Il problema di
.internalha colpito solo gli utenti macOS. Verifica il tuo ambiente di sviluppo dopo i major release del sistema operativo — i cambiamenti nella risoluzione DNS raramente finiscono nelle release note. - Documenta l'architettura DNS. Quali server DNS gestiscono quali domini? Dove sono i punti di split? Cosa succede quando la VPN si disconnette? Questa documentazione è la prima cosa di cui avrai bisogno durante un outage e l'ultima che qualcuno scrive.
- Prevedi un fallback. Se la risoluzione DNS fallisce per un servizio interno, la tua applicazione può usare un IP hardcoded? Non è elegante, ma un IP hardcoded che funziona è meglio di un nome DNS che non si risolve.
Il DNS è uno di quei servizi fondamentali invisibili quando funziona e catastrofici quando non funziona. Il guasto di .internal è un promemoria che la configurazione DNS è più fragile di quanto sembri: un singolo aggiornamento del sistema operativo può cambiare il comportamento della risoluzione per milioni di utenti. Progetta i tuoi sistemi partendo dal presupposto che il DNS si romperà, e ti sorprenderai meno quando succederà.


