Schritt-für-Schritt: Proxmox-VM → Debian-Installation → Absicherung → NetWatch-Service auf Port 3579.
Inkl. Workarounds für die häufigsten Fallstricke.
<<EOF) und lange scp/sed-Zeilen.
Diese Anleitung verwendet deshalb durchweg das Zeile-für-Zeile-Muster: kurze Befehle < 80 Zeichen,
Datei wird mit mehreren echo ... >> datei in einer Root-Shell aufgebaut. Robust und paste-fest.
local → Tab ISO Images die Datei debian-13.x-amd64-netinst.iso hochladen
(oder über Download from URL).Button Create VM rechts oben — bewährte Werte:
| Tab | Einstellung |
|---|---|
| General | Name: Netwatch |
| OS | ISO = Debian-ISO, Type Linux, Version 6.x - 2.6 Kernel |
| System | Machine q35, SCSI Controller VirtIO SCSI single,
Qemu Agent ✓ |
| Disks | 16–32 GB, Bus SCSI, Discard ✓,
SSD emulation ✓, IO thread ✓ |
| CPU | 1 Sockel, 2 Cores, Type x86-64-v2-AES oder host |
| Memory | 1024 MB · Ballooning ✓ · KSM darf an |
| Network | Bridge vmbr0, Model VirtIO, Firewall ✓ |
No cache (Default) lassen — ZFS hat seinen eigenen ARC,
doppeltes Caching kostet RAM und kann fsync-Garantien aushebeln.
Beim Start der VM siehst du oft Zeilen wie:
shpchp 0000:05:01.0: pci_hp_register failed with error -16
shpchp 0000:05:01.0: Slot initialization failed
Kein Fehler. Das ist der PCI-Hotplug-Treiber, der mit q35 mehr Slots
zu registrieren versucht als nötig. Boot, Netzwerk und Storage sind nicht betroffen.
VM starten und in der Proxmox-Konsole den Installer durchklicken:
de_DE.UTF-8 · Tastatur: Deutschnetwatchfritz.box bei FRITZ!Box)steffen (wird in Schritt 5 zum sudo-User)deb.debian.orgMit Leertaste ab-/anwählen. Nur diese setzen:
/dev/sdaAuf der Proxmox-Konsole als root einloggen.
apt update
apt install -y sudo curl ca-certificates gnupg
usermod -aG sudo steffen
apt update && apt upgrade -y
apt autoremove -y
apt install -y qemu-guest-agent
Der Service ist socket/udev-aktiviert — kein systemctl enable nötig.
Er startet automatisch, weil die VM agent: 1 gesetzt hat.
Test vom Proxmox-Host:
qm agent <VMID> ping
Leerer Output = Erfolg. Mehr Info:
qm agent <VMID> network-get-interfaces
qm agent <VMID> get-osinfo
Auf der VM-Konsole:
ip a
Aktuelle DHCP-IP merken, vom Mac aus verbinden:
ssh steffen@<aktuelle-ip>
Dann zum Root-User wechseln (Root-Passwort eingeben):
su -
Statt nano: Datei in einem Rutsch per Heredoc schreiben — die Zeile bleibt kurz genug, dass Terminal sie nicht zerlegt:
cat > /etc/network/interfaces <<'EOF'
source /etc/network/interfaces.d/*
auto lo
iface lo inet loopback
auto ens18
iface ens18 inet static
address 192.168.178.101/24
gateway 192.168.178.1
dns-nameservers 192.168.178.1 1.1.1.1
iface ens18 inet6 auto
EOF
EOF nicht ab (Prompt bleibt bei >).
Notausgang: ⌃+C, dann den Block ohne führende Leerzeichen erneut einfügen.
Die dns-nameservers-Zeile wird nur ausgewertet, wenn das Paket resolvconf installiert ist:
apt install -y resolvconf
systemctl restart networking
cat /etc/resolv.conf
In resolv.conf sollten nameserver 192.168.178.1 und nameserver 1.1.1.1 stehen.
Nach systemctl restart networking bleibt manchmal der alte DHCP-Lease aktiv neben der neuen
statischen IP (z. B. zusätzliche 192.168.178.199/24). Sauberster Fix:
reboot
Danach vom Mac neu verbinden:
ssh steffen@192.168.178.101
ip a show ens18
ping -c 2 192.168.178.1
ping -c 2 deb.debian.org
Vom Mac per SSH eingeloggt:
ssh steffen@192.168.178.101
Auf dem Mac:
ls ~/.ssh/id_ed25519.pub 2>/dev/null || ssh-keygen -t ed25519 -C "steffen@mac"
ssh-copy-id steffen@192.168.178.101
Test: nächster Login darf nicht mehr nach Passwort fragen.
ssh steffen@192.168.178.101 —
diese Session offen lassen, nichts tippen. Falls die Konfig kaputt ist, hast du noch eine offene Verbindung,
über die du retten kannst.
In der ersten Session als root die Hardening-Datei Zeile für Zeile schreiben:
sudo -i
cd /etc/ssh/sshd_config.d
echo 'PermitRootLogin no' > 99-hardening.conf
echo 'PasswordAuthentication no' >> 99-hardening.conf
echo 'KbdInteractiveAuthentication no' >> 99-hardening.conf
echo 'PubkeyAuthentication yes' >> 99-hardening.conf
echo 'X11Forwarding no' >> 99-hardening.conf
echo 'MaxAuthTries 3' >> 99-hardening.conf
echo 'LoginGraceTime 30s' >> 99-hardening.conf
echo 'ClientAliveInterval 300' >> 99-hardening.conf
echo 'ClientAliveCountMax 2' >> 99-hardening.conf
echo 'AllowUsers steffen' >> 99-hardening.conf
cat 99-hardening.conf
sshd -t
systemctl restart ssh
exit
Tests in einem dritten Tab (⌘+T):
ssh steffen@192.168.178.101 # muss klappen
ssh root@192.168.178.101 # muss scheitern
ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password \
steffen@192.168.178.101 # muss scheitern
Sind alle drei wie erwartet, ist die Sicherheitsnetz-Session entbehrlich.
sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 3579/tcp
sudo ufw enable
sudo ufw status verbose
sudo apt install -y fail2ban
sudo -i
cd /etc/fail2ban
echo '[DEFAULT]' > jail.local
echo 'bantime = 1h' >> jail.local
echo 'findtime = 10m' >> jail.local
echo 'maxretry = 5' >> jail.local
echo 'ignoreip = 127.0.0.1/8 ::1 192.168.178.0/24' >> jail.local
echo '' >> jail.local
echo '[sshd]' >> jail.local
echo 'enabled = true' >> jail.local
echo 'port = ssh' >> jail.local
echo 'backend = systemd' >> jail.local
cat jail.local
exit
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd
backend = systemd liest aus journalctl — Debian 13 schreibt keine klassische
auth.log mehr. ignoreip verhindert, dass du dich aus dem eigenen LAN selbst sperrst.
sudo apt install -y unattended-upgrades apt-listchanges
sudo dpkg-reconfigure -plow unattended-upgrades
Dialogbox: mit Tab auf Ja, Enter.
Kontrolle:
sudo systemctl status unattended-upgrades --no-pager | head -5
sudo cat /etc/apt/apt.conf.d/20auto-upgrades
Datei muss enthalten:
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
Trockenlauf:
sudo unattended-upgrade --dry-run -d 2>&1 | tail -5
/etc/apt/apt.conf.d/50unattended-upgrades
aktivieren. Geht nur sauber per Editor (nano/vim) — keine Eile, Sicherheitsupdates greifen auch ohne Reboot,
nur Kernel-Updates erst beim nächsten manuellen Neustart.
sudo -i
cd /etc/sysctl.d
echo 'net.ipv4.conf.all.rp_filter = 1' > 90-hardening.conf
echo 'net.ipv4.conf.default.rp_filter = 1' >> 90-hardening.conf
echo 'net.ipv4.icmp_echo_ignore_broadcasts = 1' >> 90-hardening.conf
echo 'net.ipv4.conf.all.accept_redirects = 0' >> 90-hardening.conf
echo 'net.ipv6.conf.all.accept_redirects = 0' >> 90-hardening.conf
echo 'net.ipv4.conf.all.send_redirects = 0' >> 90-hardening.conf
echo 'net.ipv4.conf.all.accept_source_route = 0' >> 90-hardening.conf
echo 'net.ipv6.conf.all.accept_source_route = 0' >> 90-hardening.conf
echo 'net.ipv4.tcp_syncookies = 1' >> 90-hardening.conf
echo 'kernel.kptr_restrict = 2' >> 90-hardening.conf
echo 'kernel.dmesg_restrict = 1' >> 90-hardening.conf
cat 90-hardening.conf
sysctl --system
exit
sudo timedatectl set-timezone Europe/Berlin
timedatectl
Erwartet: Time zone: Europe/Berlin (CEST, +0200), System clock synchronized: yes,
NTP service: active.
Auf dem Mac, im Projektordner. Variable für das Ziel macht die Befehle paste-fest:
cd /Users/bigbrain/Projects/gereate_website_monitoring
ssh steffen@192.168.178.101 'mkdir -p ~/netwatch-src'
T=steffen@192.168.178.101:netwatch-src/
scp -r install.sh server.js package.json public $T
Kontrolle:
ssh steffen@192.168.178.101 'ls -la ~/netwatch-src'
Erwartet: install.sh, server.js, package.json, Verzeichnis public/.
Auf der VM:
ssh steffen@192.168.178.101
cd ~/netwatch-src
sudo bash install.sh
Was passiert:
iputils-ping, curl, ca-certificatesnetwatch (no-login)/opt/netwatch/net.ipv4.ping_group_range setzen → unprivilegiertes Pingnetwatch.service aktivieren + starten✅ NetWatch installiertToken: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxDesktop: http://192.168.178.101:3579Mobile : http://192.168.178.101:3579/mobile
Der Token ist dein „Passwort" zum Dashboard. Aus der Installer-Ausgabe in den Passwortmanager kopieren.
Er ist nicht verloren, wenn du ihn vergisst — jederzeit wieder einsehbar:
sudo cat /opt/netwatch/data/token
| Frage | Antwort |
|---|---|
| Wann generiert? | Beim ersten Start, einmalig (24 zufällige Bytes, base64url) |
| Bleibt stabil? | Ja — auch nach Reboot/Update, solange die Datei existiert |
| Wie schützen? | Wie ein Passwort behandeln — wer ihn hat, bedient das Dashboard |
| Im Browser? | Beim ersten Aufruf eintippen, UI merkt ihn lokal |
Token zurücksetzen (z. B. nach Leak):
sudo systemctl stop netwatch
sudo rm /opt/netwatch/data/token
sudo systemctl start netwatch
sudo cat /opt/netwatch/data/token
| Gerät | URL |
|---|---|
| Desktop | http://192.168.178.101:3579 |
| Mobile (PWA) | http://192.168.178.101:3579/mobile |
| Aufgabe | Befehl |
|---|---|
| Status prüfen | sudo systemctl status netwatch |
| Live-Log | sudo journalctl -u netwatch -f |
| Neustart | sudo systemctl restart netwatch |
| Backups (7 Tage) | sudo ls /opt/netwatch/data/backup/ |
| Health-Check (ohne Auth) | curl http://localhost:3579/health |
| fail2ban-Status | sudo fail2ban-client status sshd |
| UFW-Status | sudo ufw status verbose |
| Updates manuell | sudo apt update && sudo apt upgrade -y |
F=/lange/datei.conf, dann echo ... >> $Fcd /etc/foo, dann mit relativem Dateinamen arbeitensudo -i, dann simples > / >> nutzen> hängen bleibt, ⌃+CGeht auch — deutlich ressourcensparender. Voraussetzungen:
features: nesting=1sysctl net.ipv4.ping_group_range muss im Container greifen —
sonst Ping nur mit cap_net_raw