Stufe 5 reopened — DAU-Requirements (pro Person, "zuletzt geblockt -> 1 Klick", temp+dauerhaft umschaltbar, scoped HA-Dashboard) nicht erfuellt → Stufe 6 gated. agh-exception.py: $client-scoped allow/unallow, list-exceptions, recent-blocked (selftest OK, reine Logik netzunabhaengig getestet). HA-Bau-Spec + Personen- Client-Modell dokumentiert. Bau/Abnahme on-network offen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
7.0 KiB
TP23 Stufe 5 (Redesign 2026-09-12) — DAU-tauglicher, personenbezogener DNS-Self-Service
🔴 Status: Bau-Spec, noch nicht deployt. Die alte Fassung (single-domain, temp-only, HA-Neustart bei neuer Domain) war „funktional ein kastriertes Pi-hole" (User-Befund 2026-09-12) und ist ersetzt. Bau + Abnahme laufen on-network (HAOS
10.40.20.105, AGH.36/.37) — beim Anlegen den home-assistant-best-practices-Skill konsultieren und via ha-mcp in.storageanlegen (native Helper,entity_idstattdevice_id). Diese Datei ist die Repro-/SSoT-Quelle.
Ziel (User-Requirements 2026-09-12)
Ein einfaches, klar verständliches End-User-UI zum Entsperren:
- alles was den Nutzer betrifft, nichts von anderen / Admin / Power-User;
- Policy nur sichtbar, wo sie für den einfachen Nutzer Relevanz hat.
| # | Requirement | Umsetzung |
|---|---|---|
| 1 | Pro Person (Login), Sicht über alle Geräte | HA-User = Person; Person → AGH-Client (Name), dessen ids = ihre Geräte-IPs |
| 2 | „zuletzt geblockt" → 1 Klick | recent-blocked --client <person> aus dem AGH-Query-Log, auf die Geräte der Person gefiltert |
| 3 | temporär UND dauerhaft, Modus umschaltbar | Dauer-Select inkl. „dauerhaft"; input_boolean self-permanent ⇄ Anfrage. Start: self-permanent |
| 4 | HA-Dashboard, scoped pro Person | ein View je Person, nur für ihre HA-User-ID sichtbar |
Identitätsmodell — AGH-Client je Person ist die SSoT
- Pro Person ein AGH-Client (
/control/clients),name= Personenname,ids= alle Geräte-IPs der Person (die per DHCP faktisch statisch sind). Damit ist Person→Gerät an einem Ort gepflegt (kein zweites Mapping-File), und Ausnahmen sind stabil bei Gerätewechsel. - Ausnahme-Regel =
@@||domain^$client='Personenname'(statt globalem@@||domain^). 🔴 Vor Scharfschalten on-network verifizieren, dass AGH$client=auf@@-Regeln wirkt (s.agh-exception.pyKopf). Fällt der Test negativ aus: Fallback =$client=<ip>|<ip>mit den Geräte-IPs statt dem Namen (Code kann beides,scoped_rule()). - Die bestehenden VLAN-CIDR-Gruppen bleiben die Policy-Grenze (Filter an/aus je VLAN); Personen-Clients sind spezifischer und tragen nur die Ausnahmen.
CLI (deployt nach /config/agh-exception.py, wirkt auf .36+.37)
allow-domain <domain> --client <person> # Ausnahme setzen (temp: HA revertiert; perm: kein Timer)
unallow-domain <domain> --client <person> # dieselbe Ausnahme entfernen
list-exceptions --client <person> --json # „meine Ausnahmen" (für Übersicht + Entfernen)
recent-blocked --client <person> --json # „zuletzt geblockt" (nur Geräte der Person)
--client nimmt einen AGH-Client-Namen (→ IPs via /control/clients) ODER eine Komma-IP/CIDR-Liste.
configuration.yaml — shell_command (einziger YAML-Teil, Backup .bak-tp23-*)
Domain und Client sind Parameter → kein HA-Neustart mehr bei neuer Domain (das war der alte
DAU-Killer: pro Domain ein eigener shell_command).
shell_command:
agh_allow: "python3 /config/agh-exception.py allow-domain '{{ domain }}' --client '{{ client }}'"
agh_unallow: "python3 /config/agh-exception.py unallow-domain '{{ domain }}' --client '{{ client }}'"
Helfer / Entities (via ha-mcp in .storage)
Pro Person (Beispiel christoph) — bei mehreren Personen je einmal, Suffix = Personen-Slug:
| Typ | entity_id | Zweck |
|---|---|---|
| command_line sensor | sensor.dns_recent_blocked_<slug> |
recent-blocked --client <person> --json → json_attributes: items (Liste {domain,count,last}); Scan-Intervall ~60 s |
| command_line sensor | sensor.dns_exceptions_<slug> |
list-exceptions --client <person> --json → aktive Ausnahmen der Person |
| input_select | input_select.dns_dauer_<slug> |
1 Stunde / 4 Stunden / dauerhaft |
| timer | timer.dns_ausnahme_<slug> |
JIT-Countdown je temporärer Freigabe (restore=false) |
| input_boolean | input_boolean.dns_permanent_ohne_anfrage |
global: an = „dauerhaft" wirkt sofort (Start); aus = erzeugt Admin-Genehmigung |
| script | script.dns_freigeben_<slug> |
nimmt domain als Feld → agh_allow; bei „dauerhaft"+Switch-aus → todo/Notification statt Freigabe; sonst Timer starten |
| automation | automation.dns_ablauf_<slug> |
timer.finished/.cancelled → agh_unallow, Aufräumen |
Dashboard — ein scoped View je Person
- Eigener storage-Dashboard-View, nur für die HA-User-ID der Person sichtbar (
visible:mit der user-id;require_admin:false). Kleiner Haushalt → ein View je Person ist praktikabel und umgeht das „Template kennt den aktuellen Betrachter nicht"-Problem. - Karte 1 „Zuletzt geblockt (antippen zum Freigeben)": rendert
sensor.dns_recent_blocked_<slug>Attributitems; jede Zeile hat einetap_action→script.dns_freigeben_<slug>mitdomainder Zeile + aktuellerinput_select-Dauer. (Dynamische Per-Zeilen-tap_action braucht i.d.R.auto-entities+template/button-cardoder ein kleines Custom-List-Template — beim Bau on-network entscheiden, das ist der Teil, der Live-Verifikation braucht.) - Karte 2 „Meine Ausnahmen":
sensor.dns_exceptions_<slug>als Liste, je Eintrag Entfernen (agh_unallow). - Karte 3 „Dauer":
input_select.dns_dauer_<slug>(inkl. „dauerhaft"). - Kein globaler Status, keine fremden Clients, keine Policy-Interna. Nur wenn relevant ein kurzer Hinweis („durch den Familien-Filter blockiert").
Genehmigungs-Variante (Modus-Switch aus)
input_boolean.dns_permanent_ohne_anfrage = off → „dauerhaft"-Klick legt statt der Freigabe einen
Eintrag in einer todo-Liste „DNS-Freigaben zu prüfen" an (+ Notification an Christoph). Beim
Abhaken führt eine Admin-Automation agh_allow ohne Timer aus. Beide Varianten sind gebaut; der
Switch entscheidet zur Laufzeit.
Alt-Reste, die beim Bau wegfallen/fixen
- Single-domain-Flow (
input_text.dns_ausnahme_domain/_aktiv,input_button…_starten,automation…_starten/_ablauf) und deragh_allow_domain/_unallow_domain-pro-Domain-Zwang. - Statuskachel-Sensor
binary_sensor.adguard_bf1_statushing am toten.16→ auf.36/.37neu.
Abnahme (on-network, = neue echte Stufe-6-Vorbedingung)
Mit einem echten Nicht-Admin-Login: geblockte Seite → erscheint in ihrer Fläche → 1 Klick
temporär (revertiert nach Ablauf) und 1 Klick dauerhaft (bleibt) → wirkt nur für diese
Person (Gegentest: anderer Client sieht die Domain weiter geblockt). .36 == .37
(agh-sync.py check). Modus-Switch beide Wege einmal durchgespielt.
Verweise
- CLI:
adguard/agh-exception.py(Subcommands +selftest). Deploy-Weg unverändert (base64→scp, s. unten). Creds/config/agh-exception.secrets(chmod 600). - Redeploy:
base64 < adguard/agh-exception.py | ssh admin@10.40.20.105 "base64 -d | sudo tee /config/agh-exception.py >/dev/null". - Personen-Client-Modell:
adguard/README.md. Kontext/Reihenfolge: TP23 indocs/teilprojekte.md.