Ein zweistufiges Firewall-Setup mit Linux, BSD und Ansible

Sobald Dienste im Internet zur Verfügung gestellt werden sollen, empfiehlt sich der Aufbau eines zweistufigen Firewall-Setups. Mögliche Realisierungen finden sich z.B. unter https://de.wikipedia.org/wiki/Demilitarisierte_Zone_(Informatik).

Zum Beispiel soll ein SMTP- oder DNS-Server mit dem Internet und dem LAN Daten austauschen können, ohne dass eine Verbindung zwischen Internet und LAN besteht.

Da jedes System von Zeit zu Zeit Sicherheitslücken aufweist, verwenden wir beim Aufbau der DMZ-Systeme zwei verschiedene Betriebssysteme auf den inneren und äusseren Firewalls. Wir haben sehr gute  Erfahrungen mit der Verwendung von Linux und OpenBSD gemacht. Dadurch sinkt die Wahrscheinlichkeit, dass eine neu entdeckte Sicherheitslücke den ungewollten Zugriff auf LAN-Systeme ermöglicht.

Durch die Formulierung der erwünschten Datenflüsse als Datenstrukturen unter Ansible ist es einfach möglich, mehrere Systeme gleichzeitig zu konfigurieren. So können gleichzeitig die innere und die äussere  Firewall sowie die lokale Firewall und das Setup des Mailservers konfiguriert werden, was die Fehlerquellen (z.B. vergessene Abhängigkeiten) deutlich minimiert.

Dieses Vorgehen ist einer manuellen Konfiguration in jedem Fall vorzuziehen, auch, weil durch das Versionieren des Ansible-Setups durch Git jederzeit nachzuvollziehen ist, wann welche Änderungen vorgenommen wurden.

Gleichzeitig ist es durch die Formulierung als Ansible-Datenstrukturen mit Hilfe neuer Templates bzw. Playbooks einfach möglich, die verwendete Firewall-Technik auszutauschen oder zu erneuern.

Die Migration etwa von iptables zu nftables, BPF (Berkeley Packet Filter) bzw. zu neuen Firewallsystemen wird durch Ansible extrem erleichtert. Auch proprietäre Firewalls lassen sich durch Ansible konfigurieren,  eine Übersicht der vorhandenen Module findet sich z.B. unter https://docs.ansible.com/ansible/2.9/modules/list_of_network_modules.html. Ein Überblick über Ansible zur Netzwerkautomatisierung ist z.B. https://docs.ansible.com/ansible/latest/network/index.html.

Bei nftables unter Linux und pf unter BSD ist es möglich, vor dem Aktivieren einer Konfigurationsänderung eine Syntax-Prüfung durch Ansible durchzuführen, so dass keine syntaktisch ungültige Konfiguration  aktiviert wird. Dazu dient der Parameter ‚validate‘ des template-Moduls.

Beispiel: VPN-Verbindungen von Niederlassungen in die Firmenzentrale

Das folgende vereinfachte Beispiel entspricht Setups, die wir für unsere Kunden entwickelt haben. Es soll der eingeschränkte VPN-Zugriff von Niederlassungen auf Server im LAN der Firmenzentrale erlaubt  werden. Durch Ansible werden automatisch die äusseren Firewalls, die VPN-Server in der DMZ sowie die inneren Firewalls konfiguriert.

Die verwendete Directory-Struktur richtet sich nach den Best-Practices unter https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html#directory-layout

Die Definition der Datenstrukturen kann in einer Datei unterhalb von group_vars/ angelegt werden. Aus Gründen der Übersichtlichkeit werden hier nicht alle Definitionen aufgeführt.

fw_vpn_access:
  # Zugriff auf Exchange-Server über VPN nur von Niederlassungen aus Deutschland:
  - source: germany_vpn_clients
    dest: germany_vpn_servers
    proto: udp
    port: 12345
    rules:
    - source: germany_branch_offices
      dest: lan_exchange_servers
      application: exchange

  # Zugriff auf DNS- und NTP-Server über VPN von allen Niederlassungen:
  - source: global_vpn_clients
    dest: global_vpn_servers
    proto: udp
    port: 12346
    rules:
    - source: global_branch_offices
      dest: dmz_dns_servers
      application: dns
    - source: global_branch_offices
      dest: dmz_ntp_servers
      application: ntp

Je nach Implementierung können die oben benannten Systeme wie etwa global_vpn_clients Mitglieder einer im Inventory definierten Host-Gruppe sein, z.B. in einer Datei „hosts“:

[global_vpn_clients]
hamburg_vpn_client.example.com
hannover_vpn_client.example.com

[global_vpn_servers]
dmz_global_vpn_1.example.com
dmz_global_vpn_2.example.com

Mit Hilfe von „magischen“ Variablen ist es möglich, auf Daten anderer Hosts zuzugreifen. Z.B. kann ein template für die Firewall-Konfiguration je nach verwendeter Firewall-Software konfiguriert werden.

Ein stark vereinfachtes Beispiel der Verwendung der obigen Datenstrukturen für Linux nftables; durch Verwendung des Ansible-Moduls ‚template‘ wird die Datei /etc/nftables.conf erzeugt:

{% for access in fw_vpn_access %}
{% for host in groups[access.source] %}
  ip saddr {{ host.wan_address }} ip daddr {{ access.dest }} {{ access.proto }} dport {{ access.port }}
{% endfor %}
{% endfor %}

Beispiele für den Zugriff auf Informationen, die zu anderen Systemen gehören, findet sich unter https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html#accessing-information-about-other-hosts-with-magic-variables.

Die beteiligten Protokolle bzw. Anwendungen wie exchange oder DNS werden ebenfalls durch Variablen definiert.

Fazit

Ansible bietet immense Vorteile, wenn komplexe und fehleranfällige Konfigurationen vorgenommen werden sollen. Bei entsprechendem Design der Datenstrukturen und Templates lassen sich mit minimalem  Konfigurationsaufwand alle beteiligten Systeme quasi-gleichzeitig aktualisieren.

Ansible erlaubt es, die verwendeten Softwarekonfigurationen, Techniken bzw. Systeme einfach zu erneuern, zu erweitern oder auszutauschen.

Durch die Verwendung von „validate“ im template-Modul ist es möglich, vor dem Aktivieren der Änderungen Syntaxprüfungen und andere Tests durchzuführen. Zusammen mit den Testmöglichkeiten z.B. mittels  Molecule (siehe unser Blogbeitrag) gewinnt die Arbeit an komplexen Firewall-Setups an Sicherheit und Nachvollziehbarkeit.

Automatisierung der Netzwerksicherheit mit Ansible