AdGuard Home — agh-bf1 (.36) / agh-bf2 (.37) · TP23 Stufe 2/4/5
Das AGH-Paar (pve LXC 110/111, 10.40.20.36 / .37) ist der Client-Resolver des BF-Netzes
(DHCP liefert beide in allen 7 VLANs). Upstream: DoH zum CF-Gateway; conditional
[/bf.emsenhuber.net/] → 10.40.20.1. Kein natives HA → Sync ist Betriebsaufwand.
Dateien
| Datei | Zweck |
|---|---|
AdGuardHome.yaml |
Repo-SSoT der Config. Passwort-Hashes REDIGIERT ('<redacted>') — keine Secrets im Git. Echte Hashes leben auf den Geräten; Klartext des API-Users in ~/.secrets. |
agh-sync.py |
check (Drift .36↔.37) · push (SSoT auf beide, gestaffelter Restart, reinjiziert Hashes live). |
agh-exception.py |
Exception-Primitive (Stufe 5) gegen beide Instanzen. |
Client-Policy (Stufe 4, clients.persistent)
Ein VLAN = eine CIDR-Gruppe. Alle nutzen denselben CF-Upstream-Filter; Divergenz = per-Client-Toggle.
| Gruppe | CIDR | filtering | safe_search | parental |
|---|---|---|---|---|
| grp-main | 10.40.10.0/24 | an (global) | – | – |
| grp-infra | 10.40.20.0/24 | aus | – | – |
| grp-iot | 10.40.30.0/24 | an | – | – |
| grp-media | 10.40.40.0/24 | aus | – | – |
| grp-untrusted | 10.40.50.0/24 | an | – | – |
| grp-guest | 10.40.90.0/24 | an | on | on |
| grp-mgmt | 10.40.99.0/24 | aus | – | – |
| dns-bypass-exempt | 10.40.10.183 + .82 | aus | – | – |
filtering aus bei INFRA/MEDIA/MGMT = bewusste Reduktion (Streaming/Server/Mgmt nicht brechen), per Gruppe rückholbar.
Personen-Clients (Stufe 5 Redesign 2026-09-12) — SSoT für Person→Gerät
Zusätzlich zu den VLAN-CIDR-Gruppen bekommt jede Person einen eigenen AGH-Client
(/control/clients): name = Personenname, ids = alle Geräte-IPs der Person. Das ist der
einzige Ort, an dem Person→Gerät gepflegt wird (kein zweites Mapping-File). Damit:
- wirken Self-Service-Ausnahmen pro Person:
@@||domain^$client='Personenname'statt global; - filtert
recent-blocked --client <person>das Query-Log auf ihre Geräte; - bleiben Ausnahmen stabil bei Gerätewechsel (Name statt IP).
🔴 On-network verifizieren, bevor Stufe 6 scharf geht: dass AGH $client= auf Allow-Regeln
(@@) in der laufenden Version greift. Fällt der Test negativ aus → Fallback $client=<ip>|<ip>
(die Geräte-IPs); agh-exception.py scoped_rule() kann beides. Die CIDR-Gruppen (Policy-Grenze)
bleiben unberührt — Personen-Clients tragen nur die Ausnahmen.
Sync-Invariante
AGH schreibt die YAML beim Restart um (normalisiert) → nicht gegen die Repo-Datei diffen,
sondern .36 == .37. Genau das prüft agh-sync.py check.
Exception-Contract (Stufe 5 — was die HA-Schicht auslöst)
Auth: Basic, User automation (~/.secrets → AGH_AUTOMATION_*). Immer auf .36 UND .37.
🔴 Wichtig für D: die beiden AGH-Endpunkte sind read-modify-write, kein simples Append —
set_rules ersetzt die ganze Liste, clients/update braucht das volle Client-Objekt.
Ein zustandsloses HA-rest_command reicht deshalb nicht; agh-exception.py ist die
Referenz-RMW-Implementierung, die D aufruft (Deploy-Weg auf HAOS in Etappe D zu entscheiden:
shell_command/command_line gegen die deployte CLI oder ein dünner Wrapper-Endpunkt).
| Aktion | CLI | AGH-API (RMW) |
|---|---|---|
| Domain pro Person entsperren | allow-domain <d> --client <person> |
GET /control/filtering/status → +@@||<d>^$client='<person>' → POST /control/filtering/set_rules |
| Domain global entsperren (Admin) | allow-domain <d> |
dito, ohne $client |
| Entsperrung zurück | unallow-domain <d> [--client <person>] |
dito, exakt dieselbe Regel entfernen |
| „meine Ausnahmen" | list-exceptions --client <person> --json |
GET /control/filtering/status, @@-Regeln parsen/filtern |
| „zuletzt geblockt" | recent-blocked --client <person> --json |
GET /control/querylog?response_status=blocked, client-seitig auf Geräte-IPs gefiltert |
| Client entfiltern (grob) | unfilter <ip> |
GET /control/clients → Objekt, filtering_enabled=false → POST /control/clients/update |
| Client wieder filtern | refilter <ip> |
dito, zurück auf global |
Reine Regel-/Filter-Logik ist netzunabhängig getestet: python3 agh-exception.py selftest.
Kein Timing in der CLI — Ablauf/Revert macht der Aufrufer (HA-Timer) und, ab Stufe 6, der RouterOS-Backstop.
RouterOS-Backstop (Stufe 6, noch NICHT gebaut)
Analog jit-admin (routeros/tp5-etappe4-input-mgmt.rsc): Address-List jit-dns-bypass mit
timeout + Forward-Regel, die dieser Liste 53/853 nach außen erlaubt — der Fail-safe, falls HA
den Revert verpasst. Gebaut via Nautobot GC (kein Direkt-Write), erst wenn die Bypass-Sperre
scharf geschaltet wird.