# Pi-hole-Steuerung Eigene Seite in der Landingpage zum Steuern von Pi-hole (v6) über dessen REST-API: Blocking ein-/ausschalten, für 30 Minuten deaktivieren und den aktuellen Status anzeigen (rot = Blocking aktiv, grün = kein Blocking). --- ## Funktionsumfang - **Status-Anzeige**: farbiges Badge - rot = Blocking aktiv - grün = kein Blocking - grau = unbekannt / nicht abrufbar - **Schalter Blocking an/aus**: aktiviert bzw. deaktiviert das Blocking dauerhaft. - **Schalter "30 Min deaktivieren"**: deaktiviert das Blocking temporär für 30 Minuten (Pi-hole-Timer = 1800 s). Live-Countdown (mm:ss) bis zur automatischen Reaktivierung. - Erreichbar unter der Route `/#/pihole` (Hash-Routing). --- ## Architektur ``` Browser (Angular SPA) │ relative Calls: /pihole/api/... ▼ Reverse-Proxy (Dev: ng serve proxy.conf.json | Prod: Nginx Proxy Manager) │ strippt /pihole/ -> /api/... ▼ Pi-hole v6 REST-API http://10.0.0.60:8088/api/... ``` **Wichtigstes Designprinzip — Same-Origin:** Die App ruft die API **relativ** unter `/pihole/api/...` auf, also immer auf demselben Host/Port, von dem die Seite geladen wurde. Dadurch gibt es **kein CORS**. Ein vorgeschalteter Reverse-Proxy leitet `/pihole/...` an die echte Pi-hole-API weiter und entfernt dabei das `/pihole`-Präfix. --- ## Dateien | Datei | Zweck | |---|---| | `src/app/pihole.interface.ts` | TypeScript-Typen für Status/Auth-Antworten | | `src/app/pihole.service.ts` | API-Service: Auth (SID), Status lesen, Blocking setzen | | `src/app/pihole-control/pihole-control.component.ts` | Komponentenlogik, Status, Countdown | | `src/app/pihole-control/pihole-control.component.html` | Template (Badge, Schalter) | | `src/app/pihole-control/pihole-control.component.css` | Styles (rot/grün/grau) | | `src/app/app.module.ts` | Deklaration der Komponente | | `src/app/app-routing.module.ts` | Route `{ path: 'pihole', component: PiholeControlComponent }` | | `src/app/app.component.html` | Navigationslink `Pi-hole` | | `src/environments/environment.ts` | Dev: `piholePassword` | | `src/environments/environment.prod.ts` | Prod: `piholePassword` | | `proxy.conf.json` | Dev-Proxy für `/pihole` | | `webserver-docker/nginx.conf` | Prod-Nginx (nur relevant, wenn NICHT über NPM proxyt wird) | > Das Feature folgt demselben Muster wie die bestehende `tasmota-control`- > Komponente (Service + Interface + Component). --- ## Konfiguration Das Pi-hole-Passwort steht in den Environment-Dateien: ```ts // src/environments/environment.ts (Dev) // src/environments/environment.prod.ts (Prod) export const environment = { production: true, // bzw. false in Dev // ... bestehende Felder ... piholePassword: 'DEIN_PIHOLE_PASSWORT' }; ``` > **Sicherheitshinweis:** Das Passwort wird ins JS-Bundle einkompiliert und ist > damit im Browser lesbar. Das ist für ein reines LAN/VPN-Tool vertretbar. > Für breitere Exposition: dediziertes Pi-hole **App-Passwort** > (Pi-hole → Settings → API → App password) statt des Admin-Passworts, oder > serverseitige Authentifizierung im Reverse-Proxy (Secret verlässt den > Server nicht). Die Pi-hole-Adresse (`http://10.0.0.60:8088`) ist im Reverse-Proxy konfiguriert, **nicht** im Frontend. --- ## Entwicklung (Dev) `ng serve` proxyt `/pihole` über `proxy.conf.json` an die Pi-hole-API. ```bash ng serve --proxy-config proxy.conf.json ``` `proxy.conf.json` (relevanter Block): ```json { "/pihole": { "target": "http://10.0.0.60:8088", "secure": false, "changeOrigin": true, "pathRewrite": { "^/pihole": "" } } } ``` > Änderungen an `proxy.conf.json` oder den Environment-Dateien erfordern einen > Neustart von `ng serve`. Aufruf: `http://localhost:4200/#/pihole` --- ## Produktion (Prod) ### Setup-Überblick - Auslieferung: Container `angular-web` (`nginx:stable-alpine`) auf der Synology-NAS. Fertiges Bundle in `/volume1/docker/landingpage/dist` (read-only gemountet), Host-Port `9999` → Container `80`. - Reverse-Proxy für `/pihole`: **Nginx Proxy Manager (NPM)** auf separatem Host (Variante A — ein Host, Custom Location). ### Bundle bauen und ausrollen ```bash # 1. Passwort in src/environments/environment.prod.ts eintragen (siehe Konfiguration) # 2. Prod-Build ng build --configuration production # 3. dist nach /volume1 kopieren (Pfad an angular.json anpassen) rsync -a --delete dist// /volume1/docker/landingpage/dist/ # 4. Container neu laden (statische Files sind sofort aktuell; Reload nur bei conf-Änderung nötig) docker exec angular-web nginx -s reload ``` ### Reverse-Proxy via Nginx Proxy Manager (Variante A) Landingpage und Pi-hole-API laufen über **denselben** NPM-Host (same-origin), damit der relative `/pihole/api`-Aufruf ohne CORS funktioniert. **Proxy Host (Details):** - Domain Names: z. B. `homenas.deine.domain` - Scheme: `http` - Forward Hostname / IP: `` - Forward Port: `9999` - Block Common Exploits: an **Advanced → Custom Nginx Configuration:** ```nginx location /pihole/ { proxy_pass http://10.0.0.60:8088/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-FTL-SID $http_x_ftl_sid; proxy_buffering off; proxy_read_timeout 30s; proxy_connect_timeout 5s; } ``` > Der **trailing slash** in `proxy_pass http://10.0.0.60:8088/;` strippt das > `/pihole/`-Präfix → Pi-hole sieht `/api/...` (äquivalent zum Dev > `pathRewrite "^/pihole" -> ""`). Ohne den trailing slash gibt es 404. > Da NPM das `/pihole`-Proxying übernimmt, ist **kein** `/pihole`-Block in > `webserver-docker/nginx.conf` nötig. Der Block dort wird nur gebraucht, wenn > der NAS-Nginx selbst proxyt (ohne NPM). Aufruf: `https://homenas.deine.domain/#/pihole` --- ## Pi-hole v6 REST-API (Referenz) Basis: `http://10.0.0.60:8088/api` ### Authentifizierung ``` POST /api/auth Body: {"password":"..."} ``` Antwort: ```json { "session": { "valid": true, "sid": "", "validity": 1800, "message": null } } ``` > **Fallstrick:** Bei falschem Passwort liefert Pi-hole `valid:true`, aber > `sid:null` und `message:"password incorrect"`. Der Service prüft daher auf > das Vorhandensein einer **`sid`**, nicht nur auf `valid`. Die `sid` wird bei Folge-Requests im Header `X-FTL-SID` mitgeschickt. ### Status lesen ``` GET /api/dns/blocking -> { "blocking": "enabled" | "disabled", "timer": | null } ``` ### Blocking setzen ``` POST /api/dns/blocking Body: { "blocking": true|false, "timer": |null } ``` - Blocking an: `{"blocking": true, "timer": null}` - Blocking aus: `{"blocking": false, "timer": null}` - 30 Min aus: `{"blocking": false, "timer": 1800}` Offizielle Doku: https://docs.pi-hole.net/api/ --- ## Service-Methoden (`pihole.service.ts`) - `authenticate()` – holt eine SID; wirft bei Fehler mit der echten Pi-hole-Meldung (z. B. "password incorrect"). - `ensureSid()` – stellt sicher, dass eine gültige SID vorliegt. - `getStatus()` – aktueller Blocking-Status inkl. Timer. - `isBlockingEnabled()` – Bool-Kurzform. - `setBlocking(enabled, timerSeconds?)` – setzt den Status. - `enableBlocking()` / `disableBlocking()` – Komfort-Wrapper. - `disableFor30Minutes()` – deaktiviert mit Timer 1800 s. - SID wird bei `401` einmal automatisch erneuert. --- ## Troubleshooting | Symptom | Ursache | Lösung | |---|---|---| | 404 beim Statusabruf | `/pihole`-Proxy fehlt oder trailing slash falsch | Custom Location in NPM prüfen; `proxy_pass ...:8088/` mit trailing slash | | "kein gültiges SID" / "password incorrect" | falsches Passwort | korrektes Admin-/App-Passwort in `environment(.prod).ts`; per `pihole setpassword` neu setzen | | Status bleibt grau / nicht abrufbar | Proxy erreicht Pi-hole nicht | Erreichbarkeit `10.0.0.60:8088` aus dem Proxy-Container testen | | Funktioniert in Dev, nicht in Prod | NPM-Host erreicht NAS/Pi-hole nicht | vom NPM-Container `wget` auf `:9999` und `10.0.0.60:8088` testen | | Falsche Port-Annahme (80) | API läuft auf `:8088`, nicht `:80` | überall `http://10.0.0.60:8088` verwenden | **Erreichbarkeitstest aus dem Reverse-Proxy-Container:** ```bash docker exec sh -c \ 'wget -qO- http://10.0.0.60:8088/api/auth; echo " exit=$?"' ``` Eine JSON-Antwort (auch "password incorrect") beweist, dass das Netz steht. --- ## Sicherheit - Passwort im Bundle ist im Browser lesbar → nur für LAN/VPN geeignet. - Den `/pihole`-Proxy bei breiterer Erreichbarkeit absichern (IP-Whitelist `allow/deny` oder Basic-Auth in NPM). - Pi-hole-API möglichst nur über den Reverse-Proxy erreichbar machen, `10.0.0.60:8088` nicht direkt nach außen exponieren.