IT

Ottimizzazione dell'host

Under review — wording may still change.

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:

LimiteCosa notiDove lo cambi
Tracciamento delle connessioni di un firewall statefulNelle ore di punta le risoluzioni rallentano o falliscono, e l’upstream sembra lento o guastoLinux: net.netfilter.nf_conntrack_max, oppure le tue regole nftables
Buffer di ricezione UDPUna parte di una raffica di query resta senza risposta, e quei client ritentanoLinux: net.core.rmem_max
Tabella dei dispositiviLe statistiche per dispositivo sono incomplete, e alcuni contatori ripartono da zeroWarden: [tracking] max_devices
Limite di dimensione del log delle queryIl log delle query tiene solo la parte finale di una giornata trafficataWarden: [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

bash
sudo sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max net.netfilter.nf_conntrack_udp_timeout
sudo dmesg | grep 'table full'
  • Se sysctl risponde cannot 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_count si avvicina a nf_conntrack_max, oppure dmesg stampa righe table 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.

bash
# 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.conf

Il 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.

/etc/nftables.conf (estratto)
text
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/24 con le reti dei tuoi client.
  • In dns_upstreams elenca tutti i server a cui Warden inoltra: i servers di [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 righe nameserver di /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-65535 tiene 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 saddr e ip6 daddr.
  • Se anche la tua catena di output ha policy drop, aggiungile due regole: meta l4proto { tcp, udp } th sport 53 accept per le risposte di Warden, e ip daddr $dns_upstreams meta l4proto { tcp, udp } th dport 53 accept per le sue query ai server upstream. Nemmeno lì i pacchetti non tracciati corrispondono a ct state established,related, quindi senza queste regole i tuoi client non ricevono risposte e Warden non raggiunge i suoi server upstream.
  • fib daddr type local limita 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:

bash
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_count

Se 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.

Tieni strette le due regole di accettazione
I pacchetti non tracciati saltano le regole stateful. Accettali solo dalle reti dei tuoi client e dagli indirizzi dei tuoi upstream. Non accettare mai 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 avvioCosa 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 sistemaWarden non è riuscito a impostare il buffer. Continua con il buffer che il socket aveva già.

Controlla il tuo host

bash
sudo journalctl -u purge-warden -b | grep 'UDP receive buffer'
sudo ss -ulmn 'sport = :53'
nstat -az UdpRcvbufErrors Udp6RcvbufErrors
  • ss mostra ogni socket sulla porta 53 con una riga skmem:(...): rb è la dimensione del suo buffer di ricezione in byte, e d conta i pacchetti che il kernel ha scartato per quel socket.
  • UdpRcvbufErrors (IPv4) e Udp6RcvbufErrors (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

bash
# 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 = 0 disattiva 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:

bash
curl -s http://127.0.0.1:8053/metrics | grep '^purge_warden_tracked_devices'
warden config show --section tracking

Senza 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.

bash
sudo warden config edit      # in [tracking], imposta: max_devices = 4096
sudo systemctl restart purge-warden

Una 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:

Buffer di ricezione e riga di avvio
src/dnsserver.rs → raise_recv_buffer()
src/dnsserver.rs → recv_buffer_note()
Upstream in chiaro
src/upstreamplain.rs → PlainUpstream::new()
Limite della tabella dei dispositivi ed espulsione
src/trackingengine.rs → evict_stalest_device()
Impostazioni che richiedono un riavvio
src/cli/commandsstart.rs → RestartOnlyRuntimeFingerprint::changed_sections()
↑↓ per navigare · Invio per aprire · Esc per chiudere Vedi tutti i risultati