Files
new-landing-page-homenas/README_Pihole.md
T
2026-06-29 13:09:54 +02:00

8.7 KiB
Raw Blame History

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 <a routerLink="/pihole">Pi-hole</a>
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:

// 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.

ng serve --proxy-config proxy.conf.json

proxy.conf.json (relevanter Block):

{
  "/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

# 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/<projektname>/ /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: <NAS-IP>
  • Forward Port: 9999
  • Block Common Exploits: an

Advanced → Custom Nginx Configuration:

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:

{ "session": { "valid": true, "sid": "<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": <sek> | null }

Blocking setzen

POST /api/dns/blocking
Body: { "blocking": true|false, "timer": <sek>|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:

docker exec <proxy-container> 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.