Gruppi
Un gruppo collega molti dispositivi a un solo profilo, con una priorità che risolve i conflitti quando un dispositivo appartiene a più gruppi.
Cos’è
Un gruppo è un insieme con nome di dispositivi che condividono la stessa policy di filtraggio. Invece di legare uno per uno i cinque dispositivi dei bambini al profilo kids, dichiari un gruppo kids con profile = "kids" e poi elenchi i dispositivi dal lato del gruppo, oppure imposti groups = ["kids"] su ogni dispositivo. Funzionano entrambe le direzioni. In tutti e due i casi, quando arriva una query da uno di quei dispositivi, Warden la risolve con il profilo del gruppo, al livello 3 della catena.
Questa pagina spiega quando creare una voce [[groups]], ogni dimensione che puoi regolare, le superfici CLI, TUI e REST che leggono e scrivono i gruppi, e come la priorità decide quando un dispositivo appartiene legittimamente a più gruppi. Per il quadro generale di come una query viene abbinata a un profilo, vedi il processo di filtraggio e la sezione ordine di precedenza più sotto.
Quando ti serve
Usa un gruppo quando un profilo vale per un insieme di dispositivi, non per uno solo. Una tipica configurazione domestica finisce con una manciata di gruppi:
- Un gruppo
familylegato adefault, per i portatili dei genitori e il desktop in cucina. - Un gruppo
kidslegato akids, con ogni dispositivo che usano i bambini: tablet, il portatile della scuola, la Switch di famiglia. - Un gruppo
iotlegato aiot, che raccoglie lampadine smart, la stampante, il robot aspirapolvere: tutto ciò il cui cloud del produttore non ti ispira fiducia. - Un gruppo
guestslegato a un profilo più restrittivo, per i dispositivi noti della VLAN ospiti (gli altri ricadono sulla subnet).
Non ti serve un gruppo per un solo dispositivo: imposta il suo profile direttamente sulla riga [[devices]]. Non ti serve un gruppo nemmeno per un’eccezione su un singolo dispositivo: dai a quel dispositivo un profilo suo con una Custom List, vedi regole personalizzate (in inglese). E non ti serve un gruppo per “ogni host di questa VLAN”: usa [[subnets]], che intercetta gli host non mappati come fallback.
Schema
Un gruppo è una voce TOML di tipo array-of-tables: [[groups]], la stessa forma di [[devices]] e [[blocklists]], non una mappa come [profiles.<id>].
[[groups]]
id = "kids"
display_name = "Bambini"
profile = "kids"
priority = 10
devices = ["kids-tablet", "alice-iphone"]Cinque campi, tre dei quali obbligatori. Non c’è un campo di note o di descrizione: i gruppi sono piccoli di proposito.
Dimensioni
Un gruppo è un vettore lungo questi assi indipendenti. Le dimensioni obbligatorie sono indicate.
| Area | Dimensione | Campo TOML | Cosa regola |
|---|---|---|---|
| Identità | Id (obbligatorio) | id | Riferimento stabile. Ogni dispositivo che vuole questo gruppo, e ogni pianificazione che lo prende come bersaglio, usa questo id. |
| Nome visualizzato (obbligatorio) | display_name | Etichetta per l’operatore, mostrata nella scheda laterale della TUI e negli elenchi della CLI. | |
| Collegamento al resolver | Profilo (obbligatorio) | profile | La policy applicata a ogni membro al livello 3 della catena. Deve riferirsi a un [profiles.<id>] definito. |
| Peso di spareggio | priority | Quando un dispositivo appartiene a più gruppi con profili diversi, vince la priorità più alta. Il validator rifiuta solo un pareggio alla priorità massima tra gruppi che puntano a profili diversi. | |
| Appartenenza | Elenco diretto | devices | L’elenco degli id dei dispositivi iscritti a questo gruppo, scritto dal lato del gruppo. |
| Riferimento inverso (esterno) | [[devices]].groups | La stessa appartenenza, scritta dal lato del dispositivo. Entrambe le direzioni iscrivono un dispositivo: il resolver le unisce al caricamento. | |
| Collegamento alle pianificazioni (esterno) | Pianificazioni che bersagliano questo gruppo | [[schedules]].target_type = "group" + target_id = "<id>" | Una pianificazione sostituisce il profilo applicato ai membri del gruppo per la durata della sua finestra. |
Cose che sembrano dimensioni di un gruppo ma non lo sono:
- Blocklist, Custom List e risposta di blocco per gruppo. Stanno sul profilo a cui il gruppo è legato. Per bloccare un dominio in più “per i bambini”, aggiungilo a una Custom List e monta quella lista sul profilo
kidsconwarden profile mount kids --custom-list <id>. - Record DNS locali per gruppo. Vale lo stesso: stanno sul profilo.
- Un gruppo “solo etichetta” senza profilo. Ogni
[[groups]]deve legare unprofile: è obbligatorio, quindi non esiste un gruppo che serva solo come etichetta organizzativa senza effetto sulla risoluzione. Per un’etichetta che non cambia nulla, usaowner,device_typeodepartmentdi un dispositivo: sono gli assi di raggruppamento della TUI.
Riferimento dei campi
| Campo | Tipo | Obbligatorio | Default | Scopo |
|---|---|---|---|---|
id | stringa, [a-z0-9-], lunghezza 1–64, senza trattino iniziale o finale | sì | — | Riferimento stabile. |
display_name | stringa | sì | — | Etichetta per TUI e CLI. Il percorso set della CLI lo vuole non vuoto. |
profile | id di profilo | sì | — | Deve riferirsi a un [profiles.<id>] dichiarato. |
priority | i32 | no | 0 | Il valore più alto vince quando un dispositivo è in più gruppi. Sono accettati valori negativi. |
devices | lista di id di dispositivi | no | [] | Ogni id deve riferirsi a un [[devices]] dichiarato. I gruppi vuoti sono ammessi. |
#[serde(deny_unknown_fields)] è attivo: un errore di battitura fallisce al caricamento della config con unknown field. Fonte: src/config/schema/group.rs, src/config/schema/validator.rs → check_groups().
Appartenenza bidirezionale
Un dispositivo può essere iscritto a un gruppo da entrambi i lati. I due campi hanno nomi diversi, ma il resolver li tratta come un solo insieme:
# Iscrizione dal lato del gruppo
[[groups]]
id = "kids"
display_name = "Bambini"
profile = "kids"
devices = ["kids-tablet", "alice-iphone"]# Iscrizione dal lato del dispositivo, stesso effetto
[[devices]]
id = "kids-tablet"
display_name = "iPad dei bambini"
mac = "11:22:33:44:55:66"
groups = ["kids"]Funzionano entrambi; non servono tutti e due. Scegli quello che si legge meglio nel file che stai modificando: di solito il lato del dispositivo, perché è lì che scrive warden device set <id> groups …. Usa il lato del gruppo quando aggiungere più dispositivi allo stesso gruppo in una volta è più pulito. Usare entrambi per la stessa coppia è innocuo ma crea confusione; con più di una decina di dispositivi, scegli una direzione e restaci.
Esempi
Un gruppo minimo: tre dispositivi condividono la policy dei bambini.
[[groups]]
id = "kids"
display_name = "Bambini"
profile = "kids"
devices = ["kids-tablet", "alice-iphone", "school-laptop"]Due gruppi che si sovrappongono, con la priorità che decide quale profilo vince per un dispositivo che appartiene a entrambi. Alice è sia in kids (priorità 10) sia in school (priorità 20), quindi viene sempre risolta con il profilo school-allow-classroom, non con il più restrittivo kids. La priorità non dipende dall’orario; per cambiare profilo in base all’ora usa una pianificazione:
[[groups]]
id = "kids"
display_name = "Bambini"
profile = "kids"
priority = 10
devices = ["kids-tablet", "alice-iphone", "school-laptop"]
[[groups]]
id = "school"
display_name = "Dispositivi della scuola"
profile = "school-allow-classroom"
priority = 20
devices = ["alice-iphone", "school-laptop"]Un gruppo IoT con diversi dispositivi smart-home, senza priorità perché nessuno di loro appartiene a un altro gruppo:
[[groups]]
id = "iot"
display_name = "IoT"
profile = "iot"
devices = ["smart-tv", "kitchen-bulb", "printer", "robot-vacuum"]Una pianificazione della buonanotte che prende come bersaglio il gruppo kids: ogni membro viene risolto con il profilo kids-night tra le 21:00 e le 07:00.
[[schedules]]
id = "kids-bedtime"
display_name = "Bambini: buonanotte"
target_type = "group"
target_id = "kids"
profile = "kids-night"
days = ["mon", "tue", "wed", "thu", "sun"]
hours = "21:00-07:00"groups.d/*.toml (un file per persona: family.toml, iot.toml, guests.toml). Il config.toml master li carica tramite includes. warden group add … --into <file> indirizza la scrittura al file giusto.CLI
Ogni sottocomando di scrittura attiva un hot reload del daemon in esecuzione (swap atomico della config, niente restart). I sottocomandi di sola lettura funzionano anche senza il daemon.
| Comando | Cosa fa |
|---|---|
warden group list | Stampa configured groups (N):, poi una riga per gruppo: <id> "<display_name>" profile=<p> priority=<n> devices=[a,b]. Senza gruppi stampa no groups configured e un suggerimento. |
warden group show <id> | Stampa id, display_name, profile, priority e l’elenco devices del gruppo stesso. I riferimenti inversi groups dal lato dei dispositivi non vengono mostrati. |
warden group add <id> --profile <p> [--display-name <n>] [--priority N] [--devices id1,id2] [--into <file>] | Crea un nuovo gruppo. --profile è obbligatorio e deve riferirsi a un profilo definito. I dispositivi elencati in --devices devono già esistere. Rifiutato se l’id è già usato. |
warden group set <id> <campo> <valore> [--into <file>] | Modifica un campo. Campi supportati: display_name, profile, priority, devices (separati da virgola). Il campo id non è modificabile: vedi rinominare un gruppo. |
warden group remove <id> [--into <file>] | Cancella il gruppo. Rifiutato prima di scrivere se un dispositivo elenca ancora <id> nel campo groups, o se una pianificazione prende il gruppo come bersaglio. L’errore nomina ogni dispositivo o pianificazione coinvolti. Un id inesistente stampa group "<id>" not found — nothing to remove ed esce con 0. |
warden group profile-allow e warden group profile-deny sono ritirati nella 0.44.0. Falliscono senza scrivere nulla. Un gruppo non ha regole proprie: per un’eccezione di gruppo che non si estenda a ogni dispositivo che condivide lo stesso profilo, crea un profilo dedicato e monta su di esso una Custom List. Vedi regole personalizzate (in inglese).
Schemi comuni:
# Crea un gruppo da zero con un paio di dispositivi già dentro
sudo warden group add kids --profile kids \
--display-name "Bambini" \
--priority 10 \
--devices kids-tablet,alice-iphone
# Lega un gruppo a un profilo diverso (ogni membro lo segue)
sudo warden group set kids profile kids-strict
# Aggiungi un dominio alla policy dei bambini
sudo warden custom-list create kids-extra --display-name "Extra bambini"
sudo warden custom-list add kids-extra tiktok.com --deny
sudo warden profile mount kids --custom-list kids-extra
# Rimuovi un gruppo: fallisce in modo esplicito se un dispositivo lo usa ancora
sudo warden group remove guests
# error: group "guests" still appears in the groups field of device(s):
# guest-laptop, guest-phone. Remove the reference first with
# `warden device set <device> groups <remaining-list>`.Dopo add, set o un remove che ha tolto qualcosa, la CLI chiede al daemon di ricaricare e stampa uno di questi quattro messaggi:
daemon accepted the reload request — activation is not correlateddaemon not running — change will take effect on next startnote: change landed on disk but no admin token is available to request a daemon reload. Run `warden token generate`, or `systemctl reload purge-warden` to activate now (SIGHUP is unauthenticated, so it works without a token).warning: change landed on disk but the daemon rejected the reload (<msg>). Check `journalctl -u purge-warden`, then try `systemctl reload purge-warden` — it enters the same reload through SIGHUP instead of IPC. Escalate to `restart` only if that fails too: it stops DNS until the lists reload.
Fonte: src/cli/commands/groups.rs, src/cli/mod.rs → GroupAction, src/cli/commands/ipc_reload.rs → report_reload_outcome().
Rinominare un gruppo
Non esiste warden group rename. Il percorso set non accetta id come campo. Per rinominare un gruppo, modifica a mano la voce [[groups]] nel TOML, aggiorna ogni [[devices]].groups e ogni [[schedules]].target_id che usa il vecchio id, poi esegui sudo warden reload. Se vuoi impedire che il vecchio id venga riusato per 90 giorni, aggiungi a mano una voce [[retired]]; nessun comando la scrive. Per un gruppo appena creato e senza pianificazioni, rimuoverlo e ricrearlo con il nuovo id di solito fa prima.
TUI
La sezione Network ha una schermata Groups. Ci arrivi con g o. Elenca i gruppi configurati e apre i moduli Add, Edit e Delete. I moduli chiamano le stesse funzioni di scrittura di warden group add / set / remove.
| Tab | Come appaiono i gruppi | Tasti utili |
|---|---|---|
| Groups | Una riga per ogni gruppo configurato, con una vista di dettaglio. Delete chiede un solo y / n. | ↑/↓ seleziona, a aggiungi, Enter / e modifica, d / Delete rimuovi, i / Esc apre o chiude il dettaglio. |
| Devices | La scheda di dettaglio del dispositivo selezionato mostra Group: con ogni appartenenza, separate da virgola. Il modulo Edit permette di selezionare più gruppi (Space attiva o disattiva); il modulo Add imposta un solo gruppo. | e modifica, ↑/↓ sposta il focus. |
| Devices, asse di raggruppamento | Il tasto G cicla l’asse di raggruppamento: none → owner → department → profile. L’appartenenza a un gruppo per ora non è un’opzione di raggruppamento; seguila tramite owner o profile, o con il nome del file se tieni un groups.d/*.toml per persona. | G cicla il raggruppamento. |
| Resolver inspector | Si apre con Shift+S da qualsiasi foglia. Dato un IP sorgente, stampa il dispositivo abbinato, il livello a cui è stato risolto e, se è scattato il livello 3, Via group: <id>. È la risposta più veloce a “perché questo dispositivo è su quel profilo?”. | Shift+S apre l’inspector, digita l’IP, Enter. |
Fonte: src/tui/tabs/groups.rs, src/tui/group_modal.rs, src/tui/tabs/devices.rs → group_line(), src/tui/resolver_modal.rs.
API REST
I gruppi non sono esposti via REST. Non c’è un endpoint /api/groups, né CRUD dei gruppi, né una vista di lettura specifica. La config unificata restituita da GET /api/config include [[groups]] in JSON, quindi una dashboard esterna può mostrare l’elenco su disco; ma le scritture passano dalla CLI o dalla TUI, mai dall’API.
GET /api/devices (autenticato con Bearer token) restituisce una riga per dispositivo con il nome del profilo risolto. Le righe non hanno un campo groups; per una vista per gruppo, uniscile agli elenchi [[groups]] di GET /api/config.
IPC
Il protocollo del socket Unix non ha verbi specifici per i gruppi: niente IpcCommand::GroupAdd, GroupUpdate, GroupRemove o GroupList. I gruppi vengono scritti su disco dalla CLI o dalla schermata Groups della TUI, con lo stesso codice: warden group … riscrive il TOML, esegue il validator e poi chiede al daemon in esecuzione di ricaricare con lo stesso IpcCommand::Reload usato da ogni altra modifica della CLI. Se il daemon è spento la scrittura arriva comunque su disco; il daemon legge i nuovi gruppi al prossimo avvio.
L’unico canale IPC che tocca lo stato dei gruppi è DevicePatch.groups: Option<Vec<String>>, usato dal modulo Edit dei dispositivi nella TUI per impostare l’elenco di appartenenza di un dispositivo alla volta. Fonte: src/api/routes.rs → build_router(), src/ipc/protocol.rs → DevicePatch.
Ordine di precedenza
I gruppi stanno al livello 3 della catena del resolver di Warden, lunga 5 livelli. La catena completa, in ordine di esecuzione: vince il primo match. (warden resolve etichetta il profilo diretto come 1 e la pianificazione come 2, anche se la pianificazione viene controllata per prima.)
- Pianificazione attiva. Una voce
[[schedules]]la cui finestra temporale copre adesso prende come bersaglio questo dispositivo o uno dei suoi gruppi. Una pianificazione che bersaglia il dispositivo vince su una che bersaglia uno dei suoi gruppi. - Profilo diretto del dispositivo. L’IP sorgente (o il MAC via ARP) corrisponde a una riga
[[devices]], e il dispositivo haprofile = "...". - Profilo del gruppo. Il dispositivo non ha un
profilediretto ma appartiene a uno o più[[groups]]. Vince il gruppo con la priorità più alta; stessa priorità e profili diversi è un errore del validator (vedi Priorità e spareggio qui sotto). - Match della subnet a prefisso più lungo. Nessun livello precedente ha trovato un match e l’IP sorgente ricade in un CIDR
[[subnets]]. Vince il prefisso più lungo. Questo livello serve le sorgenti senza una riga dispositivo, i dispositivi senza profilo, gruppo o pianificazione, e i dispositivi esclusi dal controllo del MAC (qui sotto). - Fallback globale.
[server].default_profile. Se non è impostato, le query non mappate ricevonoREFUSED.
Se [server].enforce_device_mac = true (il default, prima enforce_client_mac) e la tabella ARP mostra all’indirizzo sorgente un MAC che non è né il mac del dispositivo né uno dei suoi mac_aliases, il dispositivo viene declassato al livello 4. Un dispositivo con ip e senza mac viene escluso allo stesso modo, a meno che la tabella ARP non mostri al suo indirizzo uno dei suoi mac_aliases. I livelli di pianificazione, profilo diretto e gruppo vengono tutti saltati: si salta il legame con il gruppo, non solo il profilo diretto. Un MAC che appartiene a un altro dispositivo configurato non è un declassamento: la query viene risolta come quel dispositivo, con i suoi gruppi. Il declassamento è ergonomico, non crittografico; vedi le note di sicurezza e il controllo del MAC.
Una pianificazione attiva viene controllata prima del profilo del dispositivo, quindi vince dentro la sua finestra.
Fonte: src/profiles/resolver.rs → ProfileResolver::resolve() (il controllo del MAC, poi pianificazione, profilo diretto e gruppo).
Priorità e spareggio
priority è un i32 con default 0. Il valore più alto vince. Sono accettati valori negativi (utili per gruppi di fallback “retrocessi”).
Quando un dispositivo appartiene a due gruppi con la stessa priorità e profili diversi, il validator rifiuta di caricare la config:
device "alice-iphone" is in multiple groups with the same priority (10)
but different profiles: kids → kids, school → school-allow-classroom
help: raise the priority of the winning group, remove the device from
one group, or harmonise the profilesStessa priorità e stesso profilo va bene: il dispositivo viene risolto con quel profilo condiviso, senza conflitti da sciogliere. A livello di implementazione, in caso di pareggio l’ordinamento è per id ASC, ma il validator intercetta il caso ambiguo prima che la risoluzione parta.
Fonte: src/config/schema/validator.rs → check_group_priority_conflicts().
Note di sicurezza
warden group add,seteremovefiniscono nell’audit. Ognuno scrive un recordCliMutationconscope = "group"eaction = "group.add"/"group.set"/"group.remove"(src/cli/commands/groups.rs→add_inner_locked(),set_fields_inner_locked(),remove_inner_locked()). Chi cerca “chi ha creato il gruppokids?” lo trova inaudit.log, non solo nella cronologia git del file. Una modifica a mano del TOML non lascia traccia; i comandi sì.- La rimozione di un gruppo ancora usato viene rifiutata prima di scrivere qualsiasi cosa. La CLI nomina ogni dispositivo che elenca ancora il gruppo nel campo
groups, e ogni pianificazione che lo prende come bersaglio. - L’appartenenza a un gruppo non è un confine di sicurezza. Un dispositivo della LAN che non corrisponde a nessuna riga
[[devices]]ricade sul profilo della subnet o su quello di default: non essere in nessun gruppo significa che si applica il livello 4 o 5, non “bloccato per default”. Per rifiutare i client non mappati, lascia[server].default_profilenon impostato, così la catena restituisceREFUSEDal livello 5. - Il controllo del MAC toglie anche il gruppo. Quando la tabella ARP mostra un MAC che nessun dispositivo configurato possiede, il dispositivo scende alla risoluzione per subnet; il profilo del gruppo viene saltato insieme al profilo diretto. Un MAC che appartiene a un altro dispositivo configurato viene risolto come quel dispositivo, con i suoi gruppi. Un attaccante determinato sulla LAN può falsificare un MAC, ma il controllo ferma un adolescente curioso che cambia il proprio IP statico per uscire dal gruppo dei bambini. Vedi il threat model (in inglese).
- Una Custom List montata sul profilo di un gruppo raggiunge tutto ciò che usa quel profilo. Questo include altri gruppi, dispositivi e il
[server].default_profileche puntano a quel profilo. Se vuoi una regola che valga per un solo gruppo, crea un profilo dedicato.
Risoluzione problemi
Un dispositivo riceve il profilo sbagliato. Chiedilo direttamente al resolver: ti dice quale livello ha trovato il match e, se è scattato il livello 3, quale gruppo.
warden resolve 192.168.1.107
# Source IP: 192.168.1.107
# Matched device: alice-iphone (iPhone di Alice)
# Match level: 3 (group membership)
# Via group: school
# Active profile: school-allow-classroomSe il livello mostrato non è quello che ti aspettavi, risali la catena: il MAC corrispondeva? Era attiva una pianificazione? enforce_device_mac sta causando un declassamento? Il dispositivo dichiara un profile diretto che avevi dimenticato?
Un reload fallisce con “device ‘X’ is in multiple groups with the same priority…”. Hai aggiunto il dispositivo a un secondo gruppo con la stessa priorità di un gruppo esistente, e i due gruppi legano profili diversi. Il validator non può scegliere. Correggi in uno di questi modi: alza una delle priorità, togli il dispositivo da uno dei gruppi, oppure rendi uguali i due profili.
Un reload fallisce con groups[i] "X".profile "Y" is not defined. Il gruppo si riferisce a un profilo che non esiste: un errore di battitura, oppure il profilo è stato rimosso senza aggiornare il gruppo. Esegui warden config lint per l’elenco completo, poi correggi il refuso oppure usa warden group set <id> profile <profilo-esistente>.
Un reload fallisce con devices[i] "X".groups references "Y" which is not defined. Un dispositivo si riferisce a un gruppo non dichiarato. Stessa diagnosi: refuso o riferimento rimasto indietro. Correggi con warden device set X groups <nuova-lista>, oppure modifica a mano il file del dispositivo.
warden group remove viene rifiutato. Qualche dispositivo ha ancora il gruppo nel campo groups. L’errore nomina ogni dispositivo coinvolto. Riassegnali con warden device set <dispositivo> groups <lista-rimanente>, oppure modifica a mano il relativo devices.d/*.toml. Se una voce [[schedules]] prende il gruppo come bersaglio, la CLI rifiuta con group "<id>" is still the target of schedule(s): …; rimuovila prima con warden schedule remove <id-pianificazione>.
Un dispositivo presente solo in [[groups]].devices non riceve il profilo del gruppo. Dovrebbe riceverlo: il resolver unisce i due lati al caricamento. Se non succede, esegui warden resolve <ip> per vedere il livello effettivo della catena, e verifica che l’id del dispositivo nell’elenco devices del gruppo sia identico a [[devices]].id (il validator intercetta le discrepanze al caricamento, ma di solito la causa è un riferimento vecchio in un file modificato a mano).
Per altro, vedi risoluzione problemi (in inglese).
Vedi anche
- Dispositivi: le voci che i gruppi tengono insieme.
- Profili: ciò a cui si riferisce il campo
profiledi un gruppo. - Subnet: il profilo di fallback per gli host che non sono affatto in
[[devices]]. - Pianificazioni: il cambio di profilo a orario, che può prendere come bersaglio un gruppo con
target_type = "group". - Regole personalizzate (in inglese): le Custom List, l’unico posto dove stanno le regole.
- Riferimento CLI: l’intera superficie
warden group ….