nftwall ist eine auf nftables basierenede hochflexible Firewall Lösung. Die Konfiguration geschieht über Ansible was verschiedene Vorteile bietet:
nftwall unterstützt High Availability setups. Ein Active - Passive setup ist möglich mit zwei Firewalls, die eine erhöhte Betriebssicherheit bieten als eine einzelne Firewall. Die Synchronisation der Firewall Einstellungen geschieht über Ansible und somit dadurch synchron. Die Firewall States wird über den conntrackd einheitlich gehalten.
Auf IP Ebene wird VRRP eingesetzt um in jedem Subnetz eine zusätzliche, virtuelle IP zu erhalten, die zwischen den Firewall Lösungen hin- und her geschoben wird, so dass auch bei Ausfall des Primary Knoten der Secondary instantan übernehmen kann.
Derzeit befindet sich die nftwall Lösung noch in Entwicklung und sollte NICHT in produktiven Umgebungen eingesetzt werden.
Da nftwall auf Ansible basiert sollte Kenntnis über Ansible vorhanden sein. Diese Dokumentation hat nicht den Anspruch die Ansible Dokumentation zu ersetzen.
In einer nftwall Umgebung gibt es die eigentlich Firewall Systeme auf denen nftwall ausgeführt wird, dort wo die Firewall ihre Tätigkeit übernimmt. Diese Systeme nennen wir:
Außerdem muss es noch ein System geben wo die Ansible Konfiguration mitsamt der Rollen und Playbooks verwaltet wird. Diese Systeme nennen wir:
Für den Betrieb einer nftwall Firewall wird ein Alpine Linux System vorausgesetzt. Alpine Linux ist ein sehr schlankes Linux, das auf diversen Plattformen lauffähig ist entweder Bare Metal oder virtualisiert. Neben dem System für die Firewall selbst wird ein Ansible Host benötigt. Im Idealfall existiert bereits ein Ansible Controller, ansonsten müsste man ein solches System noch aufsetzen. Dazu bitte die vorhandenen Dokumentationen bei Ansible nutzen. Den Ansible Controller auf der Firewall selbst laufen zu lassen ist durchaus möglich, aber nicht best practice.
apk install ansible
cd AnsibleArbeitsverzeichnis
ansible-galaxy collection install
https://github.com/imp1sh/ansible_nftwallcollection.git
-p ./collections
ansible-galaxy collection install
https://github.com/imp1sh/ansible_nftwallcollection.git
cd AnsibleArbeitsverzeichnis
ansible-galaxy collection install https://github.com/imp1sh/ansible_managemynetwork.git -p ./collections
ansible-galaxy collection install https://github.com/imp1sh/ansible_managemynetwork.git
Best Practice ist die Collections in den Unterordner collections
zu installieren. Die Rollen werden auch auf Ansible Galaxy veröffentlicht, allerdings wird dringend empfohlen die Repos von Github zu verwenden, da sie dort immer aktueller sind.
Die Ansible Collection ansible_nftwallcollection beinhaltet mehrere Rollen die für den Betrieb von nftwall notwendig sind.
Diese Rolle wird nur in HA (High Availability) Umgebungen genutzt. Sie dient dazu die Connection States zwischen den Clusterteilnehmern zu synchronisieren. Sollte der Primary / Secondary Status wechseln stehen auch dort die Sessions zur Verfügung, was zur Folge hat, dass bereits geöffnete Verbindung weiterhin funktionieren. Ansonsten würde in so einem Fall eine offene SSH Verbindung einfach abbrechen und in einen Timeout laufen.
dnsmasq ist eine sehr vielseitige Software die neben DHCP auch Router Advertisement beherscht und auch nebenbei als resolving oder authoritativer Nameserver agieren kann. In HA Umgebungen kann dnsmasq jedoch nicht zum Einsatz kommen, unter Anderem weil die radvd bekannte Funktion RemoveRoute nicht vorhanden ist.
Diese Rolle wird zum Zwecke von nftwall nicht weiterentwickelt, da dnsmasq nicht den Ansprüchen für eine HA Umgebung gerecht wird.
Statt ansible_dnsmasq kommt ansible_kea und ansible_radvd zum Einsatz. Ansible_key findet sich noch in der Entwicklung.
Keepalived ist ein Wrapper für Linux Virtual Server (LVS). Es ist ein Layer 4 Load Balancer, der nebenbei auch das Verwalten von VRRP Adressen anbietet. In diesem Zusammenhang ist derzeit nur geplant die VRRP Funktionalität dieser Lösung zu nutzen. Eventuell könnten man später noch drüber nachdenken die Load Balancing Funktion dieser Software zu nutzen, sollte man planen hochverfügbare Dienste hinter der Firewall anzubieten.
Auch hier gilt, dass meine nftwall Doku nicht die keepalived Dokumenation / LVS Dokumentation ersetzt.
So sieht eine Beispielkonfiguration für VRRP Adressen aus.
kad_vrrpinstances:
- name: "WAN"
master: "alpine1.demo.junicast.de"
slave: "alpine2.demo.junicast.de"
masterif: "eth0"
slaveif: "eth0"
routerid: 51
priorityanchor: 100
advertint: 1
authpass: "sdu39sj3"
vips:
- 192.168.200.4
- name: "WAN6"
master: "alpine1.demo.junicast.de"
slave: "alpine2.demo.junicast.de"
masterif: "eth0"
slaveif: "eth0"
routerid: 52
priorityanchor: 100
advertint: 1
authpass: "Hsdf§$l3"
vips:
- 2001:470:7e68::4
Eine virtuelle IP wird zwischen zwei Systemen geteilt die auf dem Interface (hier WAN) sich in der gleichen Broadcast Domäne befinden. Neben den IP-Adressen die die beiden hosts selber haben, gibt es eine virtuelle IP-Adresse, die zu einem bestimmten Zeitpunkt immer nur auf einem der beiden Systeme aktiv ist. Diese IP-Adresse steht immer zur Verfügung, auch wenn einer der beiden Systeme ausfallen sollte. Diese IP Adresse kann so als Default Gateway oder auch andere Dinge verwendet werden.
Es ist bei Bedarf auch möglich mehrere virtuelle IPs zu konfigurrieren, dazu weitere Einträge unter vips
anlegen.
Die netconfig Rolle sorgt für die ordentliche Einrichtung der Netzwerkschnittstellen. Man kann zwischen unterschiedlichen Modi wählen: Derzeit sind nur wenige Modi für Alpine Linux unterstützt.
Beispiel für eine host_vars Definition:
netinterfaces:
vmbr0:
comment: "Main Interface auf mynet"
type: "ethernet"
method4: "static"
method6: "static"
ip4: "10.10.129.4/26"
ip6: "2001:affe:dead:7300:21b:21ff:fec1:a8c0/64"
gw4: "{{ nftvars['nets']['mynet']['gw4'] }}"
gw6: "{{ nftvars['nets']['mynet']['gw6'] }}"
Bei gw4 und gw6 kann man auch direkt die IP eintragen, damit ich pro Netz das Default Gateway nur einmal definieren muss, lege ich es in group_vars/allhosts.yml ab, etwa so:
nftvars:
nets:
mynet:
comment: "Das ist mein Netz"
ip4: "10.123.125.0/24"
ip6: "21a0:1234:1312:1::/64
gw4: "10.123.125.1"
gw6: "21a0:1234:1312:1::1/64
Die nftables Rolle stellt das Herzstück von nftwall dar. Da iptables bei Linux ausgedient hat bot sich der Nachfolger namens nftables für die Firewallregeln an. Bei modernen Linux Systemen kommt diese Technik bereits zum Einsatz, allerdings meistens unter zu Hilfenahme von Wrappern wie firewalld. Diese Programme sollen einem die Arbeit eigentlich erleichtern, setzen jedoch Spezialwissen voraus und sind oft umständlich zu bedienen.
Bei nftables arbeitet man mit von iptables gewohnten Konzepten. Um einen Einblick in nftables zu erhalten empfehle ich die netfilter Homepage.
Die nftables Rolle abstrahiert auf einfache Weise das Pflegen von Firewall Regeln. Eingehende Verbindungen vom WAN Interface werden standardmäßig abgewiesen. In den Standardeinstellungen werden nur diese Verbindungen zugelassen:
Sollte man mehrere lokale Schnittstellen haben ist auch in den Standardeinstellungen der Traffic zwischen diesen blockiert. Zugriff für die lokalen Teilnehmer auf das Internet schaltet man über die Benutzung dieser Ansible Listen-Variable frei:
nftables_masks:
- upif: "eth0"
downif: "eth1"
Diese Variablendefinition in Ansible sorgt dafür, dass Pakete aus eth1 mit der IP Adresse von eth0 maskiert werden. eth0 ist hier das upstream Interface in das Internet.
Solange keine Ziel und keine Quelladresse angegeben wird, kann man Regeln der family 46 definieren, die für IPv4 UND IPv6 gelten. Sobald man IP-Adressen mit einbaut, muss man aufteilen in separate Regeln jeweils für IPv4 und IPv6.
Hin und wieder möchte man auch auf einer Firewall eingehende Pakete erlauben die nicht für eine Weiterleitung, sondern für die Firewall selbst bestimmt sind. Im Modus Server oder Desktop ist diese Option wesentlich häufiger wichtig als auf einer Firewall selbst, die meistens keine eingehenden Pakete erwartet. In Ansible gibt es die Host- und die Gruppenebene auf denen man eingehende Verbindungen freischalten kann. Soll eine Freischaltung nur für einen bestimmten Host durchgeführt werden, so legt man diese in Ansible unter den host_vars an:
nftablesopenhost:
- dport: "http"
family: 6
comment: "Allow HTTP from specific IPv6 address only"
proto: "tcp"
saddr: " 2a00:f30:1234::1/64 "
Möchte man hingegen eine Regel auf mehreren hosts ausrollen, so kann man dies über die Gruppenzugehörigkeit in Ansible regeln. Man definiert in group_vars/all.yml:
nftablesopengroup:
gruppenname:
- dport: 53
inif: "eth1"
family: 46
proto: "tcpudp"
comment: "DNS Allow on eth0"
Destination NAT benötigt man in der Regeln wenn man eingehende Anfragen aus dem Internet auf lokale Systeme zulassen möchte. Da oft nur die Firewall selbst über eine öffentliche IPv4 Adresse verfügt muss man NAT einsetzen. Möchte man zum Beispiel die Anfragen auf Port 801 auf die lokale IP weiterleiten, nutzt man die Ansible Variable:
nftables_dnat:
- dport: 801
ip: "172.16.88.16"
proto: "tcpudp"
inif: "eth0"
outif: "eth1"
comment: "Forward Port 801 to host 17"
Wenn man den Luxus hat und öffentliche Adressen seinen Teilnehmern geben kann, möchte man gerne auf NAT verzichten und legt Firewallregeln an, die bestimmten Traffic erlauben sollen. Dazu verwendet man die nftables_forward Variable in Ansible:
nftables_forward:
- inif: "eth0"
daddr: "2001:aaaa:123:1000::/64"
saddr: "2001:faa:b33:1::/64"
dport: "22"
proto: "tcpudp"
family: 6
outif: "eth1"
comment: "Allow SSH to client3 from net1"
Die meisten Parameter der Variable sind optional, so kann man auch z.B. jeglichen Traffic zwischen zwei eth1 und eth2 erlauben:
- inif: "eth1"
outif: "eth2"
family: 46
comment: "Allow all eth1 to eth2"
- inif: "eth2"
outif: "eth1"
family: 46
comment: "Allow all eth2 to eth1"
Bei family hat man die Wahl zwischen 4, 6 oder 46. Bei dem Parameter proto kann man wählen zwischen tcp, udp und tcpudp.
Ansible bietet den eingebauten Vorzug Variablen verwenden zu können. So bietet es sich dringend an diese auch für die Verwaltung der Firewallregeln einzusetzen. So kann man beispielsweise die IP-Adresse eines Webserver als Variable definieren um diese in Regeln zu verwenden. Ändert sich einmal die IP-Adresse des Webservers so braucht man diese Adresse nicht an zig Stellen in Ansible zu korrigieren, sondern nur an einer Stelle. Bei den Variablen empfiehlt es sich unbedingt eine einheitlich Benamung zu verwenden, damit man nicht immer wieder erst nachsehen muss, wie die Variable nochmal hieß.
Dies ist ein Beispiel, wie man sich Variablen zu Nutze machen kann. (group_vars/all.yml)
nftvars:
hosts:
jabber server:
ip6: "2a00:fe1:fff:6::5/64"
ip4: “10.11.12.13/24”
comment: "chat.mydomain.de jabber server"
nets:
home:
ip4: "10.10.128.0/20"
ip6: "2a10:f30:3f::/48"
comment: "Home network"
office:
ip4: “192.168.12.0/24”
ip6: “21a0:af12::/32”
comment: “Mein besonders grosses Bueronetz”
ports:
ssh:
value: "22"
proto: "tcp"
comment: "SSH service default port"
Zur Anwendung kommen die Variablen zum Beispiel so:
nftablesopenhost:
- dport: "80"
family: 6
comment: "Allow HTTP from Office prefix"
proto: "tcp"
saddr: "{{ nftvars['nets']['office']['ip6'] }}"
nftables beherscht nativ die Fähigkeit anstatt einer Portnummer einfach den Bezeichner aus /etc/services
anzugeben. Deswegen ist es in nftwall auch legitim anstatt einer Portnummer eine Bezeichnung aus dieser Datei zu verwenden:
nftablesopenhost:
- dport: "http"
family: 6
comment: "Allow HTTP from Office prefix"
proto: "tcp"
saddr: "{{ nftvars['nets']['N6_office']['ip6'] }}"
Die Rolle ansible_sysctl sorgt dafür, dass die entsprechenden Kernelparameter zur Weiterleitung von IPv4 und IPv6 Paketen ermöglicht wird. Ohne diese Änderungen wäre ein Firewallbetrieb nich möglich. Diese Rolle ist ziemlich unflexibel und setzt keine Sysctl Parameter anch Wunsch sondern so vordefiniert, wie wir es für den Betrieb einer Firewall für notwendig halten.