OPNsense virtualisieren

Kategorien: tutorial

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

Grundlegendes Konzept

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.

Finales Konzept

Dazu sind prinziell folgende Schritte notwendig.

  1. Auf dem Server Ubuntu (aktuell 22.04) installieren.
  2. Eine Bridge vmbr02 erstellen.
  3. IP-Forwarding einschalten.
  4. Eine PREROUTING-Regel für TCP, die alles außer SSH an die Bridge weiterleitet, einrichten.
  5. Eine PREROUTING-Regel für UDP einrichten.
  6. 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.

  1. 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.
  2. Die Netplan-Konfiguration erweitern. Das war meine bevorzugte Lösung, auch wenn es dabei weitere Probleme gibt…
  3. 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

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.


  1. eth0 steht hier für die Ethernet-Schnittstelle. Normalerweise werden die Ethernet-Schnittstellen durch den Treiber benannt, zum Beispiel enp35s0 bei meinem Server. ↩︎

  2. 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 Schema virbr<n>. So gibt es keine Konflikte. ↩︎

  3. 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.

    1. The name and logo may be used to promote OPNsense based products or services.
    2. The name and logo may be used to promote or serve OPNsense or related projects and their communities.
    3. When using the logo on online media like websites, social media and apps the logo should link to https://opnsense.org
    4. 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.
    5. 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.

     ↩︎
  4. 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.

     ↩︎

Das könnte Sie auch interessieren