Skip to content

VPS Hardening — OVH Edge Network Firewall

Server: vps-9d58eb96.vps.ovh.ca149.56.132.214 (OVH VPS, Beauharnois) Stack: Dokploy (Docker Swarm, single node) + Traefik v3 + n8n + vgm-playwright + Redis/Postgres

Dokploy request logs showed constant scanner noise against the raw IP: GET /.env, path traversal (/..%2F..%2F...%2Fetc%2Fpasswd), probe POSTs — 1600+ entries, all 404. The noise itself was harmless (nothing served), but the audit it triggered found real exposure.

Finding Severity Status
Dokploy admin panel published on 0.0.0.0:3000 — deploy-anything UI on the public internet High Blocked by edge firewall; port un-publish still pending
Docker Swarm manager port 2377 listening on * Medium (mTLS-protected, but no reason to expose) Blocked
Swarm gossip 7946 + overlay VXLAN 4789/udp public Medium (gossip unauthenticated) Blocked
LLMNR 5355 on 0.0.0.0 Low Blocked
App containers (playwright, n8n, redis, postgres, redisinsight, stirling-pdf) OK — no published ports, only reachable via Traefik / internal docker networks
exim4 OK — loopback only

Note: a host firewall (ufw/iptables INPUT) could not have fixed the Docker-published ports — Docker’s DNAT bypasses the INPUT chain. The OVH edge firewall filters before traffic reaches the VM, which is why it was chosen over fail2ban/ufw.

Edge firewall rules (OVH Manager → Network → IP → 149.56.132.214)

Section titled “Edge firewall rules (OVH Manager → Network → IP → 149.56.132.214)”

The OVH edge firewall is stateless: “established” is a TCP-flag check, and UDP has no state at all — hence the explicit rules for DNS/NTP responses to outbound queries.

Priority Mode Proto Source Src port Dest port TCP status Purpose
0 Authorise TCP 184.149.58.157/32 22 SSH, allowed IP #1
1 Authorise TCP 208.111.82.254/32 22 SSH, allowed IP #2
2 Authorise TCP all 80 Traefik HTTP (Let’s Encrypt + redirect)
3 Authorise UDP all 123 NTP responses (clock sync)
4 Authorise UDP all 443 HTTP/3
5 Authorise TCP all established Return traffic for all outbound TCP (portal calls, proxy, APIs, apt, docker pulls)
6 Authorise TCP all 443 Traefik HTTPS — webhooks (Trello, Typeform), n8n, all apps
7 Authorise UDP all 53 DNS responses — mandatory, deny-all would break resolution
8 Authorise UDP all 41641 Tailscale direct path (falls back to DERP relay without it)
9 Authorise ICMP all OVH monitoring, path MTU
19 Refuse IPv4 all Deny everything else — the rule that makes the rest matter

As configured in OVH Manager (all rules Active, firewall Enabled):

OVH Edge Network Firewall rules for 149.56.132.214

Gotchas hit during setup, for next time:

  • Source port ≠ destination port. First SSH rule was drafted with source port 22 — clients connect from ephemeral ports; the rule would have matched nothing and caused lockout when deny-all activated. Source port stays empty on inbound-service rules.
  • A destination-port-less TCP Authorise = allow all TCP. One rule was briefly created without a port and would have neutralised the firewall. Deleted before activation.
  • One source IP per rule — two allowed SSH IPs = two rules.
  • Rules do nothing until the firewall toggle is flipped to Enabled; propagation took ~1 minute after enabling.
  • Port 3000 (Dokploy UI): dropped
  • Port 2377 / 7946 (swarm): dropped
  • Port 443: open, n8n.vgmautomations.com → HTTP 200 ✓ — inbound webhooks unaffected
  • Port 80: open ✓
  • SSH: verified working from both allowlisted IPs ✓

Both allowlisted SSH IPs are dynamic. If they rotate and SSH is refused: Tailscale is on the VM and unaffected by the edge firewall — ssh debian@100.79.15.82 — then update rule 0/1 sources in OVH Manager.

  1. Un-publish port 3000 (defense in depth — currently blocked only by the firewall): assign a panel domain in Dokploy Settings → Server, confirm it loads, then docker service update --publish-rm published=3000,target=3000 dokploy, and enable 2FA on the Dokploy account.
  2. IPv6 is unfiltered — the OVH edge firewall on this VPS is IPv4-only and the VM has a public v6 (2607:5300:205:200::9672). Acceptable while no AAAA records point at it; verify with dig AAAA <domain> for each zone, or disable v6 on the host if unused.
  3. Optional: CrowdSec (SSH + Traefik logs) — polish, no longer urgent.
  • fail2ban was considered and rejected as the primary fix: log-based banning reacts to noise that the firewall now prevents from mattering. Still reasonable later for sshd.
  • Scanner 404s against 80/443 will keep appearing in Dokploy/Traefik logs. That is expected background noise hitting a closed door — not an incident.