From a2be9fec88d07552dc2f6866e96e377db2df46ef Mon Sep 17 00:00:00 2001 From: arnol Date: Fri, 7 Aug 2026 19:05:44 +0200 Subject: [PATCH] Showcase nachgezogen: restliche Portfolio-Dateien auf aktuellen Stand --- 03-NAS-CT101.md | 146 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/03-NAS-CT101.md b/03-NAS-CT101.md index 20fb61e..2c7a9ab 100644 --- a/03-NAS-CT101.md +++ b/03-NAS-CT101.md @@ -136,10 +136,156 @@ systemctl enable --now filebrowser Erreichbar unter `http://nas.pve:8080` (LAN + Tailnet). **Bewusst nicht öffentlich exponiert** — für den Internetcafé-Fall gibt's den SFTP-Dateibrowser innerhalb der TOTP-gesicherten Guacamole-Verbindung (siehe `06-vps-strato.md`). +## 5. Nutzerstruktur: gemeinsam + persönliche Bereiche mit Quota + +Granulare Struktur statt eines flachen ``-only-Shares — eigene ZFS-Datasets je Bereich (nicht bloße Ordner), damit Quotas gelten und Samba/Windows den korrekten freien Platz pro Share melden. + +```bash +# Datasets (Host) +zfs create -o quota=200G nas/freigabe/gemeinsam +zfs create -o quota=200G nas/freigabe/ +zfs create -o quota=200G nas/freigabe/ +zfs set reservation=100G nas/backups + +# Ownership: erst auf dem Host (unprivileged Container!), dann im Container +chown 100000:100000 /nas/freigabe/gemeinsam /nas/freigabe/ /nas/freigabe/ +``` + +⚠️ **NICHT TUN:** die neuen Dataset-Verzeichnisse direkt aus dem Container heraus `chown`/`chmod`en, ohne vorher den Host-seitigen Owner zu setzen. Frisch angelegte Kind-Datasets gehören zunächst `root:root` (UID 0) auf dem Host — für einen unprivileged Container ist das eine andere UID als sein eigenes "root" (UID-Offset), `chown`/`chmod` von innen schlägt mit "Operation not permitted" fehl. Immer zuerst `chown 100000:100000 ` auf dem Host, danach funktioniert `chown`/`chmod` von innen normal. + +⚠️ **Warum eigene ZFS-Datasets statt `zfs create` einmal + Unterordner:** Nur echte Datasets haben ein eigenes `quota`; Windows-Explorer zeigt darüber den *tatsächlich* verbleibenden Platz je Share. Mit `userquota@uid` sähe der Client stattdessen den ganzen Pool und liefe erst beim Schreiben in einen Fehler. + +⚠️ **Korrektur — echter Kaltstart-Bug, ursprünglich falsch als „funktioniert automatisch" dokumentiert:** Der hier ursprünglich stehende Satz „neue Kind-Datasets erscheinen automatisch im bestehenden `mp0`-Bind-Mount, kein zusätzlicher Eintrag nötig" war **falsch** — bzw. nur unter den damaligen Testbedingungen richtig (Container lief bereits, Datasets wurden live dazu angelegt; ein nicht-rekursiver Bind-Mount kann in diesem Fall per Shared-Mount-Propagation live nachziehen). Bei einem echten **Kaltstart des gesamten Hosts** (z. B. nach Stromausfall) gilt das nicht: startet CT101, bevor die ZFS-Kind-Datasets erneut gemountet sind (oder greift die Propagation aus welchem Grund auch immer nicht), bleibt `/srv/freigabe/{,,gemeinsam}` dauerhaft leer (`nobody:nogroup`-Stub-Verzeichnisse) — auch ein Neustart des Containers allein behebt das **nicht**, das Bind-Mount muss neu mit den dann schon gemounteten Kind-Datasets aufgebaut werden. Reale Folge: genau das ist nach dem ersten echten Stromausfall dieses Projekts passiert, keine Ordner mehr sichtbar unter dem persönlichen Bereich. Fix, exakt das gleiche Muster wie an anderer Stelle im Setup mit ZFS-Kind-Datasets: + +```bash +pct set 101 --mp2 /nas/freigabe/,mp=/srv/freigabe/,backup=0 +pct set 101 --mp3 /nas/freigabe/,mp=/srv/freigabe/,backup=0 +pct set 101 --mp4 /nas/freigabe/gemeinsam,mp=/srv/freigabe/gemeinsam,backup=0 +pct reboot 101 +``` + +Jedes Kind-Dataset bekommt einen **eigenen, expliziten** `mp`-Eintrag statt sich auf Propagation durch den Eltern-Bind-Mount (`mp0`) zu verlassen — robust gegen jede Boot-Reihenfolge. `/nas/backups` (Abschnitt 3b) ist davon nicht betroffen, hat keine Kind-Datasets. + +Gruppe für den gemeinsamen Bereich, zweiter Nutzer (gleichrangig zu ``): +```bash +groupadd teilen +usermod -aG teilen +useradd -M -s /bin/bash -d /srv/freigabe/ +usermod -aG teilen +smbpasswd -a # + smbpasswd -e +chpasswd <<< ":" # gleiches Passwort wie SMB, wie beim Hauptnutzer + +chown root:teilen /srv/freigabe/gemeinsam +chmod 2770 /srv/freigabe/gemeinsam # setgid: neue Dateien erben automatisch die Gruppe +chown : /srv/freigabe/ && chmod 0700 /srv/freigabe/ +chown : /srv/freigabe/ && chmod 0700 /srv/freigabe/ +``` + +Drei neue Samba-Shares zusätzlich zum bestehenden `[freigabe]` (der bleibt vorerst bestehen, siehe unten): +```ini +[gemeinsam] + path = /srv/freigabe/gemeinsam + browseable = yes + read only = no + guest ok = no + valid users = , + create mask = 0660 + directory mask = 2770 + +[] + path = /srv/freigabe/ + browseable = yes + read only = no + guest ok = no + valid users = + create mask = 0600 + directory mask = 0700 + +[] + path = /srv/freigabe/ + browseable = yes + read only = no + guest ok = no + valid users = + create mask = 0600 + directory mask = 0700 +``` + +⚠️ **`[freigabe]` (das alte, flache Share) bewusst NICHT entfernt.** Die bereits vorhandenen Inhalte liegen weiterhin flach unter `/srv/freigabe` und wurden noch nicht in die neuen Bereiche einsortiert — das erfordert eine bewusste Entscheidung je Ordner, kein automatisches Verschieben. Bis diese Migration gemacht ist, bleibt `[freigabe]` der einzige Weg an die alten Daten. Danach kann es entfernt werden. + +**Zweite Filebrowser-Instanz für Backups (read-only):** analog zum Gast-Filebrowser-Muster (siehe `09-gast-zugang.md`), aber für den Eigentümer bestimmt statt für Gäste — dedizierter User, eigener Port, `-a 0.0.0.0` (LAN/Tailnet, nicht auf eine einzelne Tailscale-IP beschränkt wie beim Gast-Pendant). +```bash +useradd -M -s /usr/sbin/nologin filebackup +mkdir -p /var/lib/filebrowser-backup && chown filebackup:filebackup /var/lib/filebrowser-backup +su -s /bin/bash filebackup -c '/usr/local/bin/filebrowser config init -d /var/lib/filebrowser-backup/filebrowser.db' +su -s /bin/bash filebackup -c '/usr/local/bin/filebrowser config set -a 0.0.0.0 -p 8082 -r /srv/backups -d /var/lib/filebrowser-backup/filebrowser.db' +su -s /bin/bash filebackup -c "/usr/local/bin/filebrowser users add '' -d /var/lib/filebrowser-backup/filebrowser.db" +# Danach explizit auf read-only reduzieren - der Default nach "users add" erlaubt Create/Rename/Modify/Delete! +/usr/local/bin/filebrowser users update --perm.create=false --perm.delete=false --perm.modify=false --perm.rename=false --perm.share=false -d /var/lib/filebrowser-backup/filebrowser.db +``` + +⚠️ **NICHT TUN:** sich nach `filebrowser users add` auf Read-only verlassen, ohne die Permissions explizit zu setzen. Der Default gewährt Create/Rename/Modify/Delete/Share — für einen bewusst schreibgeschützten Backup-Browser müssen diese fünf Flags einzeln per `users update` auf `false` gesetzt werden. + +Fülllstand-Tracking (Erweiterung des HDD-/ZFS-Tracking-Scripts, siehe `08-monitoring.md`): die drei neuen Datasets zur bestehenden `zfs list`-Zeile ergänzt, plus ein einfacher Schwellenwert-Alert (≥80 % Belegung, Statefile verhindert Alert-Spam bei jedem 5-Minuten-Lauf) direkt im selben Script statt eines Grafana-nativen Alerts — konsistent mit dem Rest des Projekts, das Alerting immer über Skript+ntfy statt Grafana-Alerting löst. + +## 6. Foto-Backup vom Handy: Syncthing — ABGEBAUT + +> **Nicht mehr in Betrieb.** Scheiterte praktisch an einem Android-SAF-Scan-Hänger (App friert beim +> ersten Voll-Scan der SD-Karte ein, ANR) — stattdessen Umstieg auf eine Nextcloud-Instanz mit +> External-Storage-Anbindung auf denselben Pfad, ohne diese Redundanz. Komplett zurückgebaut: beide +> systemd-Instanzen gestoppt/deaktiviert, `syncthing`-Paket gepurged, die beiden +> `.syncthing`-Zustandsordner gelöscht (die eigentlichen Fotos blieben unangetastet), beide +> Kuma-Monitore entfernt. +> +> Der folgende Abschnitt bleibt als Referenz stehen, falls Syncthing je wieder gebraucht +> wird (z.B. für einen Anwendungsfall ohne Nextcloud-Client). + +War an die Struktur aus Abschnitt 5 blockiert (Quota-Bereiche mussten zuerst stehen). Bewusst **kein** gemeinsamer Syncthing-Prozess für beide Konten: eine **eigene Instanz je Nutzer**, symmetrisch zum Drei-Ebenen-Modell aus Abschnitt 5 — Fotos landen im jeweils persönlichen, quota-geschützten Bereich (`/srv/freigabe//Fotos`), nicht in `gemeinsam`. + +```bash +apt install -y syncthing # in Debian direkt in main, kein Drittanbieter-Repo noetig +mkdir -p /srv/freigabe//Fotos /srv/freigabe//Fotos +chown : /srv/freigabe//Fotos +chown : /srv/freigabe//Fotos + +runuser -u -- syncthing generate --home=/srv/freigabe//.syncthing \ + --no-default-folder --gui-user= --gui-password='' +runuser -u -- syncthing generate --home=/srv/freigabe//.syncthing \ + --no-default-folder --gui-user= --gui-password='' +``` + +⚠️ **NICHT TUN: `--home` weglassen und sich auf den Unix-Homedir-Default verlassen.** Der Unix-Home des Hauptnutzers ist historisch `/srv/freigabe` (der alte flache Share-Root, Altlast von vor der Quota-Umstellung) — ohne explizites `--home` wäre die Syncthing-Config dort gelandet, mitten im gemeinsamen/unmigrierten Altbestand statt im eigenen quota-geschützten Bereich. + +`config.xml` je Instanz manuell ergänzt (kein CLI-Flag dafür): GUI-Adresse auf die Tailscale-IP von CT101 gebunden (`:8384`/`:8385`, **nicht** LAN — gleiches Muster wie Vaultwarden/Forgejo), plus ein `Fotos`-Ordner mit `type="receiveonly"` (eigene Folder-ID je Nutzer, Pfad `/srv/freigabe//Fotos`). Empfangs-only ist zentral fürs Konzept: Handy = "Nur senden", Server = "Nur empfangen" — Löschungen am Handy löschen nichts auf dem Server. + +```bash +mkdir -p /etc/systemd/system/syncthing@.service.d +cat > /etc/systemd/system/syncthing@.service.d/override.conf <<'EOF' +[Service] +ExecStart= +ExecStart=/usr/bin/syncthing serve --no-browser --no-restart --logflags=0 --home=/srv/freigabe//.syncthing +EOF +# analog fuer den zweiten Nutzer mit --home=/srv/freigabe//.syncthing +systemctl daemon-reload +systemctl enable --now syncthing@.service syncthing@.service +``` + +⚠️ **NICHT TUN: beide Instanzen mit dem `default`-Sync-Listener laufen lassen.** Beide Prozesse teilen sich denselben Container/Netzwerk-Namespace — die zweite Instanz, die startet, scheitert lautlos beim Binden von TCP/QUIC `0.0.0.0:22000` ("address already in use") und ist nur noch über Relay erreichbar (langsamer, weniger privat). Fix: der zweiten Instanz in `config.xml` unter `` explizite ``-Einträge auf Port `22001` geben (`tcp://0.0.0.0:22001`, `quic://0.0.0.0:22001`, plus den Relay-Eintrag unverändert), dann neu starten. GUI-Ports (`8384`/`8385`) waren dagegen von Anfang an über den `generate`-Aufruf getrennt und kollidierten nicht. + +**Pairing (menschlicher Schritt, nicht automatisierbar):** App "Syncthing" auf jedem Handy (F-Droid/Play Store), dort den Server als Remote-Gerät per Device-ID/QR aus der Web-GUI hinzufügen, Server bestätigt eingehende Geräte-Anfrage in der GUI. Auf dem Handy den DCIM/Camera-Ordner als Folder-Typ **"Nur senden"** mit dem Server teilen, Folder-ID passend zur serverseitigen setzen. Mehrere Handys insgesamt, alle gegen die jeweils eigene Instanz. + +⚠️ Vor dem ersten Voll-Sync die tatsächliche DCIM-Größe gegen die 200-GB-Quota des persönlichen Bereichs prüfen — die Quota schützt zwar vor einem Pool-Overrun, ein DCIM-Ordner, der die Quota sprengt, bricht den Sync trotzdem einfach ab statt sauber zu laufen. + +Monitoring: zwei TCP-Port-Checks in Kuma (Port 8384/8385 über die Tailscale-IP) — Details `08-monitoring.md`. + ## Verifikation ```bash smbclient -L // -U %'' sftp @ curl -s -o /dev/null -w "%{http_code}\n" http://:8080/ +curl -s -o /dev/null -w "%{http_code}\n" http://:8082/ # Backup-Filebrowser +smbclient -L // -U %'' # sollte gemeinsam/ zeigen, nicht /freigabe/backups +curl -s -o /dev/null -w "%{http_code}\n" http://:8384/ # Syncthing Hauptnutzer (Tailnet-only) +curl -s -o /dev/null -w "%{http_code}\n" http://:8385/ # Syncthing zweiter Nutzer (Tailnet-only) ```