Netzwerk

pfSense/OPNsense mit nur einer öffentlichen IP auf einem Proxmox Root-Server

Wie man einen dedizierten Root-Server (z. B. Hetzner) mit einer einzigen öffentlichen IP so konfiguriert, dass der gesamte VM-Traffic über eine pfSense/OPNsense-Firewall läuft.

Thomas 28. Oktober 2025 12 Min. Lesezeit 199 Aufrufe 25. August 2026

Übersicht

Dieser Artikel zeigt, wie man einen dedizierten Root-Server mit statischer IP-Adresse (z. B. bei Hetzner, IONOS oder Netcup) so konfiguriert, dass der gesamte VM-Datenverkehr über eine pfSense/OPNsense-Firewall läuft, mit nur einer einzigen öffentlichen IP-Adresse. Der Proxmox-Host behält dabei zwei eigene Ports: Port 22 für SSH und Port 8006 für die GUI. Alles andere landet bei der Firewall.

Netzwerk-Architektur

Die Lösung basiert auf drei Bridges:

  • vmbr0: WAN-Bridge mit öffentlicher IPv4/IPv6
  • vmbr1: Transit-Bridge (10.0.10.0/30) für pfSense-WAN
  • vmbr2: LAN-Bridge für pfSense und VMs

Das Prinzip ist ein Catch-all-DNAT: Der komplette eingehende Verkehr auf vmbr0 geht an die pfSense unter 10.0.10.2, TCP und UDP als je eigene Regel. Davor stehen zwei RETURN-Regeln für die Ports, die auf dem Host selbst bleiben sollen, nämlich 22 und 8006. Ausgehender Verkehr aus dem Transit-Netz wird per Masquerading auf die öffentliche IP genattet.

RETURN statt einer Portnegation wie ! --dport 22 ist der wichtigere Teil des Schemas. Jeder Port, der auf dem Host bleiben soll, bekommt eine eigene Zeile am Anfang der Kette. Das lässt sich jederzeit erweitern, ohne die DNAT-Regel anzufassen, und man sieht in iptables -t nat -S auf einen Blick, was nicht weitergeleitet wird.

Schritt 1: IP-Adressen ermitteln

# Interface setzen (bei Hetzner meist eno1)
IFACE=eno1

# IPv4-Adresse
ip -o -4 addr show dev "$IFACE" | awk '{print "address "$4}'

# IPv6-Adresse (ohne fe80::)
ip -o -6 addr show dev "$IFACE" | awk '$4 !~ /^fe80::/ {print "address "$4}'

# Default-Gateways
ip route | awk -v d="$IFACE" '$1=="default" && $5==d {print "gateway "$3; exit}'
ip -6 route | awk -v d="$IFACE" '$1=="default" && $5==d {print "gateway "$3; exit}'

Schritt 2: /etc/network/interfaces konfigurieren

Die Platzhalter in spitzen Klammern durch die in Schritt 1 ermittelten Werte ersetzen. Die IPv6-Zeilen auf vmbr1 werden nur benötigt, wenn ein geroutetes /64 vorhanden ist, siehe den optionalen Abschnitt weiter unten.

source /etc/network/interfaces.d/*

auto lo
iface lo inet loopback
iface lo inet6 loopback

auto eno1
iface eno1 inet manual

auto vmbr0
iface vmbr0 inet static
        address <server-ipv4>/<prefix>
        gateway <gateway-ipv4>
        bridge-ports eno1
        bridge-stp off
        bridge-fd 0
        bridge-maxwait 0

iface vmbr0 inet6 static
        address <server-ipv6>/64
        gateway fe80::1
        # Kein proxy_ndp noetig, da das pfSense-WAN keine On-Link-IPv6 auf vmbr0 nutzt

post-up echo 1 > /proc/sys/net/ipv4/ip_forward

auto vmbr1
iface vmbr1 inet static
        address 10.0.10.1/30
        bridge-ports none
        bridge-stp off
        bridge-fd 0

        # SSH auf den Host selbst, am DNAT vorbei
        post-up iptables -t nat -C PREROUTING -i vmbr0 -p tcp --dport 22 -j RETURN 2>/dev/null || iptables -t nat -I PREROUTING 1 -i vmbr0 -p tcp --dport 22 -j RETURN
        # Proxmox-GUI auf dem Host selbst, am DNAT vorbei
        post-up iptables -t nat -C PREROUTING -i vmbr0 -p tcp --dport 8006 -j RETURN 2>/dev/null || iptables -t nat -I PREROUTING 1 -i vmbr0 -p tcp --dport 8006 -j RETURN
        # Catch-All DNAT auf die Firewall, TCP und UDP getrennt
        post-up iptables -t nat -C PREROUTING -i vmbr0 -p tcp -j DNAT --to-destination 10.0.10.2 2>/dev/null || iptables -t nat -A PREROUTING -i vmbr0 -p tcp -j DNAT --to-destination 10.0.10.2
        post-up iptables -t nat -C PREROUTING -i vmbr0 -p udp -j DNAT --to-destination 10.0.10.2 2>/dev/null || iptables -t nat -A PREROUTING -i vmbr0 -p udp -j DNAT --to-destination 10.0.10.2
        # Masquerading fuer ausgehenden Traffic aus dem Transitnetz
        post-up iptables -t nat -C POSTROUTING -s 10.0.10.0/30 -o vmbr0 -j MASQUERADE 2>/dev/null || iptables -t nat -A POSTROUTING -s 10.0.10.0/30 -o vmbr0 -j MASQUERADE

        post-down iptables -t nat -D PREROUTING -i vmbr0 -p tcp --dport 22 -j RETURN 2>/dev/null || true
        post-down iptables -t nat -D PREROUTING -i vmbr0 -p tcp --dport 8006 -j RETURN 2>/dev/null || true
        post-down iptables -t nat -D PREROUTING -i vmbr0 -p tcp -j DNAT --to-destination 10.0.10.2 2>/dev/null || true
        post-down iptables -t nat -D PREROUTING -i vmbr0 -p udp -j DNAT --to-destination 10.0.10.2 2>/dev/null || true
        post-down iptables -t nat -D POSTROUTING -s 10.0.10.0/30 -o vmbr0 -j MASQUERADE 2>/dev/null || true

# Nur noetig mit geroutetem /64, siehe Abschnitt weiter unten
iface vmbr1 inet6 static
        address fd00:10::1/64

        # IPv6-Forwarding
        post-up sysctl -w net.ipv6.conf.all.forwarding=1
        post-up sysctl -w net.ipv6.conf.default.forwarding=1

        # Route auf das geroutete /64 zur pfSense ueber den Transit-Link
        post-up ip -6 route replace <routed-ipv6-prefix>/64 via fd00:10::2 dev vmbr1
        post-down ip -6 route del <routed-ipv6-prefix>/64 || true

        # FORWARD dauerhaft erlauben, ohne Duplikate
        post-up ip6tables -C FORWARD -i vmbr1 -o vmbr0 -j ACCEPT 2>/dev/null || ip6tables -A FORWARD -i vmbr1 -o vmbr0 -j ACCEPT
        post-up ip6tables -C FORWARD -i vmbr0 -o vmbr1 -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || ip6tables -A FORWARD -i vmbr0 -o vmbr1 -m state --state RELATED,ESTABLISHED -j ACCEPT
        # Sicherstellen, dass die FORWARD-Policy nicht auf DROP steht
        post-up ip6tables -P FORWARD ACCEPT
        post-down ip6tables -D FORWARD -i vmbr1 -o vmbr0 -j ACCEPT 2>/dev/null || true
        post-down ip6tables -D FORWARD -i vmbr0 -o vmbr1 -m state --state RELATED,ESTABLISHED -j ACCEPT 2>/dev/null || true

auto vmbr2
iface vmbr2 inet static
        address <pve-lan-ip>/24
        bridge-ports none
        bridge-stp off
        bridge-fd 0
        bridge-vlan-aware yes
        bridge-vids 2-4094

Weitere Netze bekommen einfach weitere Bridges ohne Uplink, zum Beispiel eine DMZ oder eine isolierte Testumgebung. Diese Bridges brauchen keine IP auf dem Host:

auto vmbr3
iface vmbr3 inet manual
        bridge-ports none
        bridge-stp off
        bridge-fd 0
        bridge-vlan-aware yes
        bridge-vids 2-4094

Zu den NAT-Regeln gibt es drei Punkte, die in der Praxis wichtig sind.

Die Regeln hängen an vmbr1 und nicht an vmbr0, damit sie erst gesetzt werden, wenn das Transit-Netz existiert. Die Prüfung mit -C vor jedem -A beziehungsweise -I verhindert doppelte Einträge, wenn das Interface später erneut hochgefahren wird.

Die beiden Ausnahmen werden mit -I PREROUTING 1 eingefügt, die DNAT-Regeln mit -A angehängt. Nur so stehen die RETURN-Regeln garantiert vor dem DNAT, unabhängig davon, was schon in der Kette liegt.

UDP braucht eine eigene Regel. Eine Regel mit -p tcp matcht ausschließlich TCP, ohne die zweite Zeile kommt weder DNS noch WireGuard noch VoIP bei der Firewall an. Umgekehrt gilt: Wer einen UDP-Dienst auf dem Host selbst betreiben will, etwa WireGuard direkt auf Proxmox, braucht dafür eine eigene RETURN-Regel wie bei Port 22.

Schritt 3: Konfiguration prüfen und neu starten

Vor dem Neustart das Rescue-System des Providers bereithalten, bei Hetzner im Robot unter Server, Rescue. Ein Tippfehler in /etc/network/interfaces bedeutet sonst einen Ausfall bis zum Eingriff über die Konsole.

Syntax und Ergebnis lassen sich vorab ohne Neustart prüfen:

ifreload -a
ip -br a
iptables -t nat -S PREROUTING

Die Kette muss danach so aussehen:

-A PREROUTING -i vmbr0 -p tcp -m tcp --dport 8006 -j RETURN
-A PREROUTING -i vmbr0 -p tcp -m tcp --dport 22 -j RETURN
-A PREROUTING -i vmbr0 -p tcp -j DNAT --to-destination 10.0.10.2
-A PREROUTING -i vmbr0 -p udp -j DNAT --to-destination 10.0.10.2

Entscheidend ist die Reihenfolge: Die beiden RETURN-Regeln müssen vor den DNAT-Regeln stehen. Steht das DNAT vorne, ist der Host weder per SSH noch über die GUI erreichbar. Passt alles, folgt der Neustart:

/usr/sbin/reboot

Zugriff auf die Proxmox-GUI

SSH und GUI bleiben dank der beiden RETURN-Regeln direkt erreichbar:

ssh root@<server-ipv4>
https://<server-ipv4>:8006

Proxmox liefert dabei sein selbst signiertes Zertifikat aus, der Browser warnt entsprechend. Da der Host öffentlich erreichbar ist, lässt sich das über Datacenter, ACME mit Let's Encrypt lösen. Die HTTP-01-Validierung funktioniert allerdings nicht, weil Port 80 an die pfSense weitergeleitet wird. Es bleibt die DNS-01-Validierung über einen unterstützten DNS-Anbieter oder eine zusätzliche RETURN-Regel für Port 80.

Absicherung des Hosts

Ein öffentlich erreichbares Proxmox wird innerhalb von Stunden gefunden und durchprobiert. Diese drei Punkte gehören deshalb dazu:

  • SSH nur mit Schlüssel, in /etc/ssh/sshd_config oder einer Datei unter /etc/ssh/sshd_config.d/:
PermitRootLogin prohibit-password
PasswordAuthentication no
  • Zwei-Faktor-Authentifizierung für die GUI unter Datacenter, Permissions, Two Factor, in der Regel TOTP.
  • fail2ban auf dem Host, mit einem Jail für SSH und einem für pveproxy, der auf /var/log/daemon.log schaut.

Wem das zu viel Angriffsfläche ist, der lässt die RETURN-Regel für 8006 weg und nutzt den SSH-Tunnel aus dem letzten Abschnitt.

Konfiguration bei pfSense

WAN-Interface

Unter Interfaces → WAN: IPv4 auf Static setzen, Adresse 10.0.10.2/30, Gateway 10.0.10.1. Die Optionen Block private networks und Block bogon networks deaktivieren, da die WAN-IP eine private Transit-Adresse ist.

IPv6 optional: Static fd00:10::2/64, Gateway fd00:10::1.

LAN-Interface

Unter Interfaces → LAN: IPv4 Static, z. B. 192.168.10.254/24. Weitere Subnetze als OPT-Interfaces hinzufügen.

DHCP

Unter Services → DHCP Server pro Interface aktivieren, Range und Gateway eintragen. Als DNS-Server entweder die pfSense-IP mit dem DNS Resolver oder direkt externe Server wie 1.1.1.1.

Outbound NAT

Unter Firewall → NAT → Outbound den Modus auf Hybrid stellen. Alle internen Subnetze gehen per Masquerade über das WAN raus.

Dienste hinter der Firewall veröffentlichen

Da der komplette eingehende Verkehr auf der pfSense landet, werden alle Weiterleitungen dort gepflegt, unter Firewall → NAT → Port Forward. Die Ports 22 und 8006 stehen dafür nicht zur Verfügung, sie enden auf dem Proxmox-Host. Eine VM, die selbst per SSH erreichbar sein soll, bekommt also eine Weiterleitung auf einem anderen Port.

Konfiguration bei OPNsense

Die Host-Konfiguration aus Schritt 2 ist identisch, nur die Firewall wird anders bedient. Die folgenden Angaben stammen aus einer laufenden OPNsense 26.7 in genau diesem Aufbau.

WAN-Interface

Unter Interfaces → WAN: IPv4 auf Static, Adresse 10.0.10.2/30. Das Gateway 10.0.10.1 wird dabei als neues Gateway angelegt. IPv6 optional Static fd00:10::2/64 mit Gateway fd00:10::1.

Anders als oft empfohlen können Block private networks und Block bogon networks dabei aktiviert bleiben. Die erzeugte Regel blockt nach Quelladresse:

block in log quick on vtnet0 inet from {10.0.0.0/8} to {any}   # Block private networks from WAN

Eingehende Verbindungen behalten durch das DNAT auf dem Proxmox-Host ihre öffentliche Quelladresse, sie werden also nicht getroffen. Blockiert wird nur, was tatsächlich aus einem privaten Netz kommt, insbesondere der Proxmox-Host selbst mit 10.0.10.1. Wer die Firewall-GUI vom Host aus erreichen will, schaltet Block private networks ab oder legt eine passende Pass-Regel an.

Interfaces WAN: Static IPv4 10.0.10.2/30, Gateway 10.0.10.1

LAN und weitere Interfaces

Unter Interfaces → LAN: IPv4 Static, z. B. 192.168.10.254/24. Weitere Netze kommen als OPT-Interfaces dazu und liegen jeweils auf einer eigenen Bridge des Hosts. Bei VirtIO heißen die Karten vtnet0 bis vtnet3, in der Reihenfolge der Netzwerkgeräte der VM.

DHCP mit Kea

ISC DHCP ist in aktuellen Versionen nicht mehr enthalten, es stehen Kea und Dnsmasq zur Verfügung. Der Weg führt über Services → Kea DHCP → Kea DHCPv4.

Dort zuerst unter General den Dienst aktivieren und die Interfaces auswählen, auf denen er lauschen soll. Danach pro Netz ein Subnetz anlegen, jeweils mit Pool, Router und DNS-Server:

192.168.10.0/24   Pool 192.168.10.10-192.168.10.245   Router 192.168.10.254   DNS 192.168.10.254
192.168.30.0/24   Pool 192.168.30.100-192.168.30.200  Router 192.168.30.254   DNS 192.168.30.254

Wer den DNS-Resolver der Firewall verteilt und interne Namen aus einem eigenen DNS-Server nutzt, legt vorher unter Services → Unbound DNS → Query Forwarding eine Forward-Zone für die interne Domain an. Sonst rekursiert Unbound bis zu den Root-Servern und alle Clients verlieren ihre internen Namen.

Services Kea DHCP: aktivierte Interfaces und Subnetze mit Pool, Router und DNS

Outbound NAT

Unter Firewall → NAT → Outbound reicht der Modus Automatic outbound NAT rule generation. OPNsense erzeugt daraus für jedes interne Netz eine Regel auf die WAN-Adresse:

nat on vtnet0 inet from (vtnet1:network) to any -> (vtnet0:0) port 1024:65535

Hybrid wird erst gebraucht, wenn zusätzlich eigene Outbound-Regeln dazukommen sollen, etwa für ein VPN-Netz.

Firewall NAT Outbound im Modus Automatic mit der erzeugten NAT-Regel

VirtIO-Offloads abschalten

Unter Interfaces → Settings die drei Punkte Hardware CRC, Hardware TSO und Hardware LRO deaktivieren. FreeBSD auf VirtIO produziert mit aktivem Offloading sonst schwer zu findende Paketfehler. Kontrolle auf der Shell, in der Zeile darf weder TXCSUM noch TSO oder LRO stehen:

ifconfig vtnet0 | grep options

Interfaces Settings: Hardware CRC, TSO und LRO deaktiviert

Optional: Geroutetes IPv6-Subnetz für VMs (Hetzner)

Jeder Hetzner Dedicated Server kommt mit einem /64-Subnetz, das auf vmbr0 für den Host selbst konfiguriert wird. Damit auch VMs öffentliche IPv6-Adressen erhalten, muss im Hetzner Robot unter IPs / Subnets ein zusätzliches geroutetes /64 bestellt werden.

Erst dann ist die iface vmbr1 inet6 static-Sektion aus Schritt 2 sinnvoll. Sie erledigt drei Dinge: Forwarding einschalten, das bestellte /64 über den Transit-Link zur pfSense routen und die passenden FORWARD-Regeln setzen. Der Platzhalter <routed-ipv6-prefix> ist das zusätzlich bestellte Präfix, nicht das /64 von vmbr0.

IPv6 wird dabei geroutet und nicht genattet. Die pfSense bekommt auf dem Transit-Interface fd00:10::2/64 mit Gateway fd00:10::1 und verteilt das geroutete /64 auf dem LAN-Interface, entweder statisch oder per DHCPv6 und SLAAC.

Optional: Proxmox-GUI schließen und per SSH-Tunnel zugreifen

Wer die GUI gar nicht nach außen anbieten will, nimmt dieselbe Konfiguration aus Schritt 2, streicht daraus aber die beiden Zeilen für Port 8006. Es sind genau diese:

        # Proxmox-GUI auf dem Host selbst, am DNAT vorbei
        post-up iptables -t nat -C PREROUTING -i vmbr0 -p tcp --dport 8006 -j RETURN 2>/dev/null || iptables -t nat -I PREROUTING 1 -i vmbr0 -p tcp --dport 8006 -j RETURN

        post-down iptables -t nat -D PREROUTING -i vmbr0 -p tcp --dport 8006 -j RETURN 2>/dev/null || true

Die Regel für Port 22 bleibt, sonst kommt man nicht mehr auf den Host. Der Rest der Datei ist unverändert. Danach ifreload -a ausführen und kontrollieren, dass die Kette nur noch drei Regeln hat:

-A PREROUTING -i vmbr0 -p tcp -m tcp --dport 22 -j RETURN
-A PREROUTING -i vmbr0 -p tcp -j DNAT --to-destination 10.0.10.2
-A PREROUTING -i vmbr0 -p udp -j DNAT --to-destination 10.0.10.2

Wichtig bei einer bereits laufenden Installation: ifreload -a allein entfernt die alte 8006-Regel nicht zuverlässig, weil die Zeile mit post-down ja gerade gelöscht wurde. Einmal von Hand nachhelfen:

iptables -t nat -D PREROUTING -i vmbr0 -p tcp --dport 8006 -j RETURN

Port 8006 geht danach an die pfSense und ist von außen nicht mehr erreichbar. Der Zugriff läuft über einen SSH-Tunnel auf den Host:

ssh -N -L 8006:127.0.0.1:8006 root@<server-ipv4>

Danach im Browser https://localhost:8006 aufrufen. Die Option -N baut nur den Tunnel auf und öffnet keine Shell. 127.0.0.1 ist gegenüber localhost die bessere Wahl, weil der Name sonst serverseitig auf ::1 aufgelöst werden kann. Die Zertifikatswarnung bleibt in dieser Variante bestehen, da das Proxmox-Zertifikat nicht auf localhost ausgestellt ist.

Der Tunnel endet auf dem Proxmox-Host und ist damit unabhängig von der pfSense. Er funktioniert auch dann, wenn die Firewall-VM steht oder sich selbst ausgesperrt hat, denn über die noVNC-Konsole in der GUI kommt man an jede VM heran. Genau deshalb liegt Port 22 außerhalb des DNAT.

Eine einzige öffentliche IP-Adresse genügt für ein vollständiges Firewall-Setup mit VM-Isolation auf Proxmox.

Kommentare

0

Noch keine Kommentare. Sei der Erste!

Kommentar schreiben

Kommentare werden vor der Veröffentlichung geprüft.