Nftables und layer2 Firewall: Unterschied zwischen den Versionen
Zur Navigation springen
Zur Suche springen
(→Regeln) |
|||
| (24 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
| Zeile 1: | Zeile 1: | ||
| + | =Template= | ||
| + | *debian-template klonen mit dem Namen bridgefw | ||
=Vorab= | =Vorab= | ||
;Bei Virtualisierungen Bridge Ports auf Promisc setzen | ;Bei Virtualisierungen Bridge Ports auf Promisc setzen | ||
| + | *Das sind Adapater 2 und Adapater 3 | ||
| + | *Dies erlaubt der virtuellen Netzwerkkarte, auch Pakete zu empfangen, die nicht an ihre MAC-Adresse gerichtet sind - notwendig für den Bridge-Betrieb. | ||
[[Datei:Vm-promisc-1.png]] | [[Datei:Vm-promisc-1.png]] | ||
| + | ;VirtualBox | ||
| + | *Unter "Erweitert" den Promiscuous Mode auf "Allow All" stellen (siehe Bild). | ||
| + | ;Proxmox/KVM | ||
| + | *An der Linux-Bridge des Hosts ist nichts einzustellen. | ||
| + | *Ist die Proxmox-Firewall für die VM aktiv, muss dort der MAC-Filter ausgeschaltet werden, sonst werden fremde MAC-Adressen verworfen. | ||
| + | ;In der VM selbst | ||
| + | *Die Bridge schaltet ihre Ports beim Hochfahren automatisch in den Promiscuous Mode, hier ist nichts zu tun. | ||
| + | |||
| + | === Netzkonfiguration BRDIGEFW === | ||
| + | |||
| + | {| class="wikitable" style="background-color: #f2f2f2;" | ||
| + | ! Parameter !! Wert !! Erläuterung | ||
| + | |- | ||
| + | | '''Netzwerk (NIC 1)''' || SERVER|| Interface-Zuweisung in VirtualBox | ||
| + | |- | ||
| + | | '''Netzwerk (NIC 2)''' || DMZ|| Interface-Zuweisung in VirtualBox (Promiscuous-Modus Allow all) | ||
| + | |- | ||
| + | | '''Netzwerk (NIC 3)''' || DMZ-OUT|| Interface-Zuweisung in VirtualBox (Promiscuous-Modus Allow all) | ||
| + | |- | ||
| + | | '''IP''' || 10.2XX.1.9 || Statische IP | ||
| + | |- | ||
| + | | '''CIDR''' || 24 || Classless Inter-Domain Routing Präfixlänge | ||
| + | |- | ||
| + | | '''GW''' || 10.2XX.1.1 || GATEWAY | ||
| + | |- | ||
| + | | '''NS''' || 10.88.2XX.21 || Resolver | ||
| + | |- | ||
| + | | '''FQDN''' || bridgefw.it2XX.int || Fully Qualified Domain Name | ||
| + | |- | ||
| + | | '''SHORT''' || smb || Short Name | ||
| + | |- | ||
| + | | '''DOM''' || it2XX.int|| Domain Name | ||
| + | |} | ||
| + | |||
| + | =Setup= | ||
| + | *debian-setup.sh -f bridgefw.it2XX.int -a 10.2XX.1.9/24 -n 10.88.2XX.21 -g 10.2XX.1.1 | ||
| + | =Auf dem Hostsystem= | ||
| + | *vi ~/.ssh/config | ||
| + | Host bridgefw | ||
| + | Hostname 10.2XX.1.9 | ||
| + | User kit | ||
| + | ProxyJump kit@fw | ||
| + | ;Key übertragen | ||
| + | *ssh-copy-id bridgefw | ||
| + | ;Einloggen | ||
| + | *ssh bridgefw | ||
| + | |||
| + | =Was ist eine Layer 2 Firewall?= | ||
| + | *Eine normale Firewall arbeitet auf Layer 3 (IP-Ebene) und benötigt eine IP-Adresse im Datenpfad. | ||
| + | *Eine Layer 2 Firewall greift dagegen auf Ethernet-Ebene (MAC) - sie kann '''transparent''' zwischen zwei Netzwerksegmente gestellt werden, ohne eine eigene IP-Adresse im Datenpfad zu benötigen. | ||
| + | *Der Datenverkehr läuft über eine Bridge, die nftables zur Filterung nutzt. | ||
| + | *Die Clients merken von der Firewall nichts: Gateway, Adressen und Subnetz bleiben unverändert. | ||
| + | =Schaubild= | ||
| + | {{#drawio:bridgefw}} | ||
=Installation der Bridgeutils= | =Installation der Bridgeutils= | ||
*apt install bridge-utils | *apt install bridge-utils | ||
| + | *Das Paket liefert die Skripte, mit denen ifupdown die Optionen bridge_ports, bridge_fd und bridge_stp auswertet. | ||
=/etc/network/interfaces= | =/etc/network/interfaces= | ||
| Zeile 14: | Zeile 73: | ||
auto enp0s3 | auto enp0s3 | ||
iface enp0s3 inet static | iface enp0s3 inet static | ||
| − | address 10. | + | address 10.2XX.1.9/24 |
| − | gateway 10. | + | gateway 10.2XX.1.1 |
| + | dns-nameservers 10.88.2XX.21 | ||
| + | dns-search it2XX.int | ||
auto enp0s8 | auto enp0s8 | ||
| Zeile 26: | Zeile 87: | ||
iface vmbr0 inet manual | iface vmbr0 inet manual | ||
bridge_ports enp0s8 enp0s9 | bridge_ports enp0s8 enp0s9 | ||
| − | bridge_fd | + | bridge_fd 0 |
bridge_stp no | bridge_stp no | ||
| + | |||
</pre> | </pre> | ||
| + | ;Hinweise zur Konfiguration | ||
| + | ;enp0s3: | ||
| + | *Management-Interface mit eigener IP-Adresse. Es liegt '''nicht''' in der Bridge. | ||
| + | ;vmbr0 inet manual: | ||
| + | *Die Bridge bekommt bewusst keine IP-Adresse - sie arbeitet rein transparent. | ||
| + | ;bridge_fd 0: | ||
| + | *Forward Delay. Er wird nur bei aktivem STP genutzt: Die Ports durchlaufen dann die Zustände Listening und Learning, bevor sie Pakete weiterleiten. | ||
| + | *Ohne STP schaltet der Kernel die Ports sofort auf Forwarding. Der Wert 0 macht das explizit. | ||
| + | ;bridge_stp no: | ||
| + | *STP (Spanning Tree Protocol) ist deaktiviert. Da nur zwei Ports vorhanden sind, ist kein Loop möglich und STP nicht notwendig. | ||
| + | ;Bridge starten | ||
| + | *ifup vmbr0 | ||
| − | * | + | =Kontrolle der Bridge= |
| + | ;Welche Ports gehören zur Bridge und in welchem Zustand sind sie? | ||
| + | *bridge link show | ||
| + | ;Welche MAC-Adressen hat die Bridge an welchem Port gelernt? (FDB) | ||
| + | *bridge fdb show br vmbr0 | grep -v permanent | ||
| + | ;Welche Interfaces gehören zu Bridgen | ||
| + | *brctl show | ||
| + | bridge name bridge id STP enabled interfaces | ||
| + | vmbr0 8000.76e085f914d6 no enp0s8 | ||
| + | enp0s9 | ||
| + | =Erste Maschine hinte die Bridge Firewall plazieren= | ||
| + | *www -> Interface auf DMZ-OUT ändern | ||
| + | *Ping mal von www nach 1.1.1.1 | ||
| + | *Kontrolle auf der bridgefw auf den Schnittstellen enp0s8 und enp0s9 per tcpdump | ||
=Bridge-Filterung= | =Bridge-Filterung= | ||
| − | + | ==Arten von Bridge-Ketten== | |
| − | =Arten von Bridge-Ketten= | + | *Die Bridge-Familie bietet fünf Kettenarten (Hooks): |
| − | *Die Bridge-Familie bietet | ||
| − | |||
;prerouting: | ;prerouting: | ||
| − | *Hier können Pakete vor der Forward-Datenbank (FDB) Entscheidung gefiltert werden. | + | *Hier können Pakete vor der Forward-Datenbank (FDB) Entscheidung gefiltert werden. |
| − | *Die FDB liefert den Bridge-Port, der für eine bestimmte Ziel-MAC-Adresse verwendet wird. | + | *Die FDB liefert den Bridge-Port, der für eine bestimmte Ziel-MAC-Adresse verwendet wird. |
| − | *Wenn es noch keinen Eintrag in der FDB für eine gegebene Ziel-MAC gibt, werden die Pakete an alle Bridge-Ports geflutet. | + | *Wenn es noch keinen Eintrag in der FDB für eine gegebene Ziel-MAC gibt, werden die Pakete an alle Bridge-Ports geflutet. |
*Nach der FDB-Entscheidung können Pakete entweder dem Eingangs- oder dem Weiterleitweg folgen. | *Nach der FDB-Entscheidung können Pakete entweder dem Eingangs- oder dem Weiterleitweg folgen. | ||
;input: | ;input: | ||
| − | *Hier können Pakete gefiltert werden, die an den IP-Stack übergeben werden. | + | *Hier können Pakete gefiltert werden, die an den IP-Stack übergeben werden. |
| − | *Wenn | + | *Wenn die Bridge-Schnittstelle eine IP-Adresse hat und Pakete an diese IP-Adresse gehen, folgen die Pakete diesem Pfad (die Pakete verlassen die Bridge und treten in den IP-Stack ein). |
| − | ;forward: | + | ;forward: |
| − | *Diese Hook | + | *Diese Hook filtert Pakete, die von einem Bridge-Port eingehen und an einen anderen Bridge-Port geliefert werden (der Bridge-Weiterleitungspfad). |
| − | ;output: | + | *'''Im vorliegenden Szenario wird ausschließlich diese Chain genutzt''' - die Bridge hat keine IP-Adresse im Datenpfad, es wird rein gebridged. |
| − | *Hier können Pakete gefiltert werden, die vom IP-Stack kommen und deren Ziel | + | ;output: |
| − | ;postrouting: | + | *Hier können Pakete gefiltert werden, die vom IP-Stack kommen und deren Ziel ein Bridge-Port ist. |
| + | ;postrouting: | ||
*Hier können Pakete gefiltert werden, die entweder vom IP-Stack kommen oder von einem anderen Bridge-Port weitergeleitet werden. | *Hier können Pakete gefiltert werden, die entweder vom IP-Stack kommen oder von einem anderen Bridge-Port weitergeleitet werden. | ||
| − | = | + | |
| − | + | ==Bridge-Familie und br_netfilter== | |
| − | * | + | *Gebridgte Pakete laufen normalerweise '''nur''' durch die Ketten der Bridge-Familie, nicht durch ip/inet-Tabellen. |
| − | = | + | *Ist jedoch das Kernelmodul br_netfilter geladen (z.B. automatisch durch Docker), laufen sie zusätzlich durch die ip/inet-Hooks. Das führt zu schwer nachvollziehbaren Effekten. |
| − | + | *Kontrolle: | |
| + | lsmod | grep br_netfilter | ||
| + | *Keine Ausgabe bedeutet: Modul nicht geladen, alles in Ordnung. | ||
| + | |||
| + | =ARP= | ||
| + | *ARP ist im Gegensatz zur Layer 3 Firewall '''nicht''' automatisch freigeschaltet und muss explizit erlaubt werden. | ||
| + | *Eine Layer 3 Firewall sieht ARP gar nicht, weil ARP kein IP-Protokoll ist. Die Bridge sieht dagegen jeden Ethernet-Frame. | ||
| + | *Ohne ARP-Freischaltung ist keine Kommunikation im Netz möglich, da MAC-Adressen nicht aufgelöst werden können. | ||
| + | *Beispiel zum Akzeptieren von ARP-Paketen: <code>nft add rule bridge filter forward ether type arp accept</code> | ||
| + | |||
=Zustandsabhängige Filterung= | =Zustandsabhängige Filterung= | ||
| − | *Die Bridge-Familie unterstützt | + | *Die Bridge-Familie unterstützt Connection Tracking (Verbindungsverfolgung), ab Kernel 5.3. |
| − | * | + | *Durch Matching auf den Verbindungsstatus (<code>ct state</code>) können bestehende Verbindungen automatisch durchgelassen werden, ohne jede Richtung explizit freizuschalten. |
| − | + | ;Die wichtigsten Zustände | |
| − | + | ;new: | |
| − | * | + | *Erstes Paket einer Verbindung. |
| − | * | + | ;established: |
| − | + | *Paket gehört zu einer bereits bekannten Verbindung. | |
| − | * | + | ;related: |
| − | + | *Paket gehört zu einer bekannten Verbindung, ist aber eine eigene (z.B. ICMP-Fehlermeldung zu einer TCP-Verbindung). | |
| − | + | ;Besonderheit in der Bridge | |
| − | * | + | *Connection Tracking gibt es hier nur für IP. ARP wird nicht verfolgt und braucht deshalb eigene Regeln. |
| − | + | ||
| − | + | =Bridge Firewall= | |
| − | + | {{#drawio:bridge-fw}} | |
| + | |||
=Regeln= | =Regeln= | ||
| + | ;Konzept | ||
| + | *<code>enp0s8</code> ist die '''offene''' Seite (z.B. Richtung Internet/unsicheres Netz) | ||
| + | *<code>enp0s9</code> ist die '''gefilterte''' Seite (z.B. internes Netz) | ||
| + | *<code>enp0s3</code> ist das Management-Interface im vertrauenswürdigen LAN, es wird hier nicht gefiltert | ||
| + | *Die Bridge selbst hat keine IP-Adresse und ist aus den gefilterten Netzen nicht erreichbar | ||
| + | *Policy ist <code>drop</code> - alles was nicht explizit erlaubt ist, wird verworfen | ||
| + | *cat /etc/nftables.conf | ||
<pre> | <pre> | ||
#!/usr/sbin/nft -f | #!/usr/sbin/nft -f | ||
| + | |||
| + | define open = enp0s8 | ||
| + | define filter = enp0s9 | ||
flush ruleset | flush ruleset | ||
table bridge filter { | table bridge filter { | ||
| − | |||
chain forward { | chain forward { | ||
type filter hook forward priority 0; policy drop; | type filter hook forward priority 0; policy drop; | ||
| + | |||
| + | # Bestehende Verbindungen durchlassen (Connection Tracking) | ||
ct state established,related accept | ct state established,related accept | ||
| − | iif | + | |
| − | iif | + | # ARP freischalten (beide Richtungen notwendig) |
| − | + | iif $open ether type arp accept | |
| − | + | iif $filter ether type arp accept | |
| − | + | ||
| − | + | #### ICMP von filter nach open freischalten | |
| − | # | + | #iif $filter oif $open ip protocol icmp ct state new accept |
| − | log prefix " | + | |
| + | #### DNS von filter nach open freischalten (UDP und TCP) | ||
| + | #iif $filter oif $open udp dport 53 ct state new accept | ||
| + | #iif $filter oif $open tcp dport 53 ct state new accept | ||
| + | |||
| + | #### HTTP von filter nach open freischalten | ||
| + | #iif $filter oif $open tcp dport 80 ct state new accept | ||
| + | |||
| + | #### HTTPS von filter nach open freischalten | ||
| + | #iif $filter oif $open tcp dport 443 ct state new accept | ||
| + | |||
| + | #### DHCP: Clients auf der filter-Seite, Server auf der open-Seite | ||
| + | #iif $filter oif $open udp sport 68 udp dport 67 accept | ||
| + | #iif $open oif $filter udp sport 67 udp dport 68 accept | ||
| + | |||
| + | #### Alles von open nach filter freischalten | ||
| + | #iif $open oif $filter ct state new accept | ||
| + | |||
| + | # Abgelehnte Pakete loggen (sichtbar mit journalctl) | ||
| + | # begrenzt auf 10 Meldungen pro Sekunde | ||
| + | limit rate 10/second log prefix " nft-drop" | ||
} | } | ||
| + | } | ||
| + | </pre> | ||
| + | ;Hinweis zur Reihenfolge | ||
| + | *Die Regeln werden von oben nach unten abgearbeitet, das erste accept oder drop beendet die Prüfung. | ||
| + | *established,related steht ganz oben, weil die meisten Pakete zu bestehenden Verbindungen gehören - das spart Rechenzeit. | ||
| + | ;Hinweis zu ICMP | ||
| + | *Die ICMP-Regel matcht nur IPv4. IPv6 wird in diesem Szenario nicht betrachtet. | ||
| + | ;Hinweis zu DNS | ||
| + | *DNS nutzt normalerweise UDP. Bei großen Antworten (z.B. DNSSEC) weicht es auf TCP aus. Ohne die TCP-Regel schlagen solche Anfragen scheinbar zufällig fehl. | ||
| + | ;Hinweis zu DHCP | ||
| + | *Die DHCP-Regeln werden nur gebraucht, wenn Clients auf der gefilterten Seite ihre Adresse von einem Server auf der offenen Seite beziehen. | ||
| + | *Connection Tracking hilft hier nicht: Der Client fragt per Broadcast an, die Antwort des Servers passt daher zu keiner bekannten Verbindung. Deshalb werden beide Richtungen ohne ct state freigeschaltet. | ||
| + | ;Hinweis zu VLANs | ||
| + | *VLAN-getaggte Frames werden durch die Policy verworfen. Sollen getaggte Frames die Bridge passieren, sind eigene Regeln nötig. Das ist nicht Teil dieses Szenarios. | ||
| + | ;Hinweis zum Log | ||
| + | *Das führende Leerzeichen in <code>" nft-drop"</code> ist Absicht - es verbessert die Lesbarkeit im Kernel-Log. | ||
| + | *Die Begrenzung per limit verhindert, dass Broadcasts das Journal fluten. Pakete über dem Limit werden trotzdem verworfen, nur nicht mehr geloggt. | ||
| + | |||
| + | =Regel Handling= | ||
| + | ;Laden der Regeln | ||
| + | *nft -f /etc/nftables.conf | ||
| + | ;Kontrolle der Regeln | ||
| + | *nft list ruleset | ||
| + | ;Löschen der Regeln | ||
| + | *nft flush ruleset | ||
| + | ;Regeln beim Booten laden | ||
| + | *systemctl enable --now nftables | ||
| + | *Der Dienst lädt beim Start /etc/nftables.conf. | ||
| + | |||
| + | =Achtung: Fail-open= | ||
| + | *Eine Bridge ohne Regelwerk leitet '''alles''' weiter. Die Firewall ist dann ein einfaches Kabel. | ||
| + | *Das passiert: | ||
| + | **nach <code>nft flush ruleset</code> | ||
| + | **wenn der Dienst nftables nicht aktiviert ist | ||
| + | **wenn /etc/nftables.conf einen Syntaxfehler hat und nicht geladen wird | ||
| + | *Nach jeder Änderung daher mit <code>nft list ruleset</code> prüfen, ob das Regelwerk wirklich aktiv ist. | ||
| + | *Syntax vorher testen, ohne zu laden: | ||
| + | nft -c -f /etc/nftables.conf | ||
| + | |||
| + | =Live Log der Regel Verstöße= | ||
| + | *journalctl -f -t kernel -g "nft-drop" | ||
| + | |||
| + | =Verbindungen beobachten= | ||
| + | *apt install conntrack | ||
| + | ;Alle erfassten Verbindungen anzeigen | ||
| + | *conntrack -L | ||
| + | ;Neue Verbindungen live verfolgen | ||
| + | *conntrack -E | ||
| − | } | + | =Fehlersuche mit Trace= |
| + | *Mit nftrace lässt sich der Weg eines Pakets Regel für Regel verfolgen. | ||
| + | *Eine zusätzliche Chain markiert die interessierenden Pakete, hier ICMP: | ||
| + | nft add chain bridge filter trace '{ type filter hook prerouting priority 0; }' | ||
| + | nft add rule bridge filter trace ip protocol icmp meta nftrace set 1 | ||
| + | *In einem zweiten Terminal mitlesen: | ||
| + | nft monitor trace | ||
| + | *Die Ausgabe zeigt für jedes markierte Paket jede durchlaufene Regel und das Ergebnis (accept/drop). | ||
| + | *Zum Entfernen einfach das Regelwerk neu laden: | ||
| + | nft -f /etc/nftables.conf | ||
| + | =Übung= | ||
| + | *Regelwerk wie oben laden, alle mit #### markierten Regeln sind auskommentiert. | ||
| + | *Parallel den Live-Log laufen lassen. | ||
| + | ;Schritt 1 | ||
| + | *Von einem Client auf der gefilterten Seite das Gateway anpingen. Was passiert, was steht im Log? | ||
| + | *ICMP-Regel aktivieren, neu laden, erneut testen. | ||
| + | ;Schritt 2 | ||
| + | *Mit <code>dig</code> eine Namensauflösung testen, dann die DNS-Regeln aktivieren. | ||
| + | ;Schritt 3 | ||
| + | *Mit <code>curl</code> eine HTTP- und eine HTTPS-Seite abrufen, dann die Regeln für Port 80 und 443 aktivieren. | ||
| + | ;Schritt 4 | ||
| + | *Mit <code>conntrack -L</code> die entstandenen Verbindungen ansehen. | ||
| + | *Warum kommt die Antwort des Webservers durch, obwohl es keine Regel von open nach filter gibt? | ||
| + | ;Schritt 5 | ||
| + | *Die beiden ARP-Regeln auskommentieren und neu laden. Was passiert mit bestehenden und mit neuen Verbindungen? | ||
| + | *Tipp: <code>ip neigh flush all</code> auf dem Client leert den ARP-Cache. | ||
| + | ;Schritt 6 | ||
| + | *Den Dienst nftables stoppen bzw. <code>nft flush ruleset</code> ausführen und erneut testen. Was bedeutet das für den Betrieb? | ||
| − | + | =Weiterführende Artikel= | |
| + | *[[nftables schnellstart]] | ||
Aktuelle Version vom 1. Oktober 2026, 08:27 Uhr
Template
- debian-template klonen mit dem Namen bridgefw
Vorab
- Bei Virtualisierungen Bridge Ports auf Promisc setzen
- Das sind Adapater 2 und Adapater 3
- Dies erlaubt der virtuellen Netzwerkkarte, auch Pakete zu empfangen, die nicht an ihre MAC-Adresse gerichtet sind - notwendig für den Bridge-Betrieb.
- VirtualBox
- Unter "Erweitert" den Promiscuous Mode auf "Allow All" stellen (siehe Bild).
- Proxmox/KVM
- An der Linux-Bridge des Hosts ist nichts einzustellen.
- Ist die Proxmox-Firewall für die VM aktiv, muss dort der MAC-Filter ausgeschaltet werden, sonst werden fremde MAC-Adressen verworfen.
- In der VM selbst
- Die Bridge schaltet ihre Ports beim Hochfahren automatisch in den Promiscuous Mode, hier ist nichts zu tun.
Netzkonfiguration BRDIGEFW
| Parameter | Wert | Erläuterung |
|---|---|---|
| Netzwerk (NIC 1) | SERVER | Interface-Zuweisung in VirtualBox |
| Netzwerk (NIC 2) | DMZ | Interface-Zuweisung in VirtualBox (Promiscuous-Modus Allow all) |
| Netzwerk (NIC 3) | DMZ-OUT | Interface-Zuweisung in VirtualBox (Promiscuous-Modus Allow all) |
| IP | 10.2XX.1.9 | Statische IP |
| CIDR | 24 | Classless Inter-Domain Routing Präfixlänge |
| GW | 10.2XX.1.1 | GATEWAY |
| NS | 10.88.2XX.21 | Resolver |
| FQDN | bridgefw.it2XX.int | Fully Qualified Domain Name |
| SHORT | smb | Short Name |
| DOM | it2XX.int | Domain Name |
Setup
- debian-setup.sh -f bridgefw.it2XX.int -a 10.2XX.1.9/24 -n 10.88.2XX.21 -g 10.2XX.1.1
Auf dem Hostsystem
- vi ~/.ssh/config
Host bridgefw Hostname 10.2XX.1.9 User kit ProxyJump kit@fw
- Key übertragen
- ssh-copy-id bridgefw
- Einloggen
- ssh bridgefw
Was ist eine Layer 2 Firewall?
- Eine normale Firewall arbeitet auf Layer 3 (IP-Ebene) und benötigt eine IP-Adresse im Datenpfad.
- Eine Layer 2 Firewall greift dagegen auf Ethernet-Ebene (MAC) - sie kann transparent zwischen zwei Netzwerksegmente gestellt werden, ohne eine eigene IP-Adresse im Datenpfad zu benötigen.
- Der Datenverkehr läuft über eine Bridge, die nftables zur Filterung nutzt.
- Die Clients merken von der Firewall nichts: Gateway, Adressen und Subnetz bleiben unverändert.
Schaubild
Installation der Bridgeutils
- apt install bridge-utils
- Das Paket liefert die Skripte, mit denen ifupdown die Optionen bridge_ports, bridge_fd und bridge_stp auswertet.
/etc/network/interfaces
auto lo
iface lo inet loopback
auto enp0s3
iface enp0s3 inet static
address 10.2XX.1.9/24
gateway 10.2XX.1.1
dns-nameservers 10.88.2XX.21
dns-search it2XX.int
auto enp0s8
iface enp0s8 inet manual
auto enp0s9
iface enp0s9 inet manual
auto vmbr0
iface vmbr0 inet manual
bridge_ports enp0s8 enp0s9
bridge_fd 0
bridge_stp no
- Hinweise zur Konfiguration
- enp0s3
- Management-Interface mit eigener IP-Adresse. Es liegt nicht in der Bridge.
- vmbr0 inet manual
- Die Bridge bekommt bewusst keine IP-Adresse - sie arbeitet rein transparent.
- bridge_fd 0
- Forward Delay. Er wird nur bei aktivem STP genutzt: Die Ports durchlaufen dann die Zustände Listening und Learning, bevor sie Pakete weiterleiten.
- Ohne STP schaltet der Kernel die Ports sofort auf Forwarding. Der Wert 0 macht das explizit.
- bridge_stp no
- STP (Spanning Tree Protocol) ist deaktiviert. Da nur zwei Ports vorhanden sind, ist kein Loop möglich und STP nicht notwendig.
- Bridge starten
- ifup vmbr0
Kontrolle der Bridge
- Welche Ports gehören zur Bridge und in welchem Zustand sind sie?
- bridge link show
- Welche MAC-Adressen hat die Bridge an welchem Port gelernt? (FDB)
- bridge fdb show br vmbr0 | grep -v permanent
- Welche Interfaces gehören zu Bridgen
- brctl show
bridge name bridge id STP enabled interfaces vmbr0 8000.76e085f914d6 no enp0s8 enp0s9
Erste Maschine hinte die Bridge Firewall plazieren
- www -> Interface auf DMZ-OUT ändern
- Ping mal von www nach 1.1.1.1
- Kontrolle auf der bridgefw auf den Schnittstellen enp0s8 und enp0s9 per tcpdump
Bridge-Filterung
Arten von Bridge-Ketten
- Die Bridge-Familie bietet fünf Kettenarten (Hooks):
- prerouting
- Hier können Pakete vor der Forward-Datenbank (FDB) Entscheidung gefiltert werden.
- Die FDB liefert den Bridge-Port, der für eine bestimmte Ziel-MAC-Adresse verwendet wird.
- Wenn es noch keinen Eintrag in der FDB für eine gegebene Ziel-MAC gibt, werden die Pakete an alle Bridge-Ports geflutet.
- Nach der FDB-Entscheidung können Pakete entweder dem Eingangs- oder dem Weiterleitweg folgen.
- input
- Hier können Pakete gefiltert werden, die an den IP-Stack übergeben werden.
- Wenn die Bridge-Schnittstelle eine IP-Adresse hat und Pakete an diese IP-Adresse gehen, folgen die Pakete diesem Pfad (die Pakete verlassen die Bridge und treten in den IP-Stack ein).
- forward
- Diese Hook filtert Pakete, die von einem Bridge-Port eingehen und an einen anderen Bridge-Port geliefert werden (der Bridge-Weiterleitungspfad).
- Im vorliegenden Szenario wird ausschließlich diese Chain genutzt - die Bridge hat keine IP-Adresse im Datenpfad, es wird rein gebridged.
- output
- Hier können Pakete gefiltert werden, die vom IP-Stack kommen und deren Ziel ein Bridge-Port ist.
- postrouting
- Hier können Pakete gefiltert werden, die entweder vom IP-Stack kommen oder von einem anderen Bridge-Port weitergeleitet werden.
Bridge-Familie und br_netfilter
- Gebridgte Pakete laufen normalerweise nur durch die Ketten der Bridge-Familie, nicht durch ip/inet-Tabellen.
- Ist jedoch das Kernelmodul br_netfilter geladen (z.B. automatisch durch Docker), laufen sie zusätzlich durch die ip/inet-Hooks. Das führt zu schwer nachvollziehbaren Effekten.
- Kontrolle:
lsmod | grep br_netfilter
- Keine Ausgabe bedeutet: Modul nicht geladen, alles in Ordnung.
ARP
- ARP ist im Gegensatz zur Layer 3 Firewall nicht automatisch freigeschaltet und muss explizit erlaubt werden.
- Eine Layer 3 Firewall sieht ARP gar nicht, weil ARP kein IP-Protokoll ist. Die Bridge sieht dagegen jeden Ethernet-Frame.
- Ohne ARP-Freischaltung ist keine Kommunikation im Netz möglich, da MAC-Adressen nicht aufgelöst werden können.
- Beispiel zum Akzeptieren von ARP-Paketen:
nft add rule bridge filter forward ether type arp accept
Zustandsabhängige Filterung
- Die Bridge-Familie unterstützt Connection Tracking (Verbindungsverfolgung), ab Kernel 5.3.
- Durch Matching auf den Verbindungsstatus (
ct state) können bestehende Verbindungen automatisch durchgelassen werden, ohne jede Richtung explizit freizuschalten.
- Die wichtigsten Zustände
- new
- Erstes Paket einer Verbindung.
- established
- Paket gehört zu einer bereits bekannten Verbindung.
- related
- Paket gehört zu einer bekannten Verbindung, ist aber eine eigene (z.B. ICMP-Fehlermeldung zu einer TCP-Verbindung).
- Besonderheit in der Bridge
- Connection Tracking gibt es hier nur für IP. ARP wird nicht verfolgt und braucht deshalb eigene Regeln.
Bridge Firewall
Regeln
- Konzept
enp0s8ist die offene Seite (z.B. Richtung Internet/unsicheres Netz)enp0s9ist die gefilterte Seite (z.B. internes Netz)enp0s3ist das Management-Interface im vertrauenswürdigen LAN, es wird hier nicht gefiltert- Die Bridge selbst hat keine IP-Adresse und ist aus den gefilterten Netzen nicht erreichbar
- Policy ist
drop- alles was nicht explizit erlaubt ist, wird verworfen - cat /etc/nftables.conf
#!/usr/sbin/nft -f
define open = enp0s8
define filter = enp0s9
flush ruleset
table bridge filter {
chain forward {
type filter hook forward priority 0; policy drop;
# Bestehende Verbindungen durchlassen (Connection Tracking)
ct state established,related accept
# ARP freischalten (beide Richtungen notwendig)
iif $open ether type arp accept
iif $filter ether type arp accept
#### ICMP von filter nach open freischalten
#iif $filter oif $open ip protocol icmp ct state new accept
#### DNS von filter nach open freischalten (UDP und TCP)
#iif $filter oif $open udp dport 53 ct state new accept
#iif $filter oif $open tcp dport 53 ct state new accept
#### HTTP von filter nach open freischalten
#iif $filter oif $open tcp dport 80 ct state new accept
#### HTTPS von filter nach open freischalten
#iif $filter oif $open tcp dport 443 ct state new accept
#### DHCP: Clients auf der filter-Seite, Server auf der open-Seite
#iif $filter oif $open udp sport 68 udp dport 67 accept
#iif $open oif $filter udp sport 67 udp dport 68 accept
#### Alles von open nach filter freischalten
#iif $open oif $filter ct state new accept
# Abgelehnte Pakete loggen (sichtbar mit journalctl)
# begrenzt auf 10 Meldungen pro Sekunde
limit rate 10/second log prefix " nft-drop"
}
}
- Hinweis zur Reihenfolge
- Die Regeln werden von oben nach unten abgearbeitet, das erste accept oder drop beendet die Prüfung.
- established,related steht ganz oben, weil die meisten Pakete zu bestehenden Verbindungen gehören - das spart Rechenzeit.
- Hinweis zu ICMP
- Die ICMP-Regel matcht nur IPv4. IPv6 wird in diesem Szenario nicht betrachtet.
- Hinweis zu DNS
- DNS nutzt normalerweise UDP. Bei großen Antworten (z.B. DNSSEC) weicht es auf TCP aus. Ohne die TCP-Regel schlagen solche Anfragen scheinbar zufällig fehl.
- Hinweis zu DHCP
- Die DHCP-Regeln werden nur gebraucht, wenn Clients auf der gefilterten Seite ihre Adresse von einem Server auf der offenen Seite beziehen.
- Connection Tracking hilft hier nicht: Der Client fragt per Broadcast an, die Antwort des Servers passt daher zu keiner bekannten Verbindung. Deshalb werden beide Richtungen ohne ct state freigeschaltet.
- Hinweis zu VLANs
- VLAN-getaggte Frames werden durch die Policy verworfen. Sollen getaggte Frames die Bridge passieren, sind eigene Regeln nötig. Das ist nicht Teil dieses Szenarios.
- Hinweis zum Log
- Das führende Leerzeichen in
" nft-drop"ist Absicht - es verbessert die Lesbarkeit im Kernel-Log. - Die Begrenzung per limit verhindert, dass Broadcasts das Journal fluten. Pakete über dem Limit werden trotzdem verworfen, nur nicht mehr geloggt.
Regel Handling
- Laden der Regeln
- nft -f /etc/nftables.conf
- Kontrolle der Regeln
- nft list ruleset
- Löschen der Regeln
- nft flush ruleset
- Regeln beim Booten laden
- systemctl enable --now nftables
- Der Dienst lädt beim Start /etc/nftables.conf.
Achtung: Fail-open
- Eine Bridge ohne Regelwerk leitet alles weiter. Die Firewall ist dann ein einfaches Kabel.
- Das passiert:
- nach
nft flush ruleset - wenn der Dienst nftables nicht aktiviert ist
- wenn /etc/nftables.conf einen Syntaxfehler hat und nicht geladen wird
- nach
- Nach jeder Änderung daher mit
nft list rulesetprüfen, ob das Regelwerk wirklich aktiv ist. - Syntax vorher testen, ohne zu laden:
nft -c -f /etc/nftables.conf
Live Log der Regel Verstöße
- journalctl -f -t kernel -g "nft-drop"
Verbindungen beobachten
- apt install conntrack
- Alle erfassten Verbindungen anzeigen
- conntrack -L
- Neue Verbindungen live verfolgen
- conntrack -E
Fehlersuche mit Trace
- Mit nftrace lässt sich der Weg eines Pakets Regel für Regel verfolgen.
- Eine zusätzliche Chain markiert die interessierenden Pakete, hier ICMP:
nft add chain bridge filter trace '{ type filter hook prerouting priority 0; }'
nft add rule bridge filter trace ip protocol icmp meta nftrace set 1
- In einem zweiten Terminal mitlesen:
nft monitor trace
- Die Ausgabe zeigt für jedes markierte Paket jede durchlaufene Regel und das Ergebnis (accept/drop).
- Zum Entfernen einfach das Regelwerk neu laden:
nft -f /etc/nftables.conf
Übung
- Regelwerk wie oben laden, alle mit #### markierten Regeln sind auskommentiert.
- Parallel den Live-Log laufen lassen.
- Schritt 1
- Von einem Client auf der gefilterten Seite das Gateway anpingen. Was passiert, was steht im Log?
- ICMP-Regel aktivieren, neu laden, erneut testen.
- Schritt 2
- Mit
digeine Namensauflösung testen, dann die DNS-Regeln aktivieren.
- Schritt 3
- Mit
curleine HTTP- und eine HTTPS-Seite abrufen, dann die Regeln für Port 80 und 443 aktivieren.
- Schritt 4
- Mit
conntrack -Ldie entstandenen Verbindungen ansehen. - Warum kommt die Antwort des Webservers durch, obwohl es keine Regel von open nach filter gibt?
- Schritt 5
- Die beiden ARP-Regeln auskommentieren und neu laden. Was passiert mit bestehenden und mit neuen Verbindungen?
- Tipp:
ip neigh flush allauf dem Client leert den ARP-Cache.
- Schritt 6
- Den Dienst nftables stoppen bzw.
nft flush rulesetausführen und erneut testen. Was bedeutet das für den Betrieb?


