Files
oikos/infrastructure/vps-hardening.md
dtoro b42a986cc0 wiki: document 2026-05-21 netbird vanilla migration
Updates to four pages reflecting the combined → vanilla mgmt+signal+relay+coturn
cutover and the IONOS-3478-firewall-exception discovery:

* infrastructure/mesh.md — rewrites the ICE/STUN section to cover the new
  TURN endpoint, the IONOS upstream TCP-3478 filtering (load-bearing,
  undocumented before today), and the verification probe. New changelog
  entry covering the migration outcome + Device Code Stage gap.

* infrastructure/vps-hardening.md — "At a glance" lists the new 6-service
  docker stack + host coturn. Firewall section notes the new
  `iifname ens6 tcp dport 3478 accept` rule plus the IONOS upstream
  exception. New changelog entry.

* containers/124-authentik.md — replaces the "Netbird IdP integration —
  DEFERRED" section with the LANDED state: Provider details (Public
  client type — Confidential breaks PKCE on the dashboard SPA), the
  first-time owner-promotion sqlite recipe, the missing Device Code
  Stage gap + workaround (setup-keys), and a note that the old 2026-04-22
  pre-work Provider/App is now obsolete and safe to delete. Updated
  changelog (Phase 6 landed).

* operations/agent-enrollment.md — new "Getting onto Netbird" subsection
  explaining the setup-key path (currently the only working flow until
  Device Code Stage lands) and why direct OIDC from the public internet
  fails (auth.hubris.network is mesh-only-reachable). Prerequisites table
  row updated to point at the new section.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 13:48:01 +02:00

5.6 KiB

VPS hardening — 82.165.190.79 / 100.122.165.149

IONOS VPS that runs the Netbird control plane and the public ingress traefik. Hardened 2026-04-23 from its stock-Plesk state.

At a glance

  • Hostname: inspiring-ramanujan.82-165-190-79.plesk.page
  • OS: Debian 13
  • Mesh: netbird 100.122.165.149 (peer of the lab mesh; routes 192.168.8.0/24 via hubris).
  • Public: 82.165.190.79 (ens6).
  • Public DNS: IONOS wildcard *.hubris.network → 82.165.190.79.
  • Docker stack at /opt/docker-compose.yml: traefik (TLS/ACME) + dashboard + mgmt + signal + relay + proxy — netbird-mgmt 0.71.3 vanilla deploy since 2026-05-21 (see mesh.md changelog).
  • Host services (outside docker): coturn (TURN-TCP on :3478, long-term creds at /root/turn-pass.txt, used by mgmt's TURNConfig).

SSH

  • Key-only (PasswordAuthentication no, PermitRootLogin prohibit-password) via drop-in at /etc/ssh/sshd_config.d/10-hubris-hardening.conf. Original config backed up at /etc/ssh/sshd_config.bak.<ts>.
  • Mesh-only: public :22 is dropped by the nftables firewall. SSH reaches the VPS only over wt0. ListenAddress itself is still 0.0.0.0 — gating is firewall-layer.
  • Authorized root keys: PVE (root@hubris), Mac Mini (d.toro.v@pm.me). Add a new device with ssh-copy-id root@100.122.165.149 from a mesh peer before disabling its access paths.

Firewall — nftables (inet hubris-fw)

Config at /etc/nftables.conf, service enabled.

  • Public iface ens6. Wireguard iface wt0.
  • INPUT on ens6 allow-list: DHCP (67→68), rate-limited ICMP/ICMPv6, TCP 3478 (coturn TURN-TCP, added 2026-05-21). Everything else drops.
  • wt0 fully accepted in INPUT. lo accepted.
  • IONOS upstream firewall also gates inbound traffic before it reaches ens6. Open ports today: TCP 80/443 (traefik), UDP 51820 (netbird-proxy), TCP 3478 (coturn, added 2026-05-21). UDP 3478 is dropped by IONOS upstream regardless of local nftables. See mesh.md ICE/STUN for the STUN/TURN port matrix.
  • FORWARD chain at priority filter-10 (runs before Docker's FORWARD) hosts the fail2ban ban enforcement — see below.
  • Set banned4 (typed ipv4_addr, flag timeout) holds fail2ban's drops.
  • Coexists with Docker's ip nat / ip filter tables (iptables-nft compat). Do NOT flush ruleset in this config — it'll wipe Docker's state too.

fail2ban

  • Jail traefik-4xx tails /var/log/traefik/access.log (bind-mounted from container). Filter at /etc/fail2ban/filter.d/traefik-4xx.conf matches 401/403/404/429 from blog-public@file or artifacto-public@file routers only — netbird-grpc traffic isn't considered.
  • Tunables: findtime=600, maxretry=30, bantime=3600.
  • Action at /etc/fail2ban/action.d/nft-hubris.conf adds/removes elements from inet hubris-fw banned4 with per-element timeout.

CRITICAL invariant — wireguard / fail2ban

Bans must never affect wt0 or wireguard UDP. The FORWARD chain explicitly accepts the following before the ban check:

  • udp 51820 (wireguard)
  • udp 3478 (STUN)
  • ct state established,related

The INPUT ban rule is scoped to iifname "ens6".

Violating this takes the mesh down for every home device (they share one public IP) and the only recovery is IONOS console → nft flush set inet hubris-fw banned4.

Traefik access log

  • Written to /var/log/traefik/access.log on the host via a bind mount added to /opt/docker-compose.yml (traefik volumes include /var/log/traefik:/logs) plus --accesslog.filepath=/logs/access.log.
  • CLF format. Real client IP arrives correctly because docker userland-proxy is off — see public ingress.

Plesk / mail / FTP / Dr.Web

Stopped and disabled (not uninstalled). All of: dovecot, dovecot.socket, postfix, postfix@-, pc-remote, xinetd, plesk-task-manager, plesk-web-socket, sw-cp-server, sw-engine, plesk-repaird, plesk-repaird.socket, drwebd.

psa.service is masked (was a one-shot boot bootstrap). /etc/cron.d/plesk-backup-manager-task renamed to .disabled.

Reverse: systemctl unmask psa; systemctl enable --now <svc>.

Auto-patching

  • unattended-upgrades enabled (stock).
  • Drop-in at /etc/apt/apt.conf.d/52hubris-reboot.conf sets auto-reboot at 04:00 UTC when /var/run/reboot-required is set.
  • Runs inside the stock apt-daily-upgrade.timer.

Recovery paths

Ordered by preference:

  1. SSH via mesh — primary. Any mesh peer with an authorized key.
  2. IONOS web console (my.ionos.com → VPS → Console) — uses the system password, not SSH keys. Bypasses any firewall misconfig.
  3. IONOS rescue mode — boot rescue, mount rootfs, edit /etc/nftables.conf or /etc/ssh/sshd_config.d/10-hubris-hardening.conf to a known-good state, reboot.

Changelog

2026-05-21 — netbird stack migrated combined → vanilla; coturn added

Replaced the netbirdio/netbird-server combined image with the canonical mgmt + signal + relay + dashboard containers (0.71.3). Added host-side coturn for external TURN, with nftables rule iifname "ens6" tcp dport 3478 accept and an IONOS upstream firewall exception. Authentik on LXC 124 now provides OIDC for the netbird dashboard. Full context in mesh.md changelog.

2026-04-28 — wiki entry created

Initial documentation.

2026-04-23 — hardened

nftables firewall, mesh-only SSH, fail2ban traefik jail, Plesk disabled, auto-reboot 04:00 UTC, wireguard/fail2ban invariant established.