Ottimizzazione dell'host
Quattro limiti che una rete trafficata può raggiungere prima che Warden stesso resti senza spazio: come controllarli uno per uno e come alzarli.
Cos’è
I valori predefiniti di Warden vanno bene per una rete domestica. Su una rete più grande, come un ufficio o una scuola con centinaia di dispositivi, quattro limiti possono iniziare a perdere query o dati senza un errore evidente. Due sono impostazioni dell’host Linux, due sono impostazioni di Warden:
| Limite | Cosa noti | Dove lo cambi |
|---|---|---|
| Tracciamento delle connessioni di un firewall stateful | Nelle ore di punta le risoluzioni rallentano o falliscono, e l’upstream sembra lento o guasto | Linux: net.netfilter.nf_conntrack_max, oppure le tue regole nftables |
| Buffer di ricezione UDP | Una parte di una raffica di query resta senza risposta, e quei client ritentano | Linux: net.core.rmem_max |
| Tabella dei dispositivi | Le statistiche per dispositivo sono incomplete, e alcuni contatori ripartono da zero | Warden: [tracking] max_devices |
| Limite di dimensione del log delle query | Il log delle query tiene solo la parte finale di una giornata trafficata | Warden: [tracking] query_log_max_size_mb e query_log_max_files |
Fai questi controlli una volta quando prepari Warden per una rete più grande, e di nuovo quando la rete cresce.
Host con un firewall stateful
Un firewall che traccia le connessioni tiene una voce per ogni query DNS per circa 30 secondi. Quando la sua tabella è piena, il kernel scarta i pacchetti nuovi.
Un firewall stateful ricorda ogni connessione nella tabella di tracciamento delle connessioni del kernel (conntrack). firewalld, che Fedora e RHEL attivano di default, funziona così, e lo stesso vale per ufw una volta attivato. Il DNS su UDP non ha connessioni, quindi il kernel conta come un nuovo flusso ogni query che arriva da una nuova porta di origine. Questo vuol dire:
- ogni query che un client manda a Warden, e
- con le impostazioni predefinite, ogni query che Warden manda a un upstream
DNS in chiaro (
[upstream] mode = "plain", porta 53) quando la risposta non è nella sua cache.
Un flusso che ha avuto risposta resta nella tabella per
net.netfilter.nf_conntrack_udp_timeout secondi, 30 sui kernel attuali. Quindi
la tabella può accogliere all’incirca questo numero di nuovi flussi al secondo:
nf_conntrack_max / nf_conntrack_udp_timeout
Tutte le altre connessioni che l’host traccia condividono questo budget. Il
kernel dimensiona nf_conntrack_max in base alla memoria dell’host, quindi una
macchina piccola ha una tabella piccola. Quando la tabella è piena, il kernel
scarta i pacchetti nuovi e scrive nf_conntrack: table full, dropping packet.
Per Warden questo sembra un upstream lento o guasto, e i client vedono
risoluzioni che vanno in timeout.
Controlla il tuo host
sudo sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max net.netfilter.nf_conntrack_udp_timeout
sudo dmesg | grep 'table full'- Se
sysctlrispondecannot stat /proc/sys/net/netfilter/nf_conntrack_count, il tracciamento delle connessioni non è caricato su questo host. Niente di questa sezione ti riguarda. - Se nelle ore di punta
nf_conntrack_countsi avvicina anf_conntrack_max, oppuredmesgstampa righetable full, la tabella è troppo piccola.
Per dimensionare la tabella, conta fino a due voci per query (una per la query del client, una per la query all’upstream) e moltiplica per il timeout. Per esempio, una rete con picchi di 1.000 query al secondo ha bisogno di fino a 1.000 × 2 × 30 = 60.000 voci solo per il DNS, in aggiunta a tutto il resto che l’host traccia.
Soluzione 1: una tabella più grande
Funziona su ogni host, compresi quelli che usano firewalld o ufw. Una voce occupa qualche centinaio di byte di memoria del kernel, e solo finché è in uso. 262.144 è un esempio: usa il tuo valore, calcolato come sopra, con un margine.
# Applicalo subito
sudo sysctl -w net.netfilter.nf_conntrack_max=262144
# Mantienilo dopo un riavvio
echo 'net.netfilter.nf_conntrack_max = 262144' | sudo tee /etc/sysctl.d/90-purge-warden-conntrack.conf
echo 'nf_conntrack' | sudo tee /etc/modules-load.d/nf_conntrack.confIl secondo file carica il modulo di tracciamento delle connessioni presto
all’avvio, prima che systemd applichi i file in /etc/sysctl.d/. Senza di
esso, l’impostazione può essere applicata prima che il firewall carichi il
modulo, e il kernel tiene il suo valore predefinito, più piccolo.
Soluzione 2: niente tracciamento per il DNS
Questa soluzione è per gli host dove scrivi tu le regole nftables, senza
firewalld né ufw. Le regole qui sotto marcano come non tracciati i pacchetti
DNS di Warden, così non occupano nessuna voce nella tabella: le query dei client
e le risposte di Warden, e le query di Warden ai suoi server upstream con le
loro risposte. Il resto del traffico DNS dell’host, come le sue risoluzioni per
gli aggiornamenti dei pacchetti, resta tracciato. I pacchetti non tracciati non
corrispondono più a ct state established,related, quindi la catena di input
ha bisogno di due regole che li accettino: le query dei tuoi client e le
risposte dei tuoi server upstream.
define dns_upstreams = { 192.0.2.53, 192.0.2.54 }
table inet dns_notrack {
chain prerouting {
type filter hook prerouting priority raw; policy accept;
fib daddr type local meta l4proto { tcp, udp } th dport 53 notrack
fib daddr type local ip saddr $dns_upstreams meta l4proto { tcp, udp } th sport 53 notrack
}
chain output {
type filter hook output priority raw; policy accept;
meta l4proto { tcp, udp } th sport 53 notrack
ip daddr $dns_upstreams meta l4proto { tcp, udp } th dport 53 notrack
}
}
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
ct state invalid drop
iif lo accept
# Query DNS solo dai tuoi client
ip saddr 192.168.1.0/24 meta l4proto { tcp, udp } th dport 53 accept
# Risposte DNS solo dai tuoi server upstream
ip saddr $dns_upstreams meta l4proto { tcp, udp } th sport 53 th dport 1024-65535 ct state untracked accept
# ... le tue altre regole
}
}- Sostituisci
192.168.1.0/24con le reti dei tuoi client. - In
dns_upstreamselenca tutti i server a cui Warden inoltra: iserversdi[upstream]e di[upstream.fallback], e i server di ogni regola[[forwarding]]. Non aggiungere i server che usa l’host stesso (resolvectl dns, o le righenameserverdi/etc/resolv.conf), a meno che Warden non inoltri anche a loro: il loro traffico resta tracciato e non ha bisogno di regole. - Warden manda le sue query da porte sopra la 1023, e così fanno i resolver di
sistema più comuni, quindi
th dport 1024-65535tiene chiuse le porte basse ai pacchetti che dicono di arrivare da un upstream. - Per client o upstream IPv6, definisci un secondo insieme con gli indirizzi
IPv6 e aggiungi le stesse regole con
ip6 saddreip6 daddr. - Se anche la tua catena di output ha
policy drop, aggiungile due regole:meta l4proto { tcp, udp } th sport 53 acceptper le risposte di Warden, eip daddr $dns_upstreams meta l4proto { tcp, udp } th dport 53 acceptper le sue query ai server upstream. Nemmeno lì i pacchetti non tracciati corrispondono act state established,related, quindi senza queste regole i tuoi client non ricevono risposte e Warden non raggiunge i suoi server upstream. fib daddr type locallimita le regole di prerouting ai pacchetti diretti a questo host, così il traffico DNS che l’host instrada per altre macchine resta tracciato. Se questo host fa anche la traduzione degli indirizzi (NAT) per macchine che interrogano direttamente gli stessi server upstream, usa la soluzione 1: le loro risposte hanno bisogno del tracciamento per tornare indietro.
Controlla il file, caricalo, verifica che l’host risolva ancora i nomi e osserva il contatore:
sudo nft -c -f /etc/nftables.conf # solo controllo, non cambia nulla
sudo nft -f /etc/nftables.conf
getent hosts purge.cc # deve stampare un indirizzo
sudo sysctl net.netfilter.nf_conntrack_countSe getent non stampa nulla, l’host non riesce più a risolvere i nomi. Esegui
sudo nft delete table inet dns_notrack: toglie subito le regole notrack e il
tracciamento torna come prima. Senza di loro, le due regole di accettazione
nella catena di input non fanno danni. Fai lo stesso se i tuoi client non
ricevono più risposte. Poi confronta il tuo file con l’estratto e correggilo:
la tabella torna la prossima volta che il file viene caricato, per esempio
all’avvio.
Con le regole attive, nf_conntrack_count non sale più con il traffico DNS di
Warden.
th sport 53 da qualsiasi indirizzo: chiunque potrebbe raggiungere ogni porta
di questo host mandando pacchetti dalla porta 53. Quando [server] allow_from
è impostato, anche Warden rifiuta le query da altre origini, ma la prima linea
è il firewall.Buffer di ricezione UDP
Warden chiede al kernel un buffer di 2 MiB sul suo socket DNS. Un’impostazione dell’host può limitarlo, e la riga di avvio di Warden lo dice.
Le query che arrivano più in fretta di quanto Warden le legga aspettano nel buffer di ricezione del suo socket UDP. Quando il buffer è pieno, il kernel scarta le successive, e quei client ricevono una risposta solo dopo il loro timeout e un nuovo tentativo. Succede a raffiche: molti dispositivi che si risvegliano insieme, o un’app che manda decine di richieste tutte insieme.
All’avvio Warden chiede un buffer di ricezione di 2 MiB (2.097.152 byte). Linux
raddoppia ogni richiesta per la propria contabilità interna, quindi il buffer
diventa di 4 MiB, a meno che net.core.rmem_max sia più basso: il kernel
limita la richiesta a quel valore. Su molti host vale 212.992 byte, il valore
predefinito del kernel.
La riga di avvio nel journal dice in quale caso sei:
| Riga di avvio | Cosa significa |
|---|---|
DNS UDP receive buffer: 4194304 bytes (requested 2097152) | Il buffer è grande quanto Warden ha chiesto. Non c’è niente da fare. |
DNS UDP receive buffer: 425984 bytes (requested 2097152); net.core.rmem_max caps it, raise it with: sysctl -w net.core.rmem_max=2097152 (persist it in /etc/sysctl.d/) | net.core.rmem_max ha limitato la richiesta (in questo esempio a 212.992 byte, raddoppiati). Il DNS funziona, ma una raffica può perdere query. |
Un avviso, cannot raise the DNS UDP receive buffer: seguito dall’errore di sistema | Warden non è riuscito a impostare il buffer. Continua con il buffer che il socket aveva già. |
Controlla il tuo host
sudo journalctl -u purge-warden -b | grep 'UDP receive buffer'
sudo ss -ulmn 'sport = :53'
nstat -az UdpRcvbufErrors Udp6RcvbufErrorsssmostra ogni socket sulla porta 53 con una rigaskmem:(...):rbè la dimensione del suo buffer di ricezione in byte, edconta i pacchetti che il kernel ha scartato per quel socket.UdpRcvbufErrors(IPv4) eUdp6RcvbufErrors(IPv6) contano, dall’avvio, i pacchetti UDP che tutti i socket dell’host hanno scartato perché il loro buffer era pieno. Se uno dei due cresce nelle ore di punta e Warden è il servizio UDP principale dell’host, il buffer di Warden è troppo piccolo.
Alza il limite
# Applicalo subito
sudo sysctl -w net.core.rmem_max=2097152
# Mantienilo dopo un riavvio
echo 'net.core.rmem_max = 2097152' | sudo tee /etc/sysctl.d/90-purge-warden-rmem.conf
# Warden imposta il buffer solo all'avvio
sudo systemctl restart purge-warden
sudo journalctl -u purge-warden --since '5 minutes ago' | grep 'UDP receive buffer'warden reload non cambia il buffer, quindi riavvia il servizio.
net.core.rmem_max è solo un tetto: alzarlo non dà un buffer più grande a
nessun altro programma, a meno che quel programma non lo chieda.
Più di 1.024 dispositivi
Warden tiene le statistiche per al massimo 1.024 indirizzi client. Oltre quel numero, le voci viste meno di recente fanno spazio, e i loro contatori ripartono.
Warden tiene una tabella dei client che ha visto: una voce per indirizzo
client, con i contatori delle query e dei blocchi, l’ora in cui è stato visto
l’ultima volta e la sua attività delle ultime 24 ore. Le viste dei dispositivi
della dashboard e le statistiche per dispositivo leggono questa tabella.
[tracking] max_devices ne limita la dimensione, 1.024 di default.
- Una voce per indirizzo, non per dispositivo. Un dispositivo con un indirizzo IPv4 e uno IPv6, o uno che cambia il suo indirizzo IPv6 nel tempo, occupa più voci.
- Le voci non scadono da sole. Quando la tabella è piena, ogni query da un nuovo indirizzo rimuove la voce vista meno di recente tra 8 voci campionate. Se quel dispositivo ritorna, i suoi contatori ripartono da zero.
- Le risposte DNS non cambiano. Sono coinvolte solo le statistiche, e nessuna riga di log segnala che una voce è stata rimossa.
max_devices = 0disattiva le statistiche per dispositivo.
Controlla
Se l’endpoint delle metriche è attivo (vedi Metriche e log),
purge_warden_tracked_devices è il numero di voci nella tabella. Quando è
uguale a max_devices, la tabella è piena:
curl -s http://127.0.0.1:8053/metrics | grep '^purge_warden_tracked_devices'
warden config show --section trackingSenza l’endpoint delle metriche, confronta max_devices con il numero di
indirizzi client della tua rete, contando a parte gli indirizzi IPv4 e IPv6.
Alzalo
Imposta max_devices sopra quel numero, con margine per la crescita. Una voce
occupa circa 1 KiB di memoria, quindi 4.096 voci occupano circa 4 MiB.
sudo warden config edit # in [tracking], imposta: max_devices = 4096
sudo systemctl restart purge-wardenUna modifica a questa impostazione richiede un riavvio. warden reload non la
applica: il journal mostra reload requires daemon restart for runtime sections: tracking runtime,
e restano in uso le impostazioni precedenti.
Log delle query su una rete trafficata
Il log delle query tiene al massimo query_log_max_files + 1 file da
query_log_max_size_mb MiB al giorno: 800 MiB con i valori predefiniti. Una
giornata con più traffico tiene solo i suoi ultimi 800 MiB. La pagina
Query log (in inglese) spiega come
capire cosa vuol dire per la tua rete, quali impostazioni tengono di più e
quanto disco può occupare il log.
Verificalo nel codice
Ogni affermazione di questa pagina è a una funzione di distanza nel sorgente. Dove guardare:
src/dnsserver.rs → raise_recv_buffer()src/dnsserver.rs → recv_buffer_note()src/upstreamplain.rs → PlainUpstream::new()src/trackingengine.rs → evict_stalest_device()src/cli/commandsstart.rs → RestartOnlyRuntimeFingerprint::changed_sections()