BSD daemon con la telemetria che fluisce attraverso una pipeline di enrichment verso VictoriaLogs

Ho un server FreeBSD che si chiama m42 e gira da anni. Email, web, firewall, i soliti. Due anni e mezzo di backup mensili restic negli snapshot — circa 25 milioni di righe syslog su quattro formati: BSD syslog, fail2ban, pf packet filter, e nginx. Una miniera d’oro di telemetria di sicurezza, completamente non indicizzata e non ricercabile.

Ho costruito uno stack di observability su un Raspberry Pi 5 a casa — VictoriaLogs per lo storage, Telegraf per il processing, Grafana per la visualizzazione — e ho deciso di fare il backfill di ognuna di quei 25 milioni di entry attraverso la stessa identica pipeline di enrichment che processa i dati live. Geolocalizzazione GeoIP, identificazione ASN, reverse DNS per ogni indirizzo IP.

Il backfill in sé era semplice. Quello che non era semplice: i tre bug che ha scovato nelle viscere di Telegraf. Il genere di bug che emerge solo sotto carico sostenuto. Il genere che nessuno incontra perché nessuno fa questa roba.

L’architettura: replay, non riscrittura

L’approccio ingenuo è scrivere script Python che replicano la pipeline — parsare i log, arricchire con GeoIP, POST al log store. L’ho fatto. Due volte. Ogni volta gli script divergevano dalla pipeline live: nomi di campi diversi, enrichment mancanti, inconsistenze nel parsing tra Starlark e le regex Python.

TL;DR: Se usi OpenWrt con mwan3 (failover multi-WAN) e una VPN WireGuard in split-tunnel (cioè NON routi tutto il traffico attraverso la VPN), aggiungi nohostroute=1 all’interfaccia WireGuard. Senza, netifd crea un route statico per l’endpoint WireGuard all’avvio dell’interfaccia, pinnato a qualsiasi uplink sia attivo in quel momento. Per il primo corollario della Legge di Murphy, tutto ciò che può andare storto andrà storto nel momento peggiore possibile — quindi il link primario sarà giù esattamente quando WireGuard parte, e il route dell’endpoint resta incollato permanentemente al backup. La tua VPN resterà incollata al backup lento mentre il link primario se ne sta lì a non fare un cazzo. Non te ne accorgerai finché non dovrai trasferire qualcosa di grosso.

(Se routi tutto il traffico attraverso WireGuard, l’host route ti serve per evitare un routing loop — ma su un setup multi-WAN, il problema del route stantio resta identico. Ti servirà un workaround diverso, tipo uno script hotplug che aggiorni il route dell’endpoint quando mwan3 switcha uplink.)

Oggi ho scoperto che il mio tunnel WireGuard verso un server remoto strisciava a 2 Mbps da inizio febbraio. Il fix ha richiesto due comandi UCI. La root cause era il flag nohostroute mancante — più un bonus: il mio stesso firewall sabotava i miei stessi health check, facendo sembrare la fibra abbastanza inaffidabile da impedire al sistema di auto-correggersi.

Ecco la storia forense completa, perché sono ancora incazzato e meritate di imparare dalle mie sofferenze.

Prima però, un po’ di contesto su come è avvenuta questa indagine. Stavo lavorando con un assistente AI (Claude Code) che ha accesso SSH alla mia infrastruttura. Questo è possibile perché ho una base solida: autenticazione SSH a chiave ovunque, DNS interno corretto (m42, golem risolvono agli indirizzi VPN giusti), mesh WireGuard tra tutti i nodi, e l’assistente si connette tramite un ssh-agent che gira come servizio utente systemd. Una variabile d’ambiente e l’AI raggiunge ogni macchina della mia rete — e, cosa fondamentale, può incrociare i dati trovati su una macchina con quelli di un’altra. Quest’indagine mi avrebbe richiesto ore di salti tra terminali. L’AI l’ha fatta in minuti, testando ipotesi metodicamente su tre macchine simultaneamente. L’investimento in SSH, DNS e VPN fatti bene ha ripagato enormemente.

Uno sviluppatore sorridente davanti a un blog ridisegnato, mentre un robot AI soddisfatto riposa sullo sfondo circondato da token di codice fluttuanti

È come avere un ingegnere incredibilmente veloce, competente e preciso seduto accanto a te — uno che ti lascia davvero fluire creativamente senza freni. Dici “e se facessimo…” e trenta secondi dopo hai un prototipo funzionante davanti agli occhi. Dici “no, più così” e ha già finito prima che tu abbia spiegato il perché.

È questa la sensazione che ho avuto lavorando con Claude Code negli ultimi due giorni. Ho rifatto completamente questo blog — tradotto tutti e 69 gli articoli in italiano, ridisegnato il layout da zero, aggiunto un easter egg nerd con la sequenza di boot del kernel, ripulito anni di tag accumulati, e iterato su decine di decisioni di design. Tutto tracciato in git, tutto verificabile, tutto live.

Ogni singolo commit è pubblico. Se vuoi vedere il processo grezzo — il brainstorming, le iterazioni, i bugfix, il botta e risposta — è tutto nel repo: github.com/vjt/sindro.me (e il fork del tema: github.com/vjt/hugo-sindrome-theme). Non mi vergogno a mostrare come viene fatta la salsiccia. Anzi, spero che qualcuno lo trovi utile come esempio concreto di cosa sia davvero lo sviluppo assistito dall’AI — con tutti i difetti annessi.

Ecco il mio grafico di contribuzione su GitHub, per dimostrare che non sto esagerando:

Un telefono rotto con l’app bloatware dell’allarme buttato nel cestino, mentre una dashboard Home Assistant brilla trionfante su un tablet

L’app Verisure fa schifo. Lì, l’ho detto.

Non che l’allarme funzioni male — il pannello SDVECU è solido, i sensori sono affidabili, l’installazione è professionale. Ma l’app. Dio santo, l’app.

Il problema

Apri l’app per controllare lo stato dell’allarme e ti accoglie una pubblicità di Verisure stessa. Io pago fior di quattrini per il servizio e loro mi piazzano le ads dentro l’app. È il 2026 e un’azienda di sicurezza mi fa vedere banner pubblicitari quando provo a verificare se casa mia è protetta.

Ma le ads sono il meno. I veri problemi sono:

  • Routine cieche. Sì, l’app ha le “routine” — attiva a mezzanotte, disattiva alle 7. Ma non sanno dove sei. Mezzanotte e sei ancora in giardino? L’allarme si attiva e i sensori scattano. Finestra aperta? Il pannello annuncia che non riesce ad attivare, ma se non lo senti l’allarme resta spento. Vai in vacanza e dimentichi di disabilitare la routine di disattivazione mattutina? Allarme spento con la casa vuota. E le modifiche alle routine impiegano 20 minuti a propagarsi — “o il giorno dopo”. Nel 2026.
  • Zero presenza. L’app non sa dove sei. Non sa chi è in casa. Non sa se la donna delle pulizie è andata via. Nessuna automazione basata sulla posizione.
  • Una telecamera alla volta. Vuoi vedere tutte le camere? Tocca, aspetta, torna indietro, tocca la prossima, aspetta. Nessuna vista d’insieme. Nessun “cattura tutto”.
  • Lentezza biblica. Richiedi un’immagine, aspetti, aspetti, forse arriva. A volte ricarichi l’app e riprovi. Nel 2026.
  • Nessuno storage permanente. Le immagini catturate spariscono. Non c’è uno storico consultabile.
  • Nessun timestamp sulle immagini. Catturi una foto e non sai quando è stata scattata né da quale camera. Devi ricordartelo tu. Per un sistema di sicurezza è imbarazzante.
  • Notifiche generiche. Una notifica uguale per tutti. Niente notifiche actionable, niente notifiche critiche che bypassano il “Non Disturbare”.

Quello che volevo: il mio allarme, integrato nella mia domotica, con automazioni intelligenti, notifiche per tutti i residenti, e una dashboard che mostra tutto in un colpo d’occhio. Senza pubblicità.

Sezione di una casa con access point WiFi su ogni piano, onde del segnale sovrapposte, e un telefono che rimbalza caoticamente tra di essi

Tutto è cominciato con il rilevamento presenza WiFi. Avevo costruito un sistema che traccia in quale stanza si trova ognuno scrapando l’RSSI dai miei AP OpenWrt. Funzionava – ma le assegnazioni delle stanze continuavano a sfarfallare. Cucina. Ufficio. Cucina. Ufficio. Tre volte in dieci secondi. La macchina a stati era a posto. Il WiFi no.

La mia rete domestica ha sei AP OpenWrt su tre piani, due SSID – Mercury su 5 GHz, Saturn su 2,4 GHz – tutti con 802.11r per il roaming veloce. Vista da fuori, sembra una mesh fatta bene. Vista da dentro, un telefono rimbalzava tra access point 129 volte in 24 ore.

Non lo sapevo finché non ho costruito lo strumento per vederlo.

Timeline di roaming — 24 ore

Ogni riga è un client WiFi, il colore mostra a quale AP è connesso. I client sani mostrano barre lunghe e piene. Quelli malati sembrano pali da barbiere. Vedi sara-iphone? Quella striscia arcobaleno sono 129 connessioni in 24 ore – il telefono cammina in una zona di overlap tra due AP dove entrambi hanno un segnale circa uguale (e orrendo).

Mantengo un mucchio di pacchetti OpenWrt custom su quattro architetture: MediaTek Filogic (aarch64), Raspberry Pi 2 (ARM), Ramips MT7621 (MIPS) e Atheros ath79 (MIPS). L’SDK di OpenWrt gira solo su x86_64. Non ho un build server dedicato. E non ne voglio uno – una macchina che sta lì a far niente il 99,9% del tempo solo per compilare file .ipk ogni qualche giorno è un’offesa al mio senso di allocazione delle risorse.

Quindi ho costruito openwrt-builder: un sistema che fa polling dei miei repo per le modifiche, lancia una VM Hetzner usa-e-getta quando deve compilare, builda i pacchetti, li riporta indietro, e distrugge il server. Il tutto controllato via Telegram.

Vista a spaccato di una casa con access point WiFi in ogni stanza, telefoni collegati all’AP più vicino in base alla potenza del segnale

Avevo due problemi con il rilevamento presenza di Home Assistant.

Il primo: il GPS ti dice se qualcuno è a casa, ma non dove in casa si trova. La mia casa ha sei access point OpenWrt distribuiti su tre piani. Sanno già esattamente quale telefono è connesso a quale AP in ogni momento – sono dati di presenza a livello di stanza, lì nello stack WiFi, che urlano per essere usati. Sapere chi è in quale stanza apre un’intera classe di automazioni che il GPS non può toccare: luci che ti seguono, climatizzazione per stanza occupata, una dashboard che mostra la situazione della casa a colpo d’occhio.

Il secondo: la nostra donna delle pulizie sta a casa nostra un paio di giorni a settimana. Non voglio configurarle un account HA completo, installarle l’app companion sul telefono, o avere a che fare con i permessi GPS. Ma devo sapere se è a casa – perché la mia automazione dell’allarme ha bisogno di sapere se la casa è davvero vuota prima di attivarsi. Il suo telefono si connette al WiFi. Mi basta questo.

Così ho scritto openwrt-ha-presence: una macchina a stati che scrapa le metriche RSSI direttamente dai tuoi AP OpenWrt, capisce in quale stanza si trova ogni persona in base alla potenza del segnale, e pubblica lo stato casa/fuori per ogni persona su Home Assistant via MQTT Discovery. Niente cloud, niente beacon, niente parsing di log, niente database time-series. Python, async, ~600 righe di logica effettiva.

Antenna direzionale su un muro puntata verso una torre cellulare, cavo in fibra che si spezza da un lato mentre le onde 5G colmano il divario

Un paio di mesi fa, la fibra è andata giù. Come da primo corollario di Murphy, è successo nel momento peggiore in assoluto: subito prima di una riunione cruciale con un’azienda partner. Mi sono ritrovato a saltare freneticamente tra l’AP di un vicino lontano e l’hotspot del telefono, ma entrambi facevano schifo. Parliamo di 200ms di RTT e 15% di packet loss. Mi stavo scusando a profusione mentre il mio feed video si trasformava in uno slideshow del 1998; nessuno capiva una parola di quello che dicevo. Ho finito per spegnere il video e stare zitto. Opportunità persa. Mai. Più.

Così sono andato in modalità paranoica totale e ho costruito un setup di backup 5G serio.

L’hardware

Il segnale 5G qui è inesistente, quindi ho dovuto usare l’artiglieria pesante. La Poynting è una bestia. 11 dBi di guadagno, vero MIMO 4x4, cross-polarizzata, stagna. Puntala verso la torre più vicina e all’improvviso il SINR salta da “meh” a “porco dio!”.

Ma puntare un’antenna direzionale senza feedback visivo è doloroso. In pratica giri in tondo, aggiorni una web UI, bestemmi guardando il cielo.

Una gigantesca balena Docker che calpesta un server room, distruggendo catene iptables, mentre un sysadmin furioso si erge su un rack

Siamo nel 2026, e stiamo ancora lottando con l’arroganza assoluta di Docker riguardo al networking Linux.

Ecco lo scenario: faccio girare un host ibrido. Da un lato, ho una macchina virtuale KVM che fa girare Home Assistant (perché ho bisogno del controllo completo del SO e della cifratura del disco). Dall’altro, ho la solita lista di container Docker – NUT per monitorare il mio UPS Lakeview (Vultech) di merda e Technitium per DNS e DHCP – in esecuzione direttamente sull’host.

Sembra semplice. Dovrebbe essere semplice.

Ma nel momento in cui ho installato Docker, la comunicazione con la mia VM Home Assistant è morta. Semplicemente cessata di esistere.

Il problema: Docker è un dittatore

Docker, per default, tratta le tue regole iptables come se fossero semplici suggerimenti. Quando il demone si avvia, sostanzialmente sovrascrive la chain FORWARD, inserisce la sua logica, e imposta policy che isolano efficacemente qualsiasi cosa non sia un container gestito da Docker stesso.

Se hai un’interfaccia bridge per una VM (come br0 o virbr0), le regole di Docker spesso finiscono per droppare i pacchetti destinati a quella VM perché non corrispondono alla sua logica interna per il traffico dei container.


In questa pagina