feat: systemd-Unit, Installationsskript, Dockerfile und compose.yml

Unit mit StateDirectory/RuntimeDirectory, ProtectSystem=full und
MountFlags=shared; compose mit SYS_ADMIN, DAC_READ_SEARCH,
apparmor:unconfined und rshared-Bind für /mnt/netmover (Propagation zum
Host im Container getestet).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Florian Wilde
2026-10-10 19:49:03 +02:00
co-authored by Claude Opus 5.5
parent 00a0537085
commit dd9177859e
6 changed files with 182 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
#!/bin/sh
# NetMover auf Debian 13 installieren bzw. aktualisieren (als root ausführen).
#
# ./deploy/install.sh # aus diesem Verzeichnis installieren
# ./deploy/install.sh 'git+https://git.wilde-server.de/florian.wilde/netmover.git@v0.1.0'
set -eu
SOURCE="${1:-$(cd "$(dirname "$0")/.." && pwd)}"
PREFIX=/opt/netmover
if [ "$(id -u)" -ne 0 ]; then
echo "Bitte als root ausführen." >&2
exit 1
fi
echo "==> Pakete"
apt-get update -qq
apt-get install -y -qq python3 python3-venv cifs-utils nfs-common >/dev/null
echo "==> Virtuelle Umgebung in $PREFIX/venv"
install -d -m 0755 "$PREFIX"
[ -x "$PREFIX/venv/bin/python" ] || python3 -m venv "$PREFIX/venv"
"$PREFIX/venv/bin/pip" install -q --upgrade pip
"$PREFIX/venv/bin/pip" install -q --upgrade "$SOURCE"
echo "==> Konfiguration"
install -d -m 0755 /etc/netmover
if [ ! -f /etc/netmover/netmover.env ]; then
install -m 0640 "$(dirname "$0")/netmover.env" /etc/netmover/netmover.env
fi
install -d -m 0755 /mnt/netmover
echo "==> systemd"
install -m 0644 "$(dirname "$0")/netmover.service" /etc/systemd/system/netmover.service
systemctl daemon-reload
systemctl enable netmover >/dev/null 2>&1
systemctl restart netmover
sleep 2
systemctl --no-pager --lines=0 status netmover || true
echo
if journalctl -u netmover -n 200 --no-pager | grep -q "setup.required"; then
echo "Ersteinrichtung: Einrichtungscode aus dem Log:"
journalctl -u netmover -n 200 --no-pager | grep "setup.required" | tail -1
fi
echo "Fertig. Weboberfläche: http://127.0.0.1:8080 (README: SSH-Tunnel / Reverse Proxy)"
+17
View File
@@ -0,0 +1,17 @@
# Umgebungsvariablen für NetMover (/etc/netmover/netmover.env).
# Nach Änderungen: systemctl restart netmover
# Nur lokal erreichbar – für Zugriff aus dem LAN einen Reverse Proxy mit TLS
# davorsetzen (README, „Reverse Proxy“) oder per SSH-Tunnel arbeiten.
NETMOVER_HOST=127.0.0.1
NETMOVER_PORT=8080
# Hinter einem Reverse Proxy mit HTTPS:
# NETMOVER_COOKIE_SECURE=true
# NETMOVER_FORWARDED_ALLOW_IPS=127.0.0.1
# NETMOVER_MOUNT_BASE=/mnt/netmover
# NETMOVER_SESSION_HOURS=12
NETMOVER_LOG_LEVEL=INFO
# logfmt (gut lesbar im Journal) oder json
NETMOVER_LOG_FORMAT=logfmt
+45
View File
@@ -0,0 +1,45 @@
# NetMover als systemd-Dienst direkt auf dem Host (empfohlen).
# Installation: siehe README, Abschnitt „Installation“.
[Unit]
Description=NetMover – NFS/SMB-Mounts und zeitgesteuertes Verschieben
Documentation=https://git.wilde-server.de/florian.wilde/netmover
Wants=network-online.target
After=network-online.target remote-fs-pre.target
[Service]
Type=exec
# root ist nötig: mount/umount brauchen CAP_SYS_ADMIN, und Dateien auf beliebigen
# Freigaben sollen mit den richtigen Eigentümern geschrieben werden (siehe README).
User=root
EnvironmentFile=-/etc/netmover/netmover.env
ExecStart=/opt/netmover/venv/bin/netmover serve
Restart=on-failure
RestartSec=5
# Beim Stoppen werden Mounts bewusst NICHT ausgehängt.
KillMode=mixed
TimeoutStopSec=20
# /var/lib/netmover (Datenbank, Schlüssel) und /run/netmover (kurzlebige
# Credentials-Dateien für mount.cifs) – beide nur für root lesbar.
StateDirectory=netmover
StateDirectoryMode=0700
RuntimeDirectory=netmover
RuntimeDirectoryMode=0700
# Härtung, soweit mit Mounten vereinbar.
# Die Optionen unten erzeugen einen eigenen Mount-Namespace für den Dienst.
# MountFlags=shared sorgt dafür, dass Mounts unter /mnt/netmover trotzdem im
# System sichtbar sind (prüfen mit: findmnt -R /mnt/netmover).
MountFlags=shared
# /usr, /boot und /etc schreibgeschützt; /mnt, /opt, /srv, /var bleiben schreibbar
# (lokale Ziele wie /opt/paperless/consume funktionieren also).
ProtectSystem=full
PrivateTmp=yes
NoNewPrivileges=yes
ProtectControlGroups=yes
LockPersonality=yes
RestrictRealtime=yes
UMask=0022
[Install]
WantedBy=multi-user.target