Move 09-gast-zugang.md to wiki
This commit is contained in:
parent
937d719ba4
commit
ebb920f3e3
1 changed files with 0 additions and 240 deletions
|
|
@ -1,240 +0,0 @@
|
||||||
# Gast-Zugang: Read-only Showcase für Freunde/Bekannte und Bewerbungsgespräche — Rebuild-Runbook
|
|
||||||
|
|
||||||
Cross-cutting, kein eigener Host: nutzt CT101 (NAS), CT104 (Monitoring) und VPS (Caddy). Die Sicherheitsdiskussion, die zu diesem Design geführt hat: siehe `ENTSCHEIDUNGEN.md`, Abschnitt „Warum der Gast-Zugang eine eigene, komplett neue Oberfläche bekam".
|
|
||||||
|
|
||||||
## Prinzip
|
|
||||||
|
|
||||||
Zwei komplett read-only Showcase-Konten (`gast-freunde`, `gast-bewerbung`), standardmäßig **deaktiviert**, manuell vor einem Termin aktiviert (plus optionales Ablaufzeit-Sicherheitsnetz). Kein Terminal, kein Schreibzugriff, keine Sicht auf interne Login-/Zugriffs-Daten. Bewusst **kein** Zugriff auf den bestehenden Management-Desktop (CT102) — der hat ein Terminal und sitzt im flachen LAN, für Gäste zu riskant.
|
|
||||||
|
|
||||||
Drei Bausteine:
|
|
||||||
1. **Grafana-Gast-Dashboard** (CT104) — kuratierte Kopie ohne Login-Daten, plus fail2ban-Statistik als Sicherheits-Showcase
|
|
||||||
2. **Zweite Filebrowser-Instanz** (CT101) — eigener Port/eigene DB, read-only, zeigt nur `/srv/gast-demo` (= die genericierten Portfolio-Runbooks)
|
|
||||||
3. **Uptime-Kuma-Status-Page** (CT104) — öffentliche Ampel-Ansicht ohne technische Details
|
|
||||||
|
|
||||||
Alle drei über neue öffentliche Caddy-Vhosts auf dem VPS erreichbar (gleiches Muster wie `guac.<DOMAIN>`).
|
|
||||||
|
|
||||||
## 1. Grafana: Ordner-getrenntes Gast-Dashboard
|
|
||||||
|
|
||||||
Grafanas eingebaute Rolle „Viewer" reicht **nicht** als Isolation — ein Viewer sieht standardmäßig alle Dashboards der Org. Für echte Trennung: zwei Ordner mit gebrochener Berechtigungs-Vererbung.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Dashboards in getrennte Unterordner der Provisionierung legen
|
|
||||||
mkdir -p /opt/monitoring/grafana-provisioning/dashboards/intern /opt/monitoring/grafana-provisioning/dashboards/gast
|
|
||||||
mv .../dashboards/homelab.json .../dashboards/intern/
|
|
||||||
mv .../dashboards/homelab-gast.json .../dashboards/gast/
|
|
||||||
```
|
|
||||||
|
|
||||||
`provider.yaml` mit zwei Providern (je eigenem `folder:` und `path:`), danach `docker compose restart grafana` (reine Datei-Provisionierung reicht sonst nicht für neue Ordner-Zuordnung).
|
|
||||||
|
|
||||||
Anschließend Ordner-Berechtigungen umbauen (klassische Permissions-API, **nicht** die neue `/api/access-control/...`-RBAC-API — letztere zeigt zwar den Ist-Zustand an, ist aber für Änderungen umständlicher):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Intern: Viewer-Rolle komplett entfernen -> kein Viewer-Konto sieht das je automatisch
|
|
||||||
curl -u admin:$PW -X POST http://localhost:3000/api/folders/$INTERN_UID/permissions \
|
|
||||||
-d '{"items":[{"role":"Editor","permission":2}]}'
|
|
||||||
|
|
||||||
# Gast: Viewer-Rolle raus, stattdessen nur die zwei konkreten User explizit erlauben
|
|
||||||
curl -u admin:$PW -X POST http://localhost:3000/api/folders/$GAST_UID/permissions \
|
|
||||||
-d '{"items":[{"role":"Editor","permission":2},{"userId":2,"permission":1},{"userId":3,"permission":1}]}'
|
|
||||||
```
|
|
||||||
|
|
||||||
Nutzer anlegen (Rolle Viewer ist bei Admin-erstellten Usern bereits Default):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -u admin:$PW -X POST http://localhost:3000/api/admin/users \
|
|
||||||
-d '{"name":"Gast Freunde","login":"gast-freunde","password":"...","OrgId":1}'
|
|
||||||
```
|
|
||||||
|
|
||||||
⚠️ **NICHT TUN:** sich auf `isDefault`/Standard-Ordner-Vererbung verlassen und nur ein Dashboard ohne Ordner-Trennung anlegen — ein Viewer-Account sieht dann trotzdem alle anderen Dashboards der Instanz mit (bestätigt: `curl -u gast-freunde ... /api/dashboards/uid/homelab-overview` gab vor der Ordner-Trennung 200 zurück, keine echte Isolation).
|
|
||||||
|
|
||||||
Deaktivieren/Aktivieren eines Users:
|
|
||||||
```bash
|
|
||||||
curl -u admin:$PW -X POST http://localhost:3000/api/admin/users/$ID/enable # bzw. /disable
|
|
||||||
```
|
|
||||||
|
|
||||||
### Echte Hostnamen aus den Legenden entfernt (2026-07-20, Nutzerwunsch)
|
|
||||||
|
|
||||||
Die Ressourcen-Panels (CPU/RAM/Disk je LXC) wurden zunächst 1:1 aus dem internen Dashboard übernommen — die Grafana-Legende zeigt dabei den `host`-Tag aus InfluxDB, also die echten Container-Hostnamen (`adguard`, `nas`, `browser`, `vaultwarden`, `monitoring`) bzw. den PVE-Hostnamen (`<PVE_HOSTNAME>`). Auf Nutzerwunsch durch **Rollen-Bezeichnungen statt Servernamen** ersetzt — man soll erkennen können, *was* läuft (DNS-Blocking, NAS, Passwort-Manager, …), ohne die echten internen Namen zu erfahren. Umsetzung per Flux `map()`, das den `host`-Wert vor dem finalen `keep()` umschreibt:
|
|
||||||
|
|
||||||
```flux
|
|
||||||
|> map(fn: (r) => ({ r with host:
|
|
||||||
if r.host == "adguard" then "DNS / Ad-Blocking"
|
|
||||||
else if r.host == "nas" then "NAS / Dateiablage"
|
|
||||||
else if r.host == "browser" then "Remote-Desktop"
|
|
||||||
else if r.host == "vaultwarden" then "Passwort-Manager"
|
|
||||||
else if r.host == "monitoring" then "Monitoring-Stack"
|
|
||||||
else r.host }))
|
|
||||||
|> keep(columns: ["_time", "host", "_value"])
|
|
||||||
```
|
|
||||||
|
|
||||||
Der PVE-Host selbst hat nur eine Zeile (kein Tag-Vergleich nötig): `map(fn: (r) => ({ r with host: "Hauptserver" }))`. Zusätzlich Panel-**Titel** generisiert, die Hardware-Marken/Dataset-Namen im Klartext hatten: „Esprimo (PVE-Host)" → „Hauptserver", „Intenso NVMe" → „NVMe-Speicher", „nas/freigabe"/"nas/backups" → „Dateiablage"/"Backup-Speicher", „VPS" → „VPN-Gateway". Das interne Dashboard (`Intern`-Ordner) bleibt bewusst unverändert mit den echten Namen — nur die Gast-Kopie wurde angepasst. Per Live-Query gegen InfluxDB verifiziert: alle fünf LXC-Rollen + Hauptserver lösen korrekt auf, keine rohen Hostnamen mehr in den Ergebnissen.
|
|
||||||
|
|
||||||
⚠️ **NICHT TUN:** Rollen-Namen so generisch wählen, dass der Showcase-Zweck verloren geht (z.B. "System A"/"System B"). Ziel war ausdrücklich, dass ein Betrachter weiterhin erkennt, *welche Art* Dienst läuft — nur der konkrete interne Name/Hostname soll verborgen bleiben.
|
|
||||||
|
|
||||||
## 2. Zweite Filebrowser-Instanz auf CT101
|
|
||||||
|
|
||||||
Eigener Systemd-Unit, eigene DB, eigener Port, eigener dedizierter Linux-User (kein Zugriff auf sonstige Homelab-Pfade — Defense in Depth für den einzigen wirklich öffentlich erreichbaren Datei-Endpunkt):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
useradd --system --no-create-home --shell /usr/sbin/nologin gastdemo
|
|
||||||
mkdir -p /srv/gast-demo /var/lib/filebrowser-gast
|
|
||||||
chown -R gastdemo:gastdemo /srv/gast-demo /var/lib/filebrowser-gast
|
|
||||||
# Inhalt: die genericierten Portfolio-Runbooks (siehe Abschnitt 4)
|
|
||||||
|
|
||||||
runuser -u gastdemo -- filebrowser config init -d /var/lib/filebrowser-gast/filebrowser.db --root /srv/gast-demo
|
|
||||||
runuser -u gastdemo -- filebrowser users add gast-freunde '...' -d /var/lib/filebrowser-gast/filebrowser.db \
|
|
||||||
--perm.admin=false --perm.execute=false --perm.create=false --perm.rename=false \
|
|
||||||
--perm.modify=false --perm.delete=false --perm.share=false --perm.download=true --scope /
|
|
||||||
```
|
|
||||||
|
|
||||||
Systemd-Unit bindet **nur an die Tailscale-IP** (`<TS_IP_NAS>:8081`), nicht ans LAN — der einzige Zugriffsweg ist über den VPS-Caddy-Vhost + Tailscale, LAN-Nutzer erreichen die Instanz gar nicht:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
[Service]
|
|
||||||
User=gastdemo
|
|
||||||
Group=gastdemo
|
|
||||||
ExecStart=/usr/local/bin/filebrowser -d /var/lib/filebrowser-gast/filebrowser.db -a <TS_IP_NAS> -p 8081
|
|
||||||
Restart=on-failure
|
|
||||||
```
|
|
||||||
|
|
||||||
⚠️ **NICHT TUN:** `--perm.execute` vergessen. Der `users add`-Default für Execute ist `true` (Command-Runner-Feature) — beim ersten Anlegen übersehen, per `users update ... --perm.execute=false` nachgezogen. Immer nach dem Anlegen mit `filebrowser users ls -d ...` verifizieren, dass wirklich nur `Download=true` gesetzt ist.
|
|
||||||
|
|
||||||
⚠️ **NICHT TUN:** die bestehende NAS-Freigabe read-only exponieren wollen. Ein Berechtigungs-Bug in der Konfiguration würde dann echte Daten zeigen. Stattdessen komplett eigenes Verzeichnis + eigene Instanz, die von Anfang an nichts anderes kennt.
|
|
||||||
|
|
||||||
**Bekannte Einschränkung:** Filebrowser hat in dieser Version **kein** Feld/Flag zum Deaktivieren einzelner Nutzer (anders als Grafana). Der „Aus"-Zustand wird deshalb über den ganzen Systemd-Service abgebildet (`systemctl stop filebrowser-gast`) — das deaktiviert zwangsläufig beide Gast-Logins gleichzeitig auf dieser Ebene. Da beide Konten ohnehin unabhängige Passwörter mit identischen Rechten haben, ist das funktional unkritisch, aber beachten: „nur `freunde` aktiv lassen" gilt nur für Grafana, nicht für die Dateifreigabe.
|
|
||||||
|
|
||||||
## 3. Caddy-Vhosts (VPS)
|
|
||||||
|
|
||||||
Gleiches Muster wie `guac.<DOMAIN>`, `/etc/caddy/Caddyfile`:
|
|
||||||
|
|
||||||
```
|
|
||||||
grafana-gast.<DOMAIN> {
|
|
||||||
reverse_proxy <TS_IP_MONITORING>:3000
|
|
||||||
}
|
|
||||||
dateien-gast.<DOMAIN> {
|
|
||||||
reverse_proxy <TS_IP_NAS>:8081
|
|
||||||
}
|
|
||||||
status-gast.<DOMAIN> {
|
|
||||||
reverse_proxy <TS_IP_MONITORING>:3001
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`caddy validate --config /etc/caddy/Caddyfile` vor `systemctl reload caddy`. TLS-Zertifikate zieht Caddy automatisch, sobald die DNS-A-Records existieren (siehe Abschnitt 6).
|
|
||||||
|
|
||||||
## 4. Portfolio-Runbooks (Inhalt von `/srv/gast-demo`)
|
|
||||||
|
|
||||||
Genericierte Kopien aller `rebuild/*.md` in `../rebuild-portfolio/` — echte LAN-/Tailscale-IPs, Domain, Tailnet-Name, MAC-Adresse, Benutzername ersetzt durch Platzhalter (`<LAN_SUBNET>`, `<DOMAIN>`, `<TAILNET>`, `<PC1_MAC_ADDRESS>`, `<PVE_HOSTNAME>`, `<PRIMARY_USER>`, …). Erklärung der Platzhalter in `../rebuild-portfolio/VARIABLEN.md`. Debugging-Geschichten/„NICHT TUN"-Blöcke bleiben bewusst unverändert drin — nach der Anonymisierung nicht mehr auf die echte Instanz rückführbar, und genau das zeigt in einem Bewerbungsgespräch, dass reale Probleme selbst gefunden und gelöst wurden.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sed -i -e 's/192\.168\.178\.10\b/<LAN_IP_PVE>/g' ... *.md # vollständige sed-Kette: siehe Session-Log
|
|
||||||
```
|
|
||||||
|
|
||||||
Nach jeder Änderung an den echten `rebuild/*.md`-Dateien: Portfolio-Kopie manuell nachziehen (kein automatischer Sync) und erneut gegen die bekannten echten Werte grep-verifizieren.
|
|
||||||
|
|
||||||
## 5. Uptime Kuma: Gast-Monitore + Status-Page
|
|
||||||
|
|
||||||
Kein REST-API für Monitor-/Status-Page-Verwaltung — Python-Bibliothek `uptime-kuma-api` (Socket.IO-Client) nötig:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from uptime_kuma_api import UptimeKumaApi, MonitorType
|
|
||||||
api = UptimeKumaApi('http://<TS_IP_MONITORING>:3001')
|
|
||||||
api.login('admin', PASSWORD)
|
|
||||||
api.add_monitor(type=MonitorType.HTTP, name='Grafana (Gast)', url='https://grafana-gast.<DOMAIN>',
|
|
||||||
interval=60, retryInterval=60, maxretries=2, notificationIDList=[])
|
|
||||||
api.add_status_page('gast', 'Homelab Showcase')
|
|
||||||
api.save_status_page(slug='gast', title='Homelab Showcase', published=True, showPoweredBy=False,
|
|
||||||
publicGroupList=[{'name': 'Showcase', 'weight': 1,
|
|
||||||
'monitorList': [{'id': 12}, {'id': 13}, {'id': 14}]}])
|
|
||||||
```
|
|
||||||
|
|
||||||
⚠️ **NICHT TUN:** den drei Gast-Monitoren die ntfy-Notification zuweisen (weder explizit noch über `isDefault`). Die Konten werden absichtlich manuell rauf-/runtergefahren — mit Notification würde jedes Deaktivieren einen „Down"-Alarm auslösen. `notificationIDList=[]` beim Anlegen explizit setzen.
|
|
||||||
|
|
||||||
### Zweite Gruppe: echte Infrastruktur-Telemetrie hinter Rollen-Namen (2026-07-20)
|
|
||||||
|
|
||||||
Die Status-Page wirkte mit nur drei Kacheln leer. Statt reiner Ampeln (Up/Down) zeigt eine zweite Gruppe „Infrastruktur" jetzt **echte Ping-Telemetrie** (Antwortzeit-Verlauf, Uptime-%) für die Kern-Systeme — mit denselben generischen Rollen-Namen wie im Gast-Grafana-Dashboard (siehe Abschnitt 1), damit beide Oberflächen konsistent wirken:
|
|
||||||
|
|
||||||
```python
|
|
||||||
specs = [
|
|
||||||
('Hauptserver', '<LAN_IP_PVE>'), ('DNS / Ad-Blocking', '<LAN_IP_ADGUARD>'),
|
|
||||||
('NAS / Dateiablage', '<LAN_IP_NAS>'), ('Remote-Desktop', '<LAN_IP_DESKTOP>'),
|
|
||||||
('Passwort-Manager', '<LAN_IP_VAULTWARDEN>'), ('Monitoring-Stack', '<LAN_IP_MONITORING>'),
|
|
||||||
('VPN-Gateway', '<TS_IP_VPS>'),
|
|
||||||
]
|
|
||||||
for name, ip in specs:
|
|
||||||
api.add_monitor(type=MonitorType.PING, name=name, hostname=ip,
|
|
||||||
interval=60, retryInterval=60, maxretries=2, notificationIDList=[])
|
|
||||||
```
|
|
||||||
|
|
||||||
Uptime Kuma zeigt auf der Status-Page nur den konfigurierten Namen + Antwortzeit-Graph, nicht die Ziel-IP — die echten LAN-IPs sind also nur intern für den Check relevant, nie öffentlich sichtbar. Zweite Gruppe per `publicGroupList` mit eigenem `weight` ergänzt (bestehende „Showcase"-Gruppe bleibt unverändert stehen, Gruppen werden per Name identifiziert und beim Speichern komplett ersetzt — beide Gruppen müssen bei jedem `save_status_page`-Aufruf mit angegeben werden, sonst verschwindet die andere).
|
|
||||||
|
|
||||||
Verifiziert über die öffentliche URL (`curl https://status-gast.<DOMAIN>/api/status-page/heartbeat/gast`): alle sieben neuen Monitore liefern echte Ping-Antwortzeiten (0,05–25 ms je nach Ziel), Gruppennamen und Monitor-Namen kommen unverändert als reine Rollen-Bezeichnungen an — keine IP/Hostname in der API-Antwort sichtbar.
|
|
||||||
|
|
||||||
**Uptime-Kuma-Admin-Passwort verloren gegangen:** Die ursprünglichen Zugangsdaten wurden nur im Chat einer früheren Session ausgegeben und nirgends dauerhaft abgelegt (Konvention: Passwörter gehören in Vaultwarden, das ist hier nicht passiert). Reset direkt in der SQLite-DB des laufenden Containers (Uptime Kuma bringt `node`+`bcryptjs`+`sqlite3` im Image mit):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
HASH=$(docker exec uptime-kuma node -e "const bcrypt=require('bcryptjs'); console.log(bcrypt.hashSync(process.argv[1], 10));" "$NEWPW")
|
|
||||||
docker exec uptime-kuma sqlite3 /app/data/kuma.db "UPDATE user SET password = '$HASH' WHERE id = 1;"
|
|
||||||
```
|
|
||||||
|
|
||||||
Funktioniert bei laufendem Container (SQLite/WAL erlaubt den kurzen Fremdzugriff für ein einzelnes UPDATE). **Lehre:** Admin-Zugangsdaten für alle Dienste dieser Session tatsächlich in Vaultwarden ablegen, nicht nur im Chat stehen lassen.
|
|
||||||
|
|
||||||
## 6. DNS (manueller Schritt, keine API verfügbar)
|
|
||||||
|
|
||||||
Drei A-Records im STRATO-Panel für `<DOMAIN>`, alle auf die VPS-IP (gleiche IP wie der bestehende `guac`-Eintrag):
|
|
||||||
- `grafana-gast` → VPS-IP
|
|
||||||
- `dateien-gast` → VPS-IP
|
|
||||||
- `status-gast` → VPS-IP
|
|
||||||
|
|
||||||
⚠️ **NICHT TUN:** Caddy neu laden/starten, *bevor* alle A-Records korrekt gesetzt sind, und dann erwarten, dass es sich von selbst korrigiert. Certmagic (Caddys ACME-Bibliothek) merkt sich fehlgeschlagene Versuche pro Hostname und schaltet nach mehreren Fehlschlägen zur Schonung des Let's-Encrypt-Produktiv-Rate-Limits automatisch auf die **Staging-CA** um (liefert kein von Browsern akzeptiertes Zertifikat) plus einen Backoff von bis zu 20 Minuten zwischen Versuchen. Gleiches Muster wie beim ursprünglichen `guac`-Setup. Fix: erst alle DNS-Einträge verifizieren (`dig @1.1.1.1 <name>.<DOMAIN> A`, gegen mehrere öffentliche Resolver prüfen, eigener AdGuard-Resolver cached u.U. noch den alten Stand), danach `systemctl restart caddy` (nicht nur `reload` — ein Neustart setzt den internen Fehlversuch-/Backoff-Zustand zurück, ein reines `reload` nicht zuverlässig). Direkt danach in `journalctl -u caddy` auf `"certificate obtained successfully"` mit `ca":"https://acme-v02.api.letsencrypt.org/directory"` (nicht `-staging-`) prüfen.
|
|
||||||
|
|
||||||
**Stolperstein bei diesem Durchlauf:** ein Subdomain-Eintrag im STRATO-Panel landete zunächst auf `217.160.0.161` (STRATOs eigenem Parking-/Webhosting-Server) statt der eingetragenen Ziel-IP — vermutlich ein falsch vorausgewählter Eintragstyp im Formular (z.B. "STRATO-Webhosting" statt "eigene IP-Adresse"). Per `dig` direkt gegen die autoritativen STRATO-Nameserver (`docks14.rzone.de`/`shades02.rzone.de`) und mehrere öffentliche Resolver (1.1.1.1/8.8.8.8/9.9.9.9) geprüft, um Panel-Fehler von reiner Propagationsverzögerung zu unterscheiden — eine SOA-Antwort statt einer A-Antwort bedeutet "Eintrag existiert nicht", nicht "noch nicht propagiert". Ein Tippfehler in der Subdomain selbst (`datein-gast` statt `dateien-gast`) trat ebenfalls auf — beim Anlegen den tatsächlich in Caddy konfigurierten Namen exakt gegenprüfen.
|
|
||||||
|
|
||||||
## 7. Login-Tracking (Gast-Nutzung sichtbar machen)
|
|
||||||
|
|
||||||
Anders als bei Guacamole/Vaultwarden/PVE-Web/SSH ist bei den Gast-Oberflächen die Logausbeute schlechter:
|
|
||||||
|
|
||||||
- **Grafana:** loggt bei Formular-Login (`/login`, POST) **weder bei Erfolg noch bei Fehlschlag den Benutzernamen**. Ein Fehlschlag erzeugt `level=info msg="Failed to authenticate request" client=auth.client.form` + eine „Request Completed"-Zeile mit `path=/login status=401` — aber ohne Username, könnte auch ein vertippter Admin-Login sein. Ein Erfolg erzeugt **gar keine** Log-Zeile beim Login selbst; einzig nachfolgende authentifizierte Requests tragen `uname=gast-freunde`/`uname=gast-bewerbung` als Tag. `track-grafana-gast.sh` (Cron alle 5 Min. auf CT104) zählt deshalb Fehlschläge generisch und deutet „Aktivität" über das Vorkommen dieser `uname=`-Tags.
|
|
||||||
- **Filebrowser:** loggt nur Fehlschläge, mit IP (`/api/login: 403 <ip> <err>`), keine Erfolge — gleiche Einschränkung wie beim bereits dokumentierten AdGuard-Gap. `track-filebrowser-gast.sh` (Cron alle 5 Min. auf CT101, no-op wenn der Service gerade gestoppt ist) trackt entsprechend nur `fail`.
|
|
||||||
|
|
||||||
Beide schreiben ins bestehende `logins`-Measurement (`service=grafana-gast`/`service=filebrowser-gast`), gleiches Muster wie die übrigen Tracker.
|
|
||||||
|
|
||||||
**ntfy-Push (2026-07-20 ergänzt):** `track-grafana-gast.sh` pusht bei >3 Fehlversuchen (hohe Priorität) und bei jeder erkannten Aktivität (`uname=`-Tag, Standard-Priorität — das deckt "erfolgreicher Login" für Grafana ab, da ein echtes Login-Event selbst nicht separat loggbar ist). `track-filebrowser-gast.sh` pusht nur bei >3 Fehlversuchen — ein Push bei erfolgreichem Filebrowser-Login ist **technisch nicht möglich**: kein Verbose-/Debug-Log-Flag vorhanden (`filebrowser -h` zeigt nur `-l/--log` für das Ausgabeziel, keine Log-Level-Option), das Tool loggt nachweislich nur Fehlschläge — exakt dieselbe Einschränkung wie bei AdGuard.
|
|
||||||
|
|
||||||
## 8. Aktivieren/Deaktivieren + Ablaufzeit-Sicherheitsnetz
|
|
||||||
|
|
||||||
Zwei Skripte auf dem PVE-Host (steuern CT104 + CT101 gemeinsam über `pct exec`):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gast-aktivieren.sh freunde|bewerbung [stunden]
|
|
||||||
gast-deaktivieren.sh freunde|bewerbung|alle
|
|
||||||
```
|
|
||||||
|
|
||||||
`gast-aktivieren.sh` aktiviert den Grafana-User, startet `filebrowser-gast.service`, und plant bei Angabe von `[stunden]` per `at` einen einmaligen Folgejob (`gast-deaktivieren.sh <wer>`) — reines Sicherheitsnetz für den Fall, dass das manuelle Abschalten vergessen wird, kein Ersatz dafür. `gast-deaktivieren.sh` stoppt `filebrowser-gast.service` nur dann mit, wenn danach **kein** Gast-Grafana-Konto mehr aktiv ist (sonst würde das Deaktivieren von `freunde` auch `bewerbung`s Dateizugriff kappen, obwohl der ggf. noch laufen soll).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
apt-get install -y at && systemctl enable --now atd
|
|
||||||
```
|
|
||||||
|
|
||||||
⚠️ **NICHT TUN:** `at`-Pakete für die Auto-Deaktivierung annehmen, ohne `atd` separat zu aktivieren — auf frisch installiertem Debian läuft der Dienst nach `apt-get install at` nicht automatisch.
|
|
||||||
|
|
||||||
### Automatisches Passwort-Cycling
|
|
||||||
|
|
||||||
Weder Grafana OSS noch Filebrowser kennen ein "Passwort muss nach Login neu gesetzt werden"-Flag — als praktisches Äquivalent erzeugt `gast-aktivieren.sh` bei **jeder** Aktivierung ein frisches Zufallspasswort (12 Zeichen, nur Buchstaben/Ziffern ohne verwechselbare Zeichen wie `0`/`O`/`1`/`l`/`I` — bewusst niedrige Komplexität, da es dem Gast mündlich/per Kurznachricht mitgeteilt werden soll) und setzt es für **beide** Dienste gleich, damit sich der Gast nur ein Passwort merken muss:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
NEWPASS=$(tr -dc 'ABCDEFGHJKMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789' < /dev/urandom | head -c 12 || true)
|
|
||||||
curl -u admin:$GF_PASS -X PUT http://localhost:3000/api/admin/users/$ID/password -d "{\"password\":\"$NEWPASS\"}"
|
|
||||||
filebrowser users update gast-freunde -d /var/lib/filebrowser-gast/filebrowser.db -p "$NEWPASS"
|
|
||||||
```
|
|
||||||
|
|
||||||
Altes Passwort wird dadurch automatisch ungültig — keine manuelle Rotation nötig, das alte Passwort muss auch nirgends aufgehoben werden.
|
|
||||||
|
|
||||||
⚠️ **NICHT TUN:** `tr ... | head -c N` unter `set -o pipefail` verwenden, ohne den SIGPIPE-Fall abzufangen. `head` beendet die Pipe, sobald genug Bytes gelesen sind — `tr` bekommt dadurch SIGPIPE (Exit 141), was mit `pipefail` das ganze Skript abbricht. Fix: `... | head -c N || true`.
|
|
||||||
|
|
||||||
⚠️ **NICHT TUN:** das Filebrowser-Passwort per CLI setzen, während `filebrowser-gast.service` bereits läuft. Filebrowsers bbolt-Datenbank erlaubt nur einen Prozess gleichzeitig — CLI und laufender Dienst kollidieren mit `Error: timeout`. Reihenfolge im Skript: Service stoppen → Passwort per CLI setzen → Service starten.
|
|
||||||
|
|
||||||
Verifiziert: `gast-aktivieren.sh freunde` → Grafana-Login + Filebrowser-Login funktionieren; `gast-deaktivieren.sh freunde` → beides sofort wieder zu; `at`-Job mit 1 Minute Vorlauf → `gast-deaktivieren.sh` feuert zuverlässig automatisch. Nach dem DNS-/Zertifikats-Fix zusätzlich vollständig über die echten öffentlichen URLs (nicht nur intern/Tailnet) durchgetestet: Login bei aktiviertem Konto funktioniert über `https://grafana-gast.<DOMAIN>` und `https://dateien-gast.<DOMAIN>`, zeigt nachweislich nur das Gast-Dashboard, Status-Page lädt; nach Deaktivierung liefern beide öffentlichen URLs wieder 401/502. Passwort-Rotation getestet: nach zweiter Aktivierung meldet das alte Passwort 401, nur das neu ausgegebene funktioniert — für beide Konten (`freunde`/`bewerbung`) einzeln bestätigt.
|
|
||||||
|
|
||||||
### ntfy-Push bei Aktivieren/Deaktivieren (2026-07-20)
|
|
||||||
|
|
||||||
Beide Skripte schließen jetzt mit einem `curl` an `http://localhost:8090/homelab-alerts` (via `pct exec 104`, da ntfy auf CT104 läuft) ab — Standard-Priorität, kein Alarm-Ton, reine Statusmeldung ("Gast-Zugang 'freunde' wurde soeben aktiviert (Auto-Deaktivierung in 3h)." bzw. "... deaktiviert."). Über `curl "http://localhost:8090/homelab-alerts/json?poll=1&since=2m"` gegen echte Zustellung verifiziert (nicht nur, dass der `curl`-Aufruf ohne Fehler durchläuft).
|
|
||||||
Loading…
Reference in a new issue