Docker öffnet Ports auf der Firewall für Container. Das war für mich unerwartet. Es gibt auch ein paar Informationen im Netz, wie man das verhindern kann. Mir ist das alles zu joker. Es kommt auch eine richtige Firewall vor den Docker- (Oder-was-auch-immer-)Server. Da dieser Server bei einem Hoster steht beschreibt dieser Artikel, wie ich das für mich mit OPNsense umgesetzt habe.
Vorüberlegungen
Ich habe einen physischen Server bei meinem Hoster gemietet. Auf diesem sollen die Dienste hauptsächlich als Docker-Container umgesetzt werden. Bei einem ersten Experiment habe ich allerdings festgestellt, dass trotz ufw
eingerichteter Firewall das Administrations-Interface einer Docker-Anwendung aus dem Internet erreichbar war.
Eine kurze Recherche zeigt, dass ich nicht der erste war, der in die Falle getappt war. Klar war es ein Konfigurationsfehler von mir (siehe Docker Manual - Networking overview und Ask Ubuntu -). Ich hätte meinen Docker-Rechner anders konfigurieren sollen. Das soll hier aber nicht das Thema sein. Vielmehr soll eine echte Firewall, auf die Docker oder andere keinen Einfluss haben, eingesetzt werden.
Schön ist es immer, wenn man ein eigene physische Firewall vorschalten kann. Mein Hoster Hetzner bietet sogar eine einfach zu konfigurierende Firewall (siehe Hetzner Docs - Firewall), aber die ist sehr beschränkt. Es bleibt also nur eine OPNsense einzurichten;)
Konzept
Auf dem physischen Server werden mehrere virtuelle Maschinen eingerichtet. Eine virtuelle Maschine hat eine Sonderrolle. Sie ist die Firewall. Sie soll möglichst den kompletten Datenstrom des Rechners filtern. Am schönsten wäre es, wenn die Firewall-VM die Ethernet-Schnittstelle des physischen Rechners übernehmen könnte.1
Leider verliert man dadurch den Zugriff auf das Host-System über SSH. Das kann man zwar in der Firewall wieder reparieren. Es ist mir allerdings zu kompliziert (virtuelles Netzwerk für Host-System, SSH des Host-Systems daran binden und Regeln definieren). Es ist auch ein Recovery-Problem. Wenn die VM mit der Firewall abstürzt, löst nur ein Reset (Uarrgs!) des physischen Rechners das Problem, da kein Zugriff auf die Steuerung der VMs möglich ist.
Es bleibt also nur den WAN-Port der Firewall-VM zu virtualisieren und den IP-Verkehr (außer SSH) auf diese virtuelle Schnittstelle umzuleiten. Den Lösungsansatz zu diesem Konzept habe ich aus dem Youtube-Video von “The Morpheus Tutorials”. Der Lösungsansatz ist mit netfilter den IP-Verkehr mit PREROUTING- und POSTROUTING-Regeln entsprechend umleiten. Dies führt zu meinem finalen Konzept.
Dazu sind prinziell folgende Schritte notwendig.
- Auf dem Server Ubuntu (aktuell 22.04) installieren.
- Eine Bridge
vmbr0
2 erstellen. - IP-Forwarding einschalten.
- Eine PREROUTING-Regel für TCP, die alles außer SSH an die Bridge weiterleitet, einrichten.
- Eine PREROUTING-Regel für UDP einrichten.
- Eine POSTROUTING-Regel die den IP-Verkehr unter der externen IP maskiert einrichten.
Konfiguration
Die Konfiguration führe ich mit Ansible aus. Die Schritte werden hier aber ohne Ansible beschrieben.
Bridge einrichten
Das Ubuntu-Image von Hetzner verwendet Netplan zur IP-Konfiguration. Jetzt gibt es mehrere Möglichkeiten.
- Wie im Video von The Morpheus Tutorials einfach
/etc/network/interfaces
anpassen. Unter Ubuntu kann ich aber die Seiteneffekte nicht einschätzen, deshalb ist es keine Option für mich. - Die Netplan-Konfiguration erweitern. Das war meine bevorzugte Lösung, auch wenn es dabei weitere Probleme gibt…
- Von Ubuntu zu Debian 11 wechseln.
Letztendlich habe ich die dritte Variante gewählt. Netplan hat mir mehrmals bei meinen Experimenten den Remote-Zugang zugenagelt. Auch wenn es eine schöne Art ist das Netzwerk zu konfigurieren, hat es nicht so funktioniert, wie ich es erwartet hatte. Die Regeln ließen sich auch nicht debuggen, da es eigentlich nur die Möglichkeit gibt, die neue Konfiguration (zum Glück mit Timeout) anzuwenden. Letztendlich hat es mir zu lange gedauert. Ich muss irgendwann Netplan auf einer lokalen Maschine ausprobieren, damit ich die Konfiguration auf der Konsole überprüfen kann. Ohne Konsole macht es keinen Spaß.
IP-Forwarding einschalten
Damit Linux IP-Pakete zwischen verschiedenen Netzwerken routet, muss das IP-Forwarding eingeschaltet werden. Das erreicht man mit dem Kommando sysctl
(Vorsicht: nicht systemctl
). Die Option -w
oder --write
schreibt einen Wert in eine Systemvariable. Für das Weiterleiten gibt es eine Systemvariable für IPv4 und eine für IPv6.
sysctl --write net.ipv4.ip_forward=1
sysctl --write net.ipv6.conf.all.forwarding=1
Die beiden Kommandos könnte man beim Starten des Systems aufrufen oder die Variablen in einer sysctl.conf
-Datei unter /etc/sysctl.d
. In diesem Fall ist es aber schöner die Weiterleitung mit den post-up
-Hooks einzuschalten, wenn eth0
konfiguriert wurde siehe den Ansible-Schnipsel unten.
Netfilter-Regeln erstellen
Wie oben beschrieben müssen wir nach dem Errichten der Bridge den IPv4-Traffic außer dem für SSH (und Proxmox, wenn man mit Proxmox virtualisiert) mit PREROUTING- und POSTROUTING-Regeln umleiten. Im Beispiel nehme ich einmal an, dass wir Proxmox verwenden. $host_ipv4
steht für die öffentlicher IPv4-Adresse des Servers. $wan_ipv4
steht für die WAN-IPv4-Adresse der Firewall-VM. Als Prefix verwende ich /30
, also ein Netz mit zwei Host-Adressen. Das unterscheidet sich von der Anleitung im Video. Punkt-zu-Punkt-Verbindungen sind zwar im RFC3021 seit 2003 definiert, sind aber vielen Administratoren und Entwicklern unbekannt. Auch das Web-GUI von OPNsense erlaubt nicht die Auswahl der PREFIX /31
.
iptables -t nat -A PREROUTING -i $host_ipv4 -p tcp \
-m multiport ! --dport 22,8006 -j DNAT --to $wan_ipv4
iptables -t nat -A PREROUTING -i $host_ipv4 -p udp \
-j DNAT --to $wan_ipv4
iptables -t nat -A POSTROUTING -s $wan_ipv4/30 \
-o {{ ansible_default_ipv4.interface }} -j MASQUERADE
iptables -t nat -D POSTROUTING -s $wan_ipv4/30 \
-o {{ ansible_default_ipv4.interface }} -j MASQUERADE
Ansible-Beispiel
---
- name: Ensure bridges exists
blockinfile:
path: /etc/network/interfaces
block: |
auto {{ wan_bridge_device }}
iface {{ wan_bridge_device }} inet static
address {{ wan_bridge_ipv4 }}/{{ wan_ipv4_netmask }}
bridge-ports none
bridge-stp off
bridge-fd 0
post-up sysctl -w net.ipv4.ip_forward=1
post-up iptables -t nat -A PREROUTING -i {{ ansible_default_ipv4.interface }} -p tcp -m multiport ! --dport 22,8006 -j DNAT --to {{ wan_ipv4_address }}
post-up iptables -t nat -A PREROUTING -i {{ ansible_default_ipv4.interface }} -p udp -j DNAT --to {{ wan_ipv4_address }}
post-up iptables -t nat -A POSTROUTING -s {{ wan_ipv4_address }}/{{ wan_ipv4_netmask }} -o {{ ansible_default_ipv4.interface }} -j MASQUERADE
post-down iptables -t nat -D POSTROUTING -s {{ wan_ipv4_address }}/{{ wan_ipv4_netmask }} -o {{ ansible_default_ipv4.interface }} -j MASQUERADE
#OPNsense WAN
auto {{ conf_bridge_device }}
iface {{ conf_bridge_device }} inet static
address {{ conf_bridge_ipv4 }}/{{ conf_ipv4_netmask }}
bridge-ports none
bridge-stp off
bridge-fd 0
#OPNsense configuration interface
auto {{ lan_bridge_device }}
iface {{ lan_bridge_device }} inet static
bridge-ports none
bridge-stp off
bridge-fd 0
#OPNsense isolated LAN
# end
notify: Reboot system
Quellen
- Docker: Docker Manual - Networking overview (zuletzt abgerufen am 2023-01-01)
- Stackoverflow: Ask Ubuntu - Uncomplicated Firewall (UFW) is not blocking anything when using Docker (zuletzt abgerufen am 2023-01-01)
- Hetzner: Hetzner Docs - Firewall (zuletzt abgerufen 2023-01-01)
- Retana et. al.: Using 31-Bit Prefixes on IPv4 Point-to-Point Links. RFC 3021, December 2000.
- The Morpheus Tutorials: Install OpnSense on Proxmox: 1 IP only [Hetzner Server]. Youtube-Video (zuletzt abgerufen am 2023-01-01)
- Canonical: Netplan Dokumentation (zuletzt abgerufen 2023-01-01)
Bildnachweis
Die Bilder und Grafiken sind selbst erstellt. Für den Aufmacher wurden die Logos von OPNsense unter den Legal Notices3 und libvirt CC-BY-SA Redhat, 20104 verwendet.
-
eth0
steht hier für die Ethernet-Schnittstelle. Normalerweise werden die Ethernet-Schnittstellen durch den Treiber benannt, zum Beispielenp35s0
bei meinem Server. ↩︎ -
Der Name der Bridge ist nicht so wichtig. Ich verwende
vmbr0
für die WAN-Schnittstelle der Firewall. Der Virtual Machine Manager erzeugt Bridges nach dem Schemavirbr<n>
. So gibt es keine Konflikte. ↩︎ -
OPNsense Trademark policy
OPNsense is a trademark. If you wish to use the name or logo in any way, you must comply with this policy.
- The name and logo may be used to promote OPNsense based products or services.
- The name and logo may be used to promote or serve OPNsense or related projects and their communities.
- When using the logo on online media like websites, social media and apps the logo should link to https://opnsense.org
- You must request our permission to use derivatives of the name and/or logo at project @ opnsense.org prior to any use of the derivative name and/or logo, and we may grant or withhold permission to use the derivative name and/or logo in our sole discretion.
- You may not state or otherwise lead people to believe, that you represent the OPNsense project in any way other than as an individual or corporate contributor to the project.
The official OPNsense logo is available for download: OPNsense logo.
-
License
Copyright © 2010 Red Hat, Inc.
The works in this document are licensed by Red Hat under a Creative Commons Attribution–Share Alike 3.0 Unported license (“CC-BY-SA”), unless otherwise noted.
An explanation of CC-BY-SA is available at:
http://creativecommons.org/licenses/by-sa/3.0/
The original authors of these works, and Red Hat, designate the libvirt Project as the “Attribution Party” for purposes of CC-BY-SA. In accordance with CC-BY-SA, if you distribute these works or an adaptation of them, you must provide the URL for the original version.
Red Hat, as the licensor of these works, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.