Netzwerksicherheit Linuxhotel Juni 2022 (Debian)

1 Informationen zum Kurs

2 Unsere Lab-Umgebung für die Schulung - Forwarding und Routing unter Linux

  • Jeder Teilnehmer bekommt drei virtuelle Maschinen mit Debian 10 vorinstalliert (minimale Installation)
+--------+                    +----------------+
| Client |                    |     Server     |
| client-|                    |                |
|  eth0  |                    |  server-eth0   |
+---+----+                    +--------+-------+
    |              Router              |
    |             +------+             |
    | router1-eth0|      | router2-eth0|
    +-------------+      +-------------+
	      	  +---+--+
		      |router3-eth0 (Internet)
  • Benutzer: user, Password: netzwerk-2022
  • Root-Shell per su - und dem Passwort netzwerk-2022
  • IP Adressen der virtuellen Maschinen:
# Name IP URL
1 Dimitri 2a03:b0c0:2:d0::10a3:6001 https://netzwerk-001.defaultroutes.org/
2   2a03:b0c0:2:d0::fb9:a001 https://netzwerk-002.defaultroutes.org/
3 Andre 2a03:b0c0:2:d0::102e:e001 https://netzwerk-003.defaultroutes.org/
4 Jan W 2a03:b0c0:2:d0::145a:a001 https://netzwerk-004.defaultroutes.org/
5 Thomas 2a03:b0c0:2:d0::121b:4001 https://netzwerk-005.defaultroutes.org/
6 Jan C 2a03:b0c0:2:d0::fda:3001 https://netzwerk-006.defaultroutes.org/
7 Martin 2a03:b0c0:2:d0::1584:f001 https://netzwerk-007.defaultroutes.org/
8 Christian 2a03:b0c0:2:d0::1580:8001 https://netzwerk-008.defaultroutes.org/
9 Chris 2a03:b0c0:2:d0::1586:8001 https://netzwerk-009.defaultroutes.org/
10   2a03:b0c0:2:d0::1586:f001 https://netzwerk-010.defaultroutes.org/
11 Alexander 2a03:b0c0:2:d0::140b:3001 https://netzwerk-011.defaultroutes.org/
12 Carsten Strotmann 2a03:b0c0:2:d0::1589:7001 https://netzwerk-012.defaultroutes.org/

2.1 Editoren auf den virtuellen Maschinen

  • emacs
  • vi (vim)
  • mg (Micro Emacs)
  • jove
  • nano
  • joe
  • weitere Editoren können/dürfen nachinstalliert werden

3 Container auf den virtuellen Maschinen starten

  • Auf der virtuellen Maschine einloggen
  • Per sudo eine Root-Shell öffnen
  • Die Container starten
    % cd /root/lab
    % bash ./run
    
  • Beim ersten Start der Container sind zwei Fehlermeldungen zu erwarten. Bitte ignorieren.
  • Mit dem Befehl enter <containername> wird eine Shell in dem Container Namensraum gestartet

4 TMUX - terminal multiplexer

  • Beispiel: tmux auf dem Router/Server/Client starten
tmux
Aktion Tastaturkombination
Neues Terminal CTRL+b c
nächstes Terminal CTRL+b n
voheriges Terminal CTRL+b p
Terminal horizontal teilen CTRL+b "
Terminal vertikal teilen CTRL+b %
zwischen geteilten Terminals wechseln CTRL+b <cursor>
zwischen geteilten Terminals wechseln CTRL+b o
Terminal schliessen CTRL+b x
Tmux abhängen (detach) CTRL+b d
Tmux anhängen tmux attach
TMUX Kommandozeile CTRL+b :
Scrollen (mit Cursor-Tasten, Abbrechen mit CTRL+C) CTRL+[
Tastenkommandos in alle Fenster (Kommandozeile) set synchronize-panes

5 Einrichtung von FRR auf dem Router

  • Zeit: 20 Minuten
  • Wir arbeiten auf dem Router-Container
  • Empfehlung: tmux starten und in tmux arbeiten
  • Auf dem Router Server den Hostnamen ändern (als root)
echo "routerXX" > /etc/hostname
hostname routerXX
  • Um den neuen Hostnamen zu sehen mit bash eine neue Shell starten
  • Die Routing-Software frr ist schon vorinstalliert und gestartet
# systemctl status frr
● frr.service - FRRouting
   Loaded: loaded (/lib/systemd/system/frr.service; enabled; vendor preset: enabled)
   Active: active (running) since Sun 2022-06-19 08:36:38 UTC; 7min ago
     Docs: https://frrouting.readthedocs.io/en/latest/setup.html
  Process: 29 ExecStart=/usr/lib/frr/frrinit.sh start (code=exited, status=0/SUCCESS)
    Tasks: 4 (limit: 307)
   Memory: 3.8M
      CPU: 370ms
   CGroup: /system.slice/frr.service
           ├─44 /usr/lib/frr/watchfrr -d zebra staticd
           ├─58 /usr/lib/frr/zebra -d -A 127.0.0.1 -s 90000000
           └─61 /usr/lib/frr/staticd -d -A 127.0.0.1

Jun 19 08:36:37 29dcfb999f4d watchfrr[44]: staticd state -> down : initial connection attempt failed
Jun 19 08:36:37 29dcfb999f4d watchfrr[44]: Forked background command [pid 46]: /usr/lib/frr/watchfrr.sh res
tart all
Jun 19 08:36:37 29dcfb999f4d watchfrr.sh[56]: Cannot stop zebra: pid file not found
Jun 19 08:36:37 29dcfb999f4d watchfrr.sh[54]: Cannot stop staticd: pid file not found
Jun 19 08:36:37 29dcfb999f4d zebra[58]: client 14 says hello and bids fair to announce only static routes v
rf=0
Jun 19 08:36:38 29dcfb999f4d watchfrr[44]: zebra state -> up : connect succeeded
Jun 19 08:36:38 29dcfb999f4d watchfrr[44]: staticd state -> up : connect succeeded
Jun 19 08:36:38 29dcfb999f4d watchfrr[44]: all daemons up, doing startup-complete notify
Jun 19 08:36:38 29dcfb999f4d frrinit.sh[29]: Started watchfrr.
Jun 19 08:36:38 29dcfb999f4d systemd[1]: Started FRRouting.
  • IPv6 und IPv4 Konfiguration der Netzwerkschnittstellen router1-eth0 (Client-Netz) und router2-eth0 (Server-Netz) von der FRR Kommanozeile (auf dem Router)
vtysh
router0X#
router0x# conf terminal
router0x(config)# interface router1-eth0
router0x(config-if)# ipv6 address fd01::1/64
router0x(config-if)# ipv6 nd prefix fd01::/64 900 300
router0x(config-if)# no ipv6 nd suppress-ra
router0x(config-if)# no shutdown
router0x(config-if)# exit
router0x(config)# interface router2-eth0
router0x(config-if)# ipv6 address 2001:db8:1::1/64
router0x(config-if)# ipv6 nd prefix 2001:db8:1::/64 900 300
router0x(config-if)# no ipv6 nd suppress-ra
router0x(config-if)# no shutdown
router0x(config-if)# exit
router0x(config)# exit
router0x# write
router0x# conf terminal
router0x(config)# interface router1-eth0
router0x(config-if)# ip address 172.16.1.1/24
router0x(config-if)# exit
router0x(config)# interface router2-eth0
router0x(config-if)# ip address 100.64.1.1/24
router0x(config-if)# exit
router0x(config)# exit
router0x# write
router0x# exit
  • Von der virtuellen Maschine mittels enter client und enter server mit dem Client Container und dem Server Container verbinden und dort die Netzwerkkonfiguration mittels ip address show anzeigen lassen.
    • Auf dem Client und dem Server sollten schon IPv6 Adressen konfiguriert sein (via Router-Advertisements), jedoch keine IPv4 Adressen (welche wir in den nächsten Schritten manuell konfigurieren)

5.1 Zugang vom Router zum Server und zum Client

  • Zeit: 15 Minuten

5.1.1 IP Konfiguration des Server Containers

  • Manuelle IPv4 Konfiguration auf dem Server
ip addr add 100.64.1.2/24 dev server-eth0
ip route add default via 100.64.1.1
  • IP Konfiguration fest einstellen in der Datei /etc/network/interfaces
auto server-eth0
iface server-eth0 inet static
        address 100.64.1.2
        netmask 255.255.255.0
        gateway 100.64.1.1

5.1.2 IP Konfiguration des Client Containers

  • Manuelle IPv4 Konfiguration auf dem Client
ip addr add 172.16.1.2/24 dev client-eth0
ip route add default via 172.16.1.1
  • IP Konfiguration fest einstellen in der Datei /etc/network/interfaces
auto client-eth0
iface client-eth0 inet static
        address 172.16.1.2
        netmask 255.255.255.0
        gateway 172.16.1.1

5.1.3 Testen der Verbindungen

  • vom Server zum Router (auf dem Server):
ping 100.64.1.1
ping 172.16.1.1
ping6 2001:db8:1::1
ping6 fd01::1
  • vom Client zum Router (auf dem Client):
ping 172.16.1.1
ping 100.64.1.1
ping6 fd01::1
ping6 2001:db8:1::1
  • Geht ein 'ping' vom Server zum Client?

5.2 Routing (Forwarding)

  • Zeit: 5 Minuten
  • Geht ein 'ping6' vom Server zum Client?
  • Antwort: ohne IPv6-Forwarding - nein
  • Forwarding anschalten (in der FRR Shell auf dem Router)
vtysh
router0x# conf terminal
router0x(config)# ip forwarding
router0x(config)# ipv6 forwarding
router0x(config)# exit
route0x# write
  • ping6 vom Client zum Server testen, sollte nun funktionieren

5.3 Verbindung zum Internet

  • Zeit: 15 Minuten
  • Funktioniert ein 'ping' von Router ins Internet (1.0.0.1)? (auf dem Router ausführen)
ping 1.1
  • Lösung:
  • Per FRR die Adresse 192.168.1.1/24 auf die Netzwerkschnittstelle router3-eth0 konfigurieren
  • Auf der virtuellen Maschine die IP-Adresse 192.168.1.2/24 auf die Netzwerkschnittstelle internet-eth0 konfigurieren
  • Im Container router die Default-Route auf 192.168.1.2 (IP Adresse der Schnittstelle internet-eth0 auf der virtuellen Maschine) setzen
  • Auf der virtuellen Maschine (nicht in einem der Container) IPv4 Forwarding einschalten
    • Temporär
      % sysctl -w net.ipv4.ip_forward=1
      
    • Permanent in der Datei /etc/sysctl.conf
      [...]
      net.ipv4.ip_forward=1
      
  • iptables Masquerading NAT (auch Cone-NAT oder NATPT/NAT-Port-Translation oder NAT44/NAT-IPv4-zu-IPv4 genannt). Wir besprechen die NAT-Arten in dem späteren Kapitel über die Linux Firewall Systeme
  • Auf der virtuellen Maschine in einer BASH-Shell
iptables -A POSTROUTING -t nat -o eth0 -j MASQUERADE
  • Funktioniert ein 'ping' vom Server oder Client ins Internet (1.0.0.1)? (auf dem Server oder dem Client ausführen)
ping 1.1
  • Im Router-Container in einer BASH-Shell
iptables -A POSTROUTING -t nat -o router3-eth0 -j MASQUERADE
  • Nach dem Eintragen des NAT nochmals Testen, ob ein 'ping' vom Client und Server in das Internet funktioniert

5.3.1 Lösung:

  • IPv4 Adresse auf der WAN Schnittstelle des Routers und die Default-Route konfigurieren
root@router-0XX:/# vtysh

Hello, this is FRRouting (version 6.0.2).
Copyright 1996-2005 Kunihiro Ishiguro, et al.

router-0XX# conf t
router-0XX(config)# interface router3-eth0
router-0XX(config-if)# ip address 192.168.1.1/24
router-0XX(config-if)# exit
router-0XX(config-if)# ip route 0.0.0.0/0 192.168.1.2
router-0XX(config)# exit
router-0XX# write
Note: this version of vtysh never writes vtysh.conf
Building Configuration...
Integrated configuration saved to /etc/frr/frr.conf
[OK]
  • IPv4 Adresse auf der virtuellen Maschine auf der Netzwerkschnittestelle internet-eth0 konfigurieren
% ip addr add dev internet-eth0 192.168.1.2/24
  • Netzwerkschnittstelle internet-eth0 auf der virtuellen Maschine aktivieren
% ip link set dev internet-eth0 up
  • Auf der virtuellen Maschine in einer BASH-Shell das NAT einrichten
iptables -A POSTROUTING -t nat -o eth0 -j MASQUERADE
  • Auf dem Server ist die Linux Firewall iptables noch nicht installiert. Ab diesem Punkt können wir jedoch Pakete auf dem Router nachinstallieren
% apt install iptables
  • Im Router-Container in einer BASH-Shell das NAT einrichten
iptables -A POSTROUTING -t nat -o router3-eth0 -j MASQUERADE
  • An diesem Punkt sind sowohl der Router als auch Client und Server mit dem Internet verbunden und können vor dort Software nachinstallieren
  • Die Konfiguration der Lab-Umgebung ist abgeschlossen

6 TCP/IP Sicherheit

7 DNS und IP Fragmentierung

8 iptables/nftables Firewall

8.1 Einführung in Linux Firewall-Systeme

8.2 iptables

8.2.1 Chains

  • prerouting
  • input
  • output
  • forward
  • postrouting

8.2.2 Tables

  • filter (default)
  • nat
  • mangle
  • raw

8.2.3 Befehle

  1. Regeln auflisten
    iptables -L <chain> [-t <table>]
    iptables -L <chain> [-t <table>] -v -n
    
  2. Regeln einer Chain/Tabelle löschen
    iptables -F <chain> [-t <table>]
    
  3. Default Policy setzen
    iptables -P <chain> [DROP|ACCEPT]
    
  4. Chain anlegen/löschen
    iptables -N <chainname>
    iptables -X <chainname>
    
  5. Regeln anlegen/einfuegen/löschen
    iptables -A <chain> <Regel>
    iptables -I <chain> <pos> <Regel>
    iptables -R <chain> <pos> <Regel>
    iptables -D <chain> <pos>/<Regel>
    

8.2.4 Regeln

  • ein ! vor einem Regelelement negiert das Regelelement
-i <interface> # Eingangs-Interface
-o <interface> # Ausgangs-Interface
-s <ipaddr>    # Quelladresse
-d <ipaddr>    # Zieladresse
-p <protocol>  # tcp, udp, icmp ...
-dport <port>  # Zielport
-sport <port>  # Quellport
-m <modul> <modulparameter> # Modul laden und benutzen
-j <target>    # Ziel der Regel (ACCEPT, DROP, REJECT ...)

8.2.5 Targets

  • Targets definieren die Aktion einer Firewall-Regel
-j RETURN # back to calling chain
-j LOG # syslog
-j REJECT [--reject-with <icmp-error-type>]
-j SNAT --to-source <addr>[-addr][:port-port] # nur in Chain POSTROUTING, Table "nat"
-j DNAT --to-destination <addr>[-addr][:port-port] # nur den Chains PREROUTING und OUTPUT, Table "nat"
-j REDIRECT --to-ports port[-port]
-j MASQUERADE --to-ports port[-port]
-j chain-name

8.2.6 Protokoll "tcp"

--syn # Verbindungsaufbau
--tcp-flags <mask> <active> # Flags: SYN,ACK,FIN,RST,URG,PSH,ALL,NONE
# Beispiel für TCP Protokoll-Flags
--tcp-flags SYN,RST,ACK SYN

8.2.7 Protokoll "icmp"

--icmp-type <icmp-type>
iptables -p icmp -h  # lists icmp types

8.2.8 Modul "state"

  • Das iptables Modul state erlaubt es, IP Verbindungen anhand des Verbindungsstatus zu filtern
    • NEW - neue Verbindungen
    • ESTABLISHED - gültige, aktive Verbindung
    • RELATED - bei UDP Kommunikation - zugehörige Verbindungen, z.B. Antworten auf Anfragen
    • INVALID - Pakete ausserhalb einer gültigen Verbindung
-m state --state <state1[,state2,...]>

8.2.9 Modul 'recent'

  • Das Modul recent erlaubt es Pakete über einen Zeitraum miteinander in Verbindung zu setzen
  • Beispiel: nur 3 SSH Anmeldeversuche per 15 Minuten zulassen
iptables -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --set
iptables -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 900 --hitcount 3 -j DROP

8.2.10 Logging

  • Das Linux-Firewall System schreibt Log-Informationen in den Kernel-Log (die Firewall ist Bestandteil des Kernels)
-j LOG --log-level <num>
-j LOG --log-prefix <prefix> # 14 chars
-j LOG --log-tcp-options
-j LOG --log-ip-options

8.2.11 Masqerading

  • Masquerading ist eine Form des NAT, bei dem die Quell-IP-Adresse aller ausgehenden Pakete durch die Adresse des ausgehenden Netzwerkinterfaces des Firewall-Systems ersetzt wird
// masquerading auf dem Router anschalten, server und client kommen nun ins Internet
iptables -A POSTROUTING -t nat -o enp0s3 -j MASQUERADE
iptables -L -t nat

8.2.12 Beispiel einer 'iptables' Script Preamble

  • Zu Beginn eines iptables Firewall-Skriptes werden gewisse Grundeinstellungen vorgenommen. Dieses Skript-Fragment kann als Start-Vorlage für ein neues Firewall-Skript benutzt werden
#!/bin/sh

# default policy
iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROP

# bestehene Regeln löschen (Firewall Reset)
iptables -F
iptables -F -t nat

# Loopback Interface erlauben
iptables -A INPUT  -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT

8.2.13 Beispiel eines Firewall-Stopp-Script

  • Dieses kleine Skript stellt den Ursprungszustand der Linux-Firewall wieder her (alles ist erlaubt). Die Default-Policy wird auf "ACCEPT" (erlauben) gesetzt und die Inhalte die Filter- und NAT-Tabellen der Chains gelöscht.
#!/bin/sh

iptables -P INPUT   ACCEPT
iptables -P OUTPUT  ACCEPT
iptables -P FORWARD ACCEPT

# bestehene Regeln löschen (Firewall Reset)
iptables -F
iptables -F -t nat

8.2.14 Firewall Regeln testen mit "Sicherheitsnetz"

  • Dieses "Sicherheitsnetz"-Skript startet ein neues Firewall-Regelwerk ./firewall.sh. Vorher wird im Hintergrund das Firewall-Stopp-Skript gestartet, welches die Firewall nach 60 Sekunden wieder auf den ausgangszustand zurücksetzt:
(sleep 60 && ./firewall-stopp.sh) & ./firewall.sh
  • Mit dieser Befehlsfolge kann ein neues Firewall-Regelwerk getestet werden

8.3 Aufgabe Host-Firewall mit iptables

  • BIND 9 auf dem Router installieren, um eine Server Anwendung auf dem Router zu haben (welche von der Firewall blockiert werden sollte, d.h. der BIND 9 DNS Server sollte nicht vom Netzwerk erreichbar sein)
router% apt install bind9 dnsutils
router% systemctl start bind9
router% systemctl status bind9
  • Erstelle eine Host-Firewall-Regeln für den Router
    • Ausgehend alle Verbindungen erlauben
    • Eingehend auf router1-eth0, router2-eth0 und router3-eth0 nur SSH (Port 22) und ICMPv4 erlauben
    • Verworfene Pakete in das syslog (Journal) schreiben
  • Log-Ausgaben anschauen
journalctl -e  # an das Ende des Journal-Log springen
journalctl -f  # an das Ende des Journals springen und neue Einträge sofort anzeigen (wie tail -f)
  • Auf dem Router den SSH Dienst installieren und starten
router% apt install openssh-server
router% systemctl start sshd
router% systemctl status sshd

8.3.1 Tests

  • Diese Tests sollen funktionieren
    • SSH von der virtuellen Maschine zum Router (192.168.1.1)
    • "ping" vom Router in das Internet (z.B. 8.8.8.8)
    • "ping" von der virtuellen Maschine zum Router
  • dieser Test sollten nicht funktionieren
    • DNS Anfrage von der virtuellen Maschine zum Router (auf dem Router läuft ein BIND 9 DNS Server) dig @<ip-des-routers> chaos txt authors.bind. Diese Anfrage sollte ein Timeout ergeben, das Paket an Port 53 der Firewall sollte verworfen werden (im Journal nachschauen)

8.3.2 Kernel Logging und Container

  • Der Router läuft in einem Linux-Container. Linux-Container haben keinen Zugriff auf die Kernel-Logs
    • IPTables Logs werden in das Kernel-Log geschrieben und sind im Container nicht sichtbar
  • Lösung:
    • Das Paket ulogd2 installieren und starten
      router% apt install ulogd2
      router% systemctl start ulogd
      
    • IPTables Log-Regeln müssen nun nach NFLOG (NetFilter Log) anstatt LOG schreiben:
      # Pakete verwerfen und ins Syslog schreiben
      iptables -A INPUT -j NFLOG --nflog-prefix "FW Drop:"
      iptables -A INPUT -j DROP
      
    • Die Log-Datei befindet sich unter /var/log/ulog/syslogemu.log
      # tail -f /var/log/ulog/syslogemu.log
      Jun 20 13:11:51 router-012 FW Drop: IN=router3-eth0 OUT= MAC=7a:1c:aa:f4:78:f8:b2:8c:51:a2:42:58:08:00 SRC=192.168.1.2 DST=192.168.1.1 LEN=81 TOS=00 PREC=0x00 TTL=64 ID=27464 PROTO=UDP SPT=53048 DPT=53 LEN=61 MARK=0
      Jun 20 13:11:56 router-012 FW Drop: IN=router3-eth0 OUT= MAC=7a:1c:aa:f4:78:f8:b2:8c:51:a2:42:58:08:00 SRC=192.168.1.2 DST=192.168.1.1 LEN=81 TOS=00 PREC=0x00 TTL=64 ID=27545 PROTO=UDP SPT=53048 DPT=53 LEN=61 MARK=0
      Jun 20 13:12:01 router-012 FW Drop: IN=router3-eth0 OUT= MAC=7a:1c:aa:f4:78:f8:b2:8c:51:a2:42:58:08:00 SRC=192.168.1.2 DST=192.168.1.1 LEN=81 TOS=00 PREC=0x00 TTL=64 ID=28587 PROTO=UDP SPT=53048 DPT=53 LEN=61 MARK=0
      

8.3.3 Eine mögliche Lösung

#!/bin/sh

# default policy
iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROP

# bestehene Regeln löschen (Firewall Reset)
iptables -F
iptables -F -t nat

# Loopback Interface erlauben
iptables -A INPUT  -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT

# bestehende eingehende Verbindungen aus dem Internet erlauben
iptables -A INPUT  -i router3-eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
# bestehende eingehende Verbindungen aus dem Client/Server-Netz erlauben
iptables -A INPUT  -i router1-eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT  -i router2-eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT

# bestehende ausgehende Verbindungen erlauben
iptables -A OUTPUT  -m state --state RELATED,ESTABLISHED -j ACCEPT

# neue ausgehende Verbindungen erlauben
iptables -A OUTPUT  -m state --state NEW -j ACCEPT

# NATPT fuer ausgehende Verbindungen
iptables -A POSTROUTING -t nat -o router3-eth0 -j MASQUERADE

# SSH eingehend erlauben
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT

# icmp eingehend erlauben
iptables -A INPUT -p icmp -j ACCEPT

# Pakete verwerfen und ins Syslog schreiben
iptables -A INPUT -j LOG --log-prefix "FW Drop:"
iptables -A INPUT -j DROP
  • aktives (Filter-Tabelle-) Regelwerk mit Regel-Nummern, Zählern und Interface-Abhängigkeiten anzeigen
iptables -L -v -n --line-numbers

8.4 Aufgabe: DNS Anfragen zum BIND 9 DNS-Server erlauben

  • Zeit: 10 Minuten
  • Erweitere die Firewall, so das DNS-Anfragen (Port 53 UDP und TCP) von der virtuellen Maschine zum Router möglich sind
  • Firewall-Regeln neu laden und dann die Firewall testen (UDP und TCP Transport, von der virtuellen Maschine aus testen)
apt install dnsutils
dig @192.168.1.1 txt chaos authors.bind
dig +tcp @192.168.1.1 txt chaos version.bind

8.4.1 Lösungsvorschlag

[...]
# DNS eingehend erlauben
iptables -A INPUT -i router3-eth0 -p tcp --dport 53 -m state --state NEW -j ACCEPT
iptables -A INPUT -i router3-eth0 -p udp --dport 53 -m state --state NEW -j ACCEPT
[...]

8.5 Firewall testen mit "nmap"

  • Test der Firewall von der virtuellen Maschine aus (d.h. diese Befehle direkt auf der virtuellen Maschine ausführen)
apt install nmap
nmap -sT -v -A 192.168.1.1 # <ip-der-firewall>
  • Auf der virtuellen Maschine eine Route für das Client-Segment hinzufügen, dann ein Scan des Client-Netzes hinter der Firewall durchführen
ip route add 172.16.1.0/24 via 192.168.1.1
nmap -sT -v -A 172.16.1.0/24 # <ip-netz-hinter-der-firewall>

8.6 Firewall Unittest und Dokumentation mit Emacs-Org-Mode und Babel

apt install emacs-nox ruby
  • Org-Babel-Dokument (, am Anfang der Zeilen entfernen, sind notwendig um Org-Mode innerhalb von einem Org-Mode Dokument einzubetten)
#+Title: Firewall-Dokumentation
#+Language: de

* Babel anschalten                                                 :noexport:
#+BEGIN_EXAMPLE
(org-babel-do-load-languages 'org-babel-load-languages
  '((ruby . t)
))
#+END_EXAMPLE

#+BEGIN_SRC ruby :exports results
require 'time'
"Dieser Firewalltest wurde am #{Time.now}
 auf Rechner #{`hostname`} erstellt"
#+END_SRC

#+RESULTS:
: Dieser Firewalltest wurde am 2016-11-30 09:10:09 +0100
:  auf Rechner csmobile4.home.strotmann.de
:  erstellt

* Firewall Regeln

Dies sind unsere aktuellen Firewall Regeln

** INPUT Chain

*** Filter Tabelle

#+BEGIN_SRC ruby :exports results
`sudo iptables -L INPUT -t filter -v -n --line-numbers`
#+END_SRC

#+RESULTS:
#+begin_example
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1        0     0 ACCEPT     udp  --  virbr0 *       0.0.0.0/0            0.0.0.0/0            udp dpt:53
2        0     0 ACCEPT     tcp  --  virbr0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:53
3        0     0 ACCEPT     udp  --  virbr0 *       0.0.0.0/0            0.0.0.0/0            udp dpt:67
4        0     0 ACCEPT     tcp  --  virbr0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:67
5    63268   28M ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
6      591 41163 ACCEPT     all  --  lo     *       0.0.0.0/0            0.0.0.0/0
7     2655  631K INPUT_direct  all  --  *      *       0.0.0.0/0            0.0.0.0/0
8     2655  631K INPUT_ZONES_SOURCE  all  --  *      *       0.0.0.0/0            0.0.0.0/0
9     2655  631K INPUT_ZONES  all  --  *      *       0.0.0.0/0            0.0.0.0/0
10       0     0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate INVALID
11     516 84169 REJECT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            reject-with icmp-host-prohibited
#+end_example

* Firewall Tests

 * Können wir das Internet über Port 80 erreichen?

#+BEGIN_SRC ruby :exports both
`nmap -sT linuxhotel.de -p 80`
#+END_SRC

#+RESULTS:
:
: Starting Nmap 7.12 ( https://nmap.org ) at 2016-11-30 09:16 CET
: Nmap scan report for linuxhotel.de (217.69.87.63)
: Host is up (0.030s latency).
: rDNS record for 217.69.87.63: namebased-hostings.villa-vogelsang.de
: PORT   STATE SERVICE
: 80/tcp open  http
:
: Nmap done: 1 IP address (1 host up) scanned in 0.53 seconds

8.7 iptables Regeln sichern

  • iptables Regeln im Linux-Kernel können mit dem Befehl iptables-save in eine (Text-)Datei gesichert werden. Die Regeln aus einer Sicherungs-Datei können über den Befehl iptables-restore wieder hergestellt werden
router0x$ iptables-save > /etc/iptables.rules

8.8 Firewall-Config aus dem Internet

bart-firewall-config.gif

8.9 Verbindungen auf dem Router anschauen per iftop

router0x$ apt install iftop
router0x$ iftop -i router3-eth0
  • Taste p schaltet die Anzeige der UDP/TCP Ports an
  • Taste n schaltet DNS-Rückwärtsauflösung für IP-Adressen aus

8.10 IP6tables: Beispiel eines IPv6 Firewall Regelwerks

#!/bin/sh

# default policy
ip6tables -P INPUT DROP
ip6tables -P OUTPUT DROP
ip6tables -P FORWARD DROP

# bestehene Regeln löschen (Firewall Reset)
ip6tables -F
ip6tables -F -t nat

# Loopback Interface erlauben
ip6tables -A INPUT  -i lo -j ACCEPT
ip6tables -A OUTPUT -o lo -j ACCEPT

# Pakete mit RH0 (Routing Header 0, Source Routing) blocken
ip6tables -A INPUT -m rt --rt-type 0 -j DROP
ip6tables -A FORWARD -m rt --rt-type 0 -j DROP
ip6tables -A OUTPUT -m rt --rt-type 0 -j DROP

# bestehende eingehende Verbindungen aus dem Internet erlauben
ip6tables -A INPUT  -i enp0s3 -m state --state RELATED,ESTABLISHED -j ACCEPT

# bestehende eingehende Verbindungen aus dem Client-Netz erlauben
ip6tables -A INPUT  -i enp0s8 -m state --state RELATED,ESTABLISHED -j ACCEPT

# bestehende ausgehende Verbindungen erlauben
ip6tables -A OUTPUT  -m state --state RELATED,ESTABLISHED -j ACCEPT
ip6tables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT

# neue ausgehende Verbindungen erlauben
ip6tables -A OUTPUT  -m state --state NEW -j ACCEPT

# Link-Lokaler Multicast eingehend erlauben
ip6tables -A INPUT -s ff02::/16 -j ACCEPT

# Link-Lokale Kommunikation im Subnet erlauben
ip6tables -A INPUT -s fe80::/10 -j ACCEPT

# ICMPv6
ip6tables -A INPUT -p icmpv6 --icmpv6-type 1 -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type 2 -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type 3/0 -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type 4/1 -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type 4/2 -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-request -m limit --limit 900/minute -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-reply   -m limit --limit 900/minute -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbour-solicitation -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbour-advertisement -m hl --hl-eq 255 -j ACCEPT
# ICMPv6 redirect erlauben (notwendig fuer Router-Failover bei mehreren Default-Routern)
ip6tables -A INPUT -p icmpv6 --icmpv6-type redirect -m hl --hl-eq 255 -j ACCEPT
ip6tables -A INPUT -p icmpv6 -j LOG --log-prefix "ICMPv6:"
# Explizites DROP
ip6tables -A INPUT -p icmpv6 -j DROP

8.11 ICMP Filter

8.11.1 IPv4

  • Empfehlungen für ICMPv4 Filter in Firewalls "Recommendations for filtering ICMP messages" https://datatracker.ietf.org/doc/html/draft-ietf-opsec-icmp-filtering Dieses Dokument erklärt die verschiedenen ICMPv4 Meldungen, und ob diese in einer Gateway/Router-Firewall oder Host-Firewall geblockt werden können/sollten. Das Dokument gibt Empfehlungen welche ICMPv4 Meldungen per Rate-Limit in der Menge kontrolliert werden sollten.

8.11.2 IPv6

  • Firewall-Regeln für ICMPv6 werden in RFC 4890: "Recommendations for Filtering ICMPv6 Messages in Firewalls" beschrieben https://datatracker.ietf.org/doc/html/rfc4890
  • Unbedingt benötigte ICMPv6 Kommunikation
    • Destination Unreachable (Type 1) - All codes
    • Packet Too Big (Type 2)
    • Time Exceeded (Type 3) - Code 0 only
    • Parameter Problem (Type 4) - Codes 1 and 2 only
    • Echo request / Echo reply (needed for Teredo)
  • oft benötigte ICMPv6 Meldungen
    • Time Exceeded (Type 3) - Code 1
    • Parameter Problem (Type 4) - Code 0
    • Mobile IPv6 (nur wenn eingesetzt)
      • Home Agent Address Discovery Request (Type 144)
      • Home Agent Address Discovery Reply (Type 145)
      • Mobile Prefix Solicitation (Type 146)
      • Mobile Prefix Advertisement (Type 147)
  • selektiv Filtern (nur erlauben wenn diese Funktionen benutzt werden)
    • Node Information Query (Type 139)
    • Node Information Response (Type 140)
    • Router Renumbering (Type 138)
    • ICMPv6 mit experimentellen Nachrichten-Typen (100, 101, 200, and 201)
    • Erweiterungs-Nachrichten-Typen (Types 127 and 255)
  • alles andere Blocken, insbesondere
    • Seamoby Experimental (Type 150)
    • Unallocated Error messages (Types 5-99 inclusive and 102-126 inclusive)
    • Unallocated Informational messages (Types 154-199 inclusive and 202-254 inclusive)
  • Liste der ip6tables ICMPv6 Codes
ip6tables -p ipv6-icmp -h

8.12 Aufgabe: Routing-Firewall

  • Zeit: 30-40 Minuten
  • Erstelle erweiterte Regeln für das Routing in der Firewall auf der Router Maschine
    • SSH von Client/Firewall zum Server erlauben
    • HTTP (Port 80) vom Client zum Server erlauben
    • DNS von Client und vom Server ins Internet erlauben
    • HTTP(s) (80/443) vom Client/Server ins Internet zulassen (für Software Updates)
    • Masquerading NAT für Verbindungen ins 'Internet'
    • keine anderen ausgehenden Verbindungen erlauben

8.12.1 Tests

  • Auf dem Server OpenSSH Server installieren und den SSH Dienst starten
server% apt install openssh-server
server% systemctl start sshd
  • Auf dem Server einen Benutzer nutzer anlegen (adduser nutzer)
  • Auf dem Server für den Benutzer nutzer ein Passwort vergeben (passwd nutzer)
  • SSH vom Client zum Server testen
client# apt install openssh-client
client# ssh nutzer@100.64.1.2
  • HTTP vom Client zum Server testen
    • auf dem Server ist ein NGINX Webserver mit der Default Willkommensseite installiert und gestartet
  • auf dem Client einen Text-Modus Browser starten
    • die Dateien im Heimverzeichnis des Benutzers "root" auf dem Server sollten angezeigt werden (wenn keine Datei "index.html" vorliegt)
client# links 100.64.1.2
  • DNS vom Server ins Internet testen
server# apt install dnsutils
server# dig @1.1.1.1 kernel.org a
  • HTTP/HTTPS vom Client ins Internet testen
client% echo "nameserver 1.1.1.1" > /etc/resolv.conf
client% links kernel.org
client% links https://notes.defaultroutes.de
  • Auf dem Client "nmap" installieren und von dort den Router und den Server notes.defaultroutes.de (im Internet) scannen (was lässt die Firewall von innen nach aussen durch)
client% apt install nmap
client% nmap -sT 172.16.1.2
client% nmap -sT 100.64.1.1
client% nmap -sT 100.64.1.2
client% nmap -sT notes.defaultroutes.de
  • Bei einem Scan vom Server/Client oder von der Firewall in das Internet dürfen nur die Ports 53, 80 und 443 als offen angezeigt werden. Rechner in den Client und Server Netzen dürfen nur über diese Ports mit der Aussenwelt kommunizieren. Ein SSH vom Server ins Internet darf nicht möglich sein.

8.12.2 Eine mögliche Lösung

#!/bin/sh

iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROP

# bestehene Regeln löschen (Firewall Reset)
iptables -F
iptables -F -t nat

# Loopback Interface erlauben
iptables -A INPUT  -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT

# bestehende ausgehende/geroutete Verbindungen erlauben
iptables -A OUTPUT  -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT

# bestehende eingehende Verbindungen erlauben
iptables -A INPUT  -m state --state RELATED,ESTABLISHED -j ACCEPT

# NATPT fuer ausgehende Verbindungen
iptables -A POSTROUTING -t nat -o router3-eth0 -j MASQUERADE

# SSH eingehend zur Firewall/Router erlauben
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT

# SSH vom Router zum Server-Netz erlauben
iptables -A OUTPUT -o router2-eth0 -p tcp --dport 22 -m state --state NEW -j ACCEPT

# SSH vom Router zum Client-Netz erlauben
iptables -A OUTPUT -o router1-eth0 -p tcp --dport 22 -m state --state NEW -j ACCEPT

# SSH vom Client-Netz zum Server-Netz/Internet erlauben
iptables -A FORWARD -i router1-eth0 -p tcp --dport 22 -m state --state NEW -j ACCEPT

# HTTP vom Client zum Server erlauben
iptables -A FORWARD -s 172.16.1.2 -d 100.64.1.2 -p tcp --dport 80 -m state --state NEW -j ACCEPT

# DNS ausgehend zulassen
iptables -A OUTPUT -o router3-eth0 -m state --state NEW -p udp --dport 53 -j ACCEPT
iptables -A OUTPUT -o router3-eth0 -m state --state NEW -p tcp --dport 53 -j ACCEPT

# DNS vom Client/Server zulassen
iptables -A FORWARD -p udp --dport 53 -m state --state NEW -j ACCEPT
iptables -A FORWARD -p tcp --dport 53 -m state --state NEW -j ACCEPT

# HTTP/HTTPS vom Router/Client/Server zulassen
iptables -A FORWARD -o router3-eth0 -m state --state new -p tcp --dport 80  -j ACCEPT
iptables -A FORWARD -o router3-eth0 -m state --state new -p tcp --dport 443 -j ACCEPT
iptables -A OUTPUT  -o router3-eth0 -m state --state new -p tcp --dport 80  -j ACCEPT
iptables -A OUTPUT  -o router3-eth0 -m state --state new -p tcp --dport 443 -j ACCEPT

# icmp erlauben
iptables -A INPUT -p icmp -j ACCEPT

# Pakete verwerfen
iptables -A INPUT   -j NFLOG --nflog-prefix "FW INP Drop:"
iptables -A FORWARD -j NFLOG --nflog-prefix "FW FRW Drop:"
iptables -A OUTPUT  -j NFLOG --nflog-prefix "FW OUT Drop:"
iptables -A INPUT -j DROP
iptables -A FORWARD -j DROP
iptables -A OUTPUT -j DROP

8.13 Beispiel Destination-NAT (DNAT)

  • 15 Minuten
  • Per DNAT lässt sich die Ziel-Adresse von Netzwerk-Paketen umschreiben.
  • Beispiel: Zugriff vom Internet/Virtuellen Maschine zum Webserver im Server Container (in das Firewall-Skript an passender Stelle einbauen)
# HTTP vom Internet auf Server zulassen
iptables -A FORWARD -i router3-eth0 -m state --state new -p tcp --dport 80 -j ACCEPT
  • Auf der virtuellen Maschine eine Route auf den Server setzen und dann per Text-Modus Browser links die URL http://100.64.1.2 aufrufen
virtuelle maschine% ip route add 100.64.1.0/24 via 192.168.1.1
virtuelle maschine% apt install links
virtuelle maschine% links http://100.64.1.2
  • Der "produktive" Webserver soll kurzzeitig von Netz genommen werden und ein alternativer Webserver soll die Rolle übernehmen. Dabei soll die IP-Adresse des Servers im DNS nicht geändert werden.
  • Per IPTables DNAT werden wir die Anfragen zum Server-Container auf den Client-Container umleiten
  • Auf dem Client (der hier die Rolle eines Backup-Webserver einnimmt) eine kleine Webseite erzeugen und den Webserver starten
echo "dies ist der Client" > ~/index.html
cd ~
apt install python3
python3 -m http.server 80
  • Backup DNAT um Anfragen an den Webserver 100.64.1.2 auf den Client "umzuleiten"
# HTTP per DNAT von Server auf den Client "umbiegen"
iptables -A PREROUTING -t nat -i router3-eth0 -p tcp --dport 80 \
    -j DNAT --to-destination 172.16.1.2

8.13.1 DNAT per REDIRECT: Stealth Proxy

  • Alternative, wenn das Ziel eine Anwendung auf der Firewall-Maschine ist (z.B. für einen "stealth" reverse Proxy)
iptables -A PREROUTING -t nat -i router3-eth0 -p tcp --dport 80 \
    -j REDIRECT --to-port 3128

8.14 Modernes Linux: iptables oder nftables

  • Verwendet unser Debian 10 Linux noch iptables? Verfolge die Links auf dem Programm iptables. Wie heisst das wirklich genutzte Binärprogramm?
  • Die nftables Regel-Darstellung des aktuellen IPTables Regelwerkes anschauen
    router% nft list ruleset
    

8.15 nftables Firewall

./firewall-stopp.sh
iptables -L
iptables -L -t nat
  • "nftables" installieren (bei Debian 10 ist "nftables" schon installiert und aktiv)
apt install nftables
  • Das nftables Ruleset auflisten (sollte am Anfang leer sein)
nft list ruleset
  • Ein nft Regelsatz für eine Host-Firewall, diesen nftables Regelsatz per Editor in die Datei /etc/nftables.conf schreiben
# Regelsatz löschen
flush ruleset

# Variable "any" definieren
define any = 0::0/0

table inet filter {
        chain input {
                type filter hook input priority 0;

		# set default policy to "drop"
		policy drop;

                # accept any localhost traffic
                iif lo accept

                # accept traffic originated from us
                ct state established,related accept

                # activate the following line to accept common local services
                tcp dport { 22, 80, 443 } ct state new accept

                # NTP multicast
                ip6 daddr ff02::1010 udp dport 123 accept

                # mDNS (avahi)
                ip6 daddr ff02::fb udp dport 5353 accept
                ip  daddr 224.0.0.251 udp dport 5353 accept

                # DHCPv6
                ip6 saddr $any udp dport 546 accept

                # IPP (CUPS)
                tcp dport 631 accept

                # Accept neighbour discovery otherwise IPv6 connectivity breaks.
                ip6 saddr $any icmpv6 type { nd-neighbor-solicit,
                                             nd-router-advert,
                                             nd-neighbor-advert }  accept

                # Accept essential icmpv6
                ip6 saddr $any icmpv6 type { echo-reply,
                                             echo-request,
                                             packet-too-big,
                                             destination-unreachable,
                                             time-exceeded,
                                             parameter-problem } accept

                # count and drop any other traffic (group 0 sends the log output to NFLOG instead of kernel syslog)
                counter log prefix "nftables drop: " group 0 drop
        }
}
  • nftables Firewall laden
systemctl start nftables
  • Regelwerk anzeigen
nft list ruleset
  • Log-Ausgaben anzeigen, es sollten nun Pakete mit dem Log-Prefix "nftables drop:" im Log erscheinen
journalctl -f | grep nftables
  • Eigenen Router von der virtuellen Maschine aus mit nmap scannen (einmal mit nftables Firewall eingeschaltet, danach mit der nftables Firewall ausgeschaltet. Gibt es einen Unterschied?)
nmap -sT 192.168.1.xxx # <--- IP Adresse des Routers
  • Beipsiel der nmap Ausgabe
Starting Nmap 7.70 ( https://nmap.org ) at 2019-12-10 11:31 CET
Nmap scan report for 192.168.1.166
Host is up (0.00048s latency).
Not shown: 996 filtered ports
PORT    STATE  SERVICE
22/tcp  open   ssh
80/tcp  closed http
443/tcp closed https
631/tcp closed ipp
MAC Address: 08:00:00:00:00:0A (Unknown)

Nmap done: 1 IP address (1 host up) scanned in 5.08 seconds

8.16 Masquerading NAT mit nftables

  • Wie auch bei iptables wird Masquerading NAT (Cone NAT oder NATPT) nach der Routing-Entscheidung angewendet, bevor das Paket die Firewall verlässt
  • Es muss eine Chain mit prerouting Hook existieren, in welcher die Rück-Pakete zurück-übersetz werden. Diese Chain kann auch leer sein. Fehlt die prerouting Chain, so funktioniert das Masquerading NAT nicht!
table ip nat {
	chain prerouting {
		type nat hook prerouting priority 0;
	}

	# Ersetze die Quell-IP-Adresse aller Pakete, welche die Firewall über die
        # Netzwerk-Schnittstelle "router3-eth0" verlassen
	chain postrouting {
		type nat hook postrouting priority 0;
		oifname "router3-eth0" masquerade
	}
}

8.17 Übung: eine Dual-Stack Routing Firewall

  • 30 Minuten
  • Erstelle auf dem Router-Container eine Dual-Stack Routing Firewall
    • SSH von Client/Firewall zum Server erlauben
    • HTTP (Port 80) vom Client zum Server erlauben
    • DNS von Client und vom Server ins Internet erlauben
    • HTTP(s) (80/443) vom Client/Server ins Internet zulassen (für Software Updates)
    • Masquerading NAT für Verbindungen ins 'Internet'
    • keine anderen ausgehenden Verbindungen erlauben

8.17.1 Eine mögliche Lösung

flush ruleset

# Variable "any" definieren
define any = 0::0/0

table inet filter {
   chain forward {
     type filter hook forward priority 0;
     policy drop;

     # Bestehende Verbindungen erlauben
     ct state established, related accept
     # SSH von Client zum Server
     oif router2-eth0 ip saddr 172.16.1.2 tcp dport 22 ct state new accept
     oif router2-eth0 ip6 saddr fd01::/64 tcp dport 22 ct state new accept
     # HTTP vom Client zum Server
     iif router1-eth0 oif router2-eth0 tcp dport 80 accept
     # DNS vom Client/Server in das Internet erlauben
     iif { router1-eth0, router2-eth0 } oif router3-eth0 tcp dport 53 accept
     iif { router1-eth0, router2-eth0 } oif router3-eth0 udp dport 53 accept
     # HTTP und HTTPS vom Client/Server in das Internet
     iif { router1-eth0, router2-eth0 } oif router3-eth0 tcp dport { 80, 443 } accept
     counter log prefix "nft forward drop: " group 0 drop
   }
   chain input {
        type filter hook input priority 0;

        # set default policy to "drop"
        policy drop;
        # accept any localhost traffic
        iif lo accept
        # accept traffic originated from us
        ct state established,related accept
        # Allow SSH towards the Firewall
        tcp dport 22 ct state new accept
        # Accept neighbour discovery otherwise IPv6 connectivity breaks.
        ip6 saddr $any icmpv6 type { nd-neighbor-solicit,
                                     nd-router-advert,
                                     nd-neighbor-advert }  accept
        # Accept essential icmpv6
        ip6 saddr $any icmpv6 type { echo-reply,
                                     echo-request,
                                     packet-too-big,
                                     destination-unreachable,
                                     time-exceeded,
                                     parameter-problem } accept
        # count and drop any other traffic
        counter log prefix "nftables drop: " group 0 drop
  }
  chain output {
        type filter hook output priority 0;
        policy drop;

        # Von der Firewall SSH zum Server erlauben
	oif router2-eth0 tcp dport ssh accept;

        ip6 daddr $any icmpv6 type { nd-neighbor-solicit,
                                     nd-router-advert,
                                     nd-neighbor-advert }  accept
        # Accept essential icmpv6
        ip6 daddr $any icmpv6 type { echo-reply,
                                     echo-request,
                                     packet-too-big,
                                     destination-unreachable,
                                     time-exceeded,
                                     parameter-problem } accept

        # count and drop any other traffic
        counter log prefix "nft output drop: " group 0 drop
  }
}
table ip nat {
        chain prerouting {
                type nat hook prerouting priority 0;
        }
        chain postrouting {
                type nat hook postrouting priority 0;
                oif router3-eth0 masquerade
        }
}

9 eBPF/BCC

  • eBPF ist die extended Berkeley Packet Filter virtuelle Maschine im Linux Kernel
  • BCC ist die BPF Compiler Collection, eine Sammlung von Tools und eBPF Programmen
  • eBPF ist eine Weiterentwicklung der original Berkeley Packet Filter Technologie https://en.wikipedia.org/wiki/Berkeley_Packet_Filter

9.1 Die eBPF Idee

  • eBPF erlaubt es dem Benutzer, Programme im Betriebssystem-Kern sicher innerhalb einer Sandbox auszuführen
    • eBPF wird benutzt im die Funktionen des Betriebssystem-Kerns sicher und effizient zu erweitern, ohne den Quell-Code des Kernels ändern oder Module laden zu müssen
    • eBPF kann Netzwerk-Pakete (und andere Datenstrukturen) innerhalb des Linux-Kernels überwachen und verändern
    • eBPF Programme sind keine Kernel Module, es ist nicht notwendig ein Kernel-Entwickler zu sein um eBPF benutzen zu können
      • Wissen über Programmierung in der Sprache "C" ist jedoch von Vorteil

9.2 eBPF

ebpf.png

9.3 eBPF Einsatzgebiete

9.4 Die Wurzeln von BPF

  • Der originale BSD Packet Filter (BPF) wurde von Steven McCanne und Van Jacobson am Lawrence Berkeley Laboratory entwickelt (https://www.tcpdump.org/papers/bpf-usenix93.pdf)
    • BPF wurde auf fast alle Unix/Linux Systeme und viele non-Unix Betriebssysteme portiert (z.B. Windows, BeOS/Haiku, OS/2 …)
    • BPF ist die Basis-Technologie hinter bekannten Netzwerk-Sniffing Werkzeugen wie tcpdump und Wireshark

9.5 BPF am Beispiel von tcpdump

  • Wird ein Werkzeug auf Basis vom BPF verwendet, so wird der Filter in einen Bytecode für die BPF virtuelle Maschine im Linux-Kernel übersetzt und in den Kernel geladen
    • Das Betriebssystem ruft das Programm für jedes Netzwerk-Paket auf, welches den Netzwerk-Stack durchläuft
    • Nur Pakete welche auf den Filter-Ausdruck passen werden an das Programm im Userspace weitergeleitet (tcpdump in dieses Beispiel)
    • BPF reduziert die Menge an Daten welche zwischen dem Kernel und dem Userspace ausgetauscht werden müssen

9.6 BPF am Beispiel von tcpdump

tcpdump kann angewiesen werden den BPF Quellcode des tcpdump Filters auszugeben:

# tcpdump -d port 53 and host 1.1.1.1
Warning: assuming Ethernet
(000) ldh      [12]
(001) jeq      #0x86dd          jt 19   jf 2
(002) jeq      #0x800           jt 3    jf 19
(003) ldb      [23]
(004) jeq      #0x84            jt 7    jf 5
(005) jeq      #0x6             jt 7    jf 6
(006) jeq      #0x11            jt 7    jf 19
(007) ldh      [20]
(008) jset     #0x1fff          jt 19   jf 9
(009) ldxb     4*([14]&0xf)
(010) ldh      [x + 14]
(011) jeq      #0x35            jt 14   jf 12
(012) ldh      [x + 16]
(013) jeq      #0x35            jt 14   jf 19
(014) ld       [26]
(015) jeq      #0x1010101       jt 18   jf 16
(016) ld       [30]
(017) jeq      #0x1010101       jt 18   jf 19
(018) ret      #262144
(019) ret      #0

9.7 eBPF vs. BPF

  • Während BPF (heute auch cBPF = classic BPF genannt) Netzwerk Pakete im Betriebssystem-Kern filtert, kann eBPF noch auf weitere Datenstrukturen Filter anwenden:
    • Kernel Systemcalls
    • Kernel Tracepoints
    • Kernel Funktionen
    • Userspace Tracepoints
    • Userspace Funktionen

9.8 eBPF und der Linux Kernel

  • Die erste Version von eBPF wurde im Linux Kernel 3.18 eingeführt
    • die meisten neuen Kernel Versionen seither haben weitere, neue eBPF Funktionen implementiert
    • Linux Distributionen (Red Hat/Canonical/Suse) haben zum Teil eBPF Funktionen auf ältere LTS Kernel Versionen zurückportiert
    • Eine Übersicht der eBPF Funktionen nach Linux Kernel Version aufgeschlüsselt: https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md

9.9 Die eBPF Architektur

9.9.1 Die eBPF virtuelle Maschine

  • eBPF Programme werden für eine virtuelle CPU Architektur übersetzt
  • Der Programmcode wird in den Linux Kernel geladen und dort geprüft
  • Auf einigen CPU Architekturen (amd64, AARCH64) wird der eBPF Bytecode in nativen Maschinencode re-compiliert (Just in Time Compiler = JIT)

9.9.2 XDP - Express Data Path

  • Der express data path (XDP) innerhalb des Linux-Kernels ist eine Infrastruktur, um auf unterster Ebene Kontrolle über Netzwerk-Pakete auszuüben
    • Der normale Datenfluss im Linux Netzwerk-Stack kann via XDP umgangen werden
    • eBPF Programme können in den eXpress Data Path (XDP) geladen werden

9.9.3 XDP / eBPF Hardware Offloading

  • XDP eBPF Programm können auf verschiedenen Ebenen in den Linux Kernel geladen werden
    • Offload XDP: direkt in die Netzwerk-Hardware (ASIC/FPGA, benötigt Unterstützung für XDP in der Hardware, z.B. vorhanden in den Netronome Netzwerkadaptern)
    • Native XDP: In den Linux Kernel Netzwerk-Treiber der Netzwerkschnittstelle (benötigt Unterstützung durch den Treiber)
    • Generic XDP: In den Linux Kernel Netzwerk-Stack (weniger Performance, aber ohne besondere Unterstützung von Hardware oder Treibern möglich)

9.9.4 XDP / eBPF Ausführungs-Ebenen

xdp-ebpf-level.png

9.10 XDP Funktionen

  • XDP Programme können
    • lesen: Netzwerk-Pakete und Statistiken sammeln
    • verändern: - den Inhalt der Netzwerkpakete ändern
    • verwerfen: Ausgewählte Netzwerk-Pakete können verworfen werden (Firewall)
    • umleiten: Netzwerkpakete können auf dem gleichen oder anderen Netzwerkschnittstellen umgeleitet werden (Switching/Routing)
    • durchlassen: Das Netzwerkpaket wird an den Linux TCP/IP Stack zur normalen Bearbeitung übergeben

9.11 XDP vs DDoS Angriffe

  • XDP kann unerwünschten Netzwerk-Verkehr schon sehr früh im Netzwerk-Stack verwerfen (z.B. innerhalb der Netzwerk-Hardware). Dies ist ein guter Schutz gegen DDoS Angriffe

ebpf-xdf-ddos-twitter.png

9.12 eBPF/XDP Support in DNS Software

9.13 eBPF benutzen

9.13.1 eBPF Programme erstellen

  • eBPF Programme können auf verschiedene Weise erstellt werden
    • Als Low level eBPF Assembler Code
    • Mit einem High Level Compiler (mit Hilfe von LLVM): C / GO / Rust / Lua / Python …
    • Spezialisierte eBPF Script-Sprachen: z.B. bpftrace

9.13.2 BCC

  • BCC ist die BPF Compiler Sammlung

9.13.3 Fertige BCC eBPF Programme

bcc_tracing_tools_2019.png

9.13.4 Beispiel der Benutzung eines mit BCC erstellten ePBF Werkzeugs (1/2)

  • Die Syscalls eines BIND 9 Prozesses auswerten mit dem Programm syscount
# syscount-bpfcc -p `pgrep named` -i 10
Tracing syscalls, printing top 10... Ctrl+C to quit.
[07:34:19]
SYSCALL                   COUNT
futex                       547
getpid                      121
sendto                      113
read                         56
write                        31
epoll_wait                   31
openat                       23
close                        20
epoll_ctl                    20
recvmsg                      20

9.13.5 Beispiel der Benutzung eines mit BCC erstellten eBPF Werkzeugs (2/2)

  • Die Linux Capabilities von laufenden Prozessen anzeigen
# capable-bpfcc | grep named
07:36:17  0      29378  (named)          24   CAP_SYS_RESOURCE     1
07:36:17  0      29378  (named)          24   CAP_SYS_RESOURCE     1
07:36:17  0      29378  (named)          12   CAP_NET_ADMIN        1
07:36:17  0      29378  (named)          21   CAP_SYS_ADMIN        1
07:36:17  0      29378  named            6    CAP_SETGID           1
07:36:17  0      29378  named            6    CAP_SETGID           1
07:36:17  0      29378  named            7    CAP_SETUID           1
07:36:17  109    29378  named            24   CAP_SYS_RESOURCE     1

9.13.6 Die eBPF Skriptsprache "bpftrace"

  • bpftrace ist eine kleine Skripting-Sprace ähnlich wie awk oder dtrace
  • bpftrace Programme binden sich an eBPF-Probes und führen Funktionen aus, wann immer ein System-Ereignis gemeldet wird (systemcall, function-call)
  • bpftrace hat Hilfs-Strukturen eingebaut um direkt mit eBPF Datanstrukturen arbeiten zu können
  • bpftrace erlaubt es eBPF Programme im Vergleich zu BCC kompakter zu schreiben

9.14 Instrumentierung des Linux TCP/IP Stacks mittels eBPF

9.14.1 BCC und bpftrace Programme

  • Es gibt hunderte kleine eBPF Programme welche es dem Benutzer erlauben tief in den Linux Netzwerk Stack zu schauen the Linux network stack
    • Die BCC Beispielprogramme
    • Die bpftrace Beispielprogramme
    • Die Programme aus dem eBPF Büchern und den dazugehörigen Webseiten

9.14.2 Beispiel: gethostlatency

  • Das BCC Programm gethostlatency misst die Latenz der client-seitigen DNS Namensauflösung durch Systemaufrufe wie getaddrinfo oder gethostbyname
# gethostlatency-bpfcc
TIME      PID    COMM                  LATms HOST
10:21:58  19183  ping                 143.22 example.org
10:22:18  19184  ssh                    0.03 host.example.de
10:22:18  19184  ssh                   60.59 host.example.de
10:22:35  19185  ping                  23.44 isc.org
10:22:49  19186  ping                4459.72 yahoo.co.kr

9.14.3 Beispiel: netqtop

  • netqtop - Gibt Statistiken über die Warteschlangen einer Netzwerkschnittstelle aus. Mit diesem Programm können Informationen bei der Überlastung einer Netzwerkschnittstelle gesammelt werden.
# netqtop-bpfcc -n eth0 -i 10
Mon Nov 15 07:43:29 2021
TX
 QueueID    avg_size   [0, 64)    [64, 512)  [512, 2K)  [2K, 16K)  [16K, 64K)
 0          297.82     2          48         1          4          0
 Total      297.82     2          48         1          4          0

RX
 QueueID    avg_size   [0, 64)    [64, 512)  [512, 2K)  [2K, 16K)  [16K, 64K)
 0          70.95      43         34         0          0          0
 Total      70.95      43         34         0          0          0
-----------------------------------------------------------------------------

9.14.4 Beispiel: tcptracer

  • Dieses Programm zeig den Status von TCP Verbindungen im System (A = accept, C = connected, X = closed) sowie der Quell- und Ziel IP-Adressen und -Ports.
# tcptracer-bpfcc -p $(pgrep named)
Tracing TCP established connections. Ctrl-C to end.
T  PID    COMM             IP SADDR            DADDR            SPORT  DPORT
C  29404  isc-net-0000     4  127.0.0.1        127.0.0.1        41555  953
A  29378  isc-socket-0     4  127.0.0.1        127.0.0.1        953    41555
X  29404  isc-socket-0     4  127.0.0.1        127.0.0.1        41555  953
X  29378  isc-socket-0     4  127.0.0.1        127.0.0.1        953    41555
C  29378  isc-net-0000     4  46.101.109.138   192.33.4.12      43555  53
C  29378  isc-net-0000     4  46.101.109.138   192.33.4.12      33751  53
X  29378  isc-socket-0     4  46.101.109.138   192.33.4.12      43555  53
X  29378  isc-socket-0     4  46.101.109.138   192.33.4.12      33751  53
C  29378  isc-net-0000     4  46.101.109.138   193.0.14.129     38145  53
C  29378  isc-net-0000     4  46.101.109.138   192.33.14.30     40905  53
X  29378  isc-socket-0     4  46.101.109.138   193.0.14.129     38145  53
X  29378  isc-socket-0     4  46.101.109.138   192.33.14.30     40905  53

9.14.5 Beispiel: tcpconnlat

  • tcpconnlat gibt die Latenz einer TCP-basierten Verbindung aus, hier eine ausgehende DNS Anfrage über TCP eines BIND 9 resolvers (in Beispiel eine Abfrage von microsoft.com txt, wobei die Antwort zu groß für ein 1232 Byte UDP Paket ist)
    • isc-net-0000 ist der interne Name des BIND 9 Threads
# tcpconnlat-bpfcc
PID    COMM         IP SADDR            DADDR            DPORT LAT(ms)
29378  isc-net-0000 4  46.101.109.138   193.0.14.129     53    37.50
29378  isc-net-0000 4  46.101.109.138   192.52.178.30    53    14.01
29378  isc-net-0000 4  46.101.109.138   199.9.14.201     53    8.48
29378  isc-net-0000 4  46.101.109.138   192.42.93.30     53    1.90
29378  isc-net-0000 4  46.101.109.138   40.90.4.205      53    14.27
29378  isc-net-0000 4  46.101.109.138   199.254.48.1     53    19.21
29378  isc-net-0000 4  46.101.109.138   192.48.79.30     53    7.66
29378  isc-net-0000 4  46.101.109.138   192.41.162.30    53    7.97
29396  isc-net-0000 4  127.0.0.1        127.0.0.1        53    0.06

9.14.6 Beispiel: udplife

  • udplife ist ein bpftrace um die UDP Roundtrip-Time (hier DNS round trip time) einer UDP Kommunikation auszugeben (Programm von Brendan Gregg, siehe Links)
# udplife.bt
Attaching 8 probes...
PID   COMM       LADDR           LPORT RADDR           RPORT   TX_B   RX_B MS
29378 isc-net-00 46.101.109.138  0     199.19.57.1     16503     48    420 268
29378 isc-net-00 46.101.109.138  0     51.75.79.143    81        49     43 13
29378 isc-net-00 46.101.109.138  0     199.6.1.52      16452     48    408 24
29378 isc-net-00 46.101.109.138  0     199.249.120.1   81        44     10 9
29378 isc-net-00 46.101.109.138  0     199.254.31.1    32891     64     30 273
29378 isc-net-00 46.101.109.138  0     65.22.6.1       32891     64     46 266

9.14.7 Beispiele für den Einsatz von eBPF: "Server agnostic DNS augmentation using eBPF"

9.15 BIND 9 Instrumentieren

9.15.1 Beispiel: Logging von DNS Forwarding-Entscheidungen

  • Ein BIND 9 DNS Resolver mit einer Forward-Zone konfiguriert.
zone "dnslab.org" {
        type forward;
        forwarders { 1.1.1.1; 8.8.8.8; };
};
  • Das BIND 9 Logging System ist sehr mächtig, hat jedoch keine Funktion um DNS Forwarding Entscheidungen zu loggen
  • Ziel: Ein bpftrace Skript erstellen um BIND 9 DNS Forwarding-Entscheidungen auszugeben

9.15.2 Schritt 1 - Use the force source

  • Der BIND 9 Quellcode ist auf dem ISC Github Server offen verfügbar und durchsuchbar: https://gitlab.isc.org
  • Eine Suche im BIND 9 Quellcode nach forwarding findet die Funktion dns_fwdtable_find in /lib/dns/forward.c. Das schaut erfolgversprechend aus:

dns_fwdtable_find.png

9.15.3 Schritt 2 - Ein Proof-of-Concept Test

  • Die Funktion dns_fwdtable_find nimmt als Eingangsparameter einen Domain-Namen und liefert den Wert 0 wenn der Namen via Forwarding aufgelöst werden muss, und einen Wert > 0 wenn Forwarding nicht verwendet wird
    • Ein bpftrace one-liner gibt uns die Information ob diese Funktion für diese Aufgabe benutzbar ist:
bpftrace -e 'uretprobe:/lib/x86_64-linux-gnu/libdns-9.16.22-Debian.so:dns_fwdtable_find { print(retval) }'

9.15.4 Schritt 2 - Ein Proof-of-Concept Test

forwarding-bpftrace-poc.png

9.15.5 Schritt 3 - Das bpftrace Skript planen

  • Nun da wir die Funktion für die Aufgabe gefunden und verifiziert haben können wir ein bpftrace Skript schreiben
  • Das Skript wird
    • Den Domain Namen, welcher der Funktion dns_fwdtable_find übergeben wird, beim Eintritt in die Funktion speichern
    • Den Rückgabewert der Funktion (retval) auf den Wert Null (0) prüfen und den Domain Namen ausgeben wenn Forwarding benutzt wird

9.15.6 Herausforderung - Kampf mit structs

  • Der Domain-Name welcher auf Forwarding geprüft werden soll wird der Funktion als Datenstruktur (struct) vom Typ dns_name_t übergeben
    • Es ist leider kein einfacher Zeiger auf eine Zeichenkette, welche wir ausdrucken können
  • Eine Suche durch die Dokumentation des ISC BIND 9 Quellcodes findet die Datenstruktur dns_name_t. Das 2te Feld ist ein unsigned char * ndata, dies scheint der Domain-Name zu sein
  • Die Definition des Datenstruktur dns_name_t befindet sich in der Datei lib/dns/include/dns/name.h dns_name_t_definition.png
  • bpftrace beutzt eine der Programmiersprache C ähnliche Syntax, daher können wir die Definition der Datenstruktur aus dem BIND 9 Quellcode direkt in das bpftrace Skript importieren
    • Die verkettete Liste und das Feld isc_buffer_t wird für unser Skript nicht benötigt und da diese Felder keine eingebauten Datentypen beschreiben kommentieren wir diese aus:
#!/usr/bin/bpftrace

struct dns_name {
        unsigned int   magic;
        unsigned char *ndata;
        unsigned int   length;
        unsigned int   labels;
        unsigned int   attributes;
        unsigned char *offsets;
//      isc_buffer_t  *buffer;
//      ISC_LINK(dns_name_t) link;
//      ISC_LIST(dns_rdataset_t) list;
};
[...]

9.15.7 Einen Text beim Start des Skripts ausgeben

  • Die BEGIN Pseudo-Probe wird beim Start des Skriptes aktiv und gibt eine Nachricht auf das Terminal aus um dem Benutzer darüber zu informieren das das Skript erfolgreich gestartet wurde
[...]
BEGIN
{
  print("Waiting for forward decision...\n");
}
[...]

9.15.8 Den Funktionsaufruf überwachen

  • Diese Probe wird aktiv wenn die Funktion im BIND 9 aufgerufen wird
    • Es ist eine uprobe (User-Space Entry-Probe)
    • Die Probe instrumentalisiert die Funktion dns_fwdtable_find in der dynamischen Bibliotheks-Datei /lib/x86_64-linux-gnu/libdns-9.16.22-Debian.so
    • Das 2te Argument des Funktionsaufrufs (arg1) wird in ein struct dns_name gecastet und das Feld ndata referenziert
    • Der Inhalt des Feldes wird in der Variable @dns_name[tid] (indiziert mit der Thread ID (tid) des laufenden BIND 9 Threads im Prozess) gespeichert
[...]
uprobe:/lib/x86_64-linux-gnu/libdns-9.16.22-Debian.so:dns_fwdtable_find
{
  @dns_name[tid] = ((struct dns_name *)arg1)->ndata
}
[...]

9.15.9 Den Rücksprung aus der Funktion überwachen

  • Die 3te Probe wird beim Verlassen der Funktion aktiv (uretprobe - User-space Funktion Return Probe)
    • Gleiche Bibliotheks-Datei und Funktion wie zuvor
  • Ist der Rückgabewert der Dunktion Null 0 (Domain Name muss über Forwarding aufgelöst werden) wird der Wert der Variable @dns_name[tid] in eine Zeichenkette gewandelt und ausgegeben
  • Die Variable @dns_name[tid] wird nicht weiter benötigt und gelöscht
uretprobe:/lib/x86_64-linux-gnu/libdns-9.16.22-Debian.so:dns_fwdtable_find
{
 if (retval == 0) {
    printf("Forwarded domain name: %s\n", str(@dns_name[tid]));
 }
 delete(@dns_name[tid]);
}

9.15.10 Das vollständige Skript

#!/usr/bin/bpftrace

struct dns_name {
        unsigned int   magic;
        unsigned char *ndata;
        unsigned int   length;
        unsigned int   labels;
        unsigned int   attributes;
        unsigned char *offsets;
//      isc_buffer_t  *buffer;
//      ISC_LINK(dns_name_t) link;
//      ISC_LIST(dns_rdataset_t) list;
};

BEGIN
{
  print("Waiting for forward decision...\n");
}
uprobe:/lib/x86_64-linux-gnu/libdns-9.16.22-Debian.so:dns_fwdtable_find
{
  @dns_name[tid] = ((struct dns_name *)arg1)->ndata
}

uretprobe:/lib/x86_64-linux-gnu/libdns-9.16.22-Debian.so:dns_fwdtable_find
{
 if (retval == 0) {
    printf("Forwarded domain name: %s\n", str(@dns_name[tid]));
 }
 delete(@dns_name[tid]);
}

9.15.11 The Skript bei der Anwendung

  • Immer wenn ein Domain-Namen im BIND 9 Resolver aufgelöst wird, wird auch das bpftrace Skript aktiv
    • In diesem Beispiel werden alle Anfragen an die Domain dnslab.org per Forwarding weitergeleitet, jedoch nicht die Anfragen an ietf.org

bpftrace-script.png

9.16 Paket Filter mit eBPF

9.16.1 eBPF als Netzwerk Firewall

  • Mittels eBPF können sehr effiziente Firewall-Systeme gebaut werden
    • eBPF kann Netzwerk-Verkehr stoppen, bevor dieser den Linux TCP/IP Stack oder die Anwendung erreicht
    • Da eBPF pro Netzwerk-Paket ein volles Programm ausführt können komplexe Filter definiert werden
      • Filter auf Basis von DNS Query Namen
      • DNSSEC Daten in der Antwort verfügbar?
      • Quell-IP des antwortenden autoritativen DNS Servers (blockieren bekannter "bösartiger" DNS Server)
      • EDNS data (DNS Pakete mit DNS Cookies bevorzugen)

9.16.2 eBPF Firewall Beispiel: Block-Non-DNS

  • Dieses Beispiel zeigt einen simplen eBPF Netzwerk-Filter
    • Der Filter blockiert jeglichen UDP Verkehr an ein Interface (hier das Loopback-Interface) mit der Ausnahme von UDP DNS Paketen (Port 53)
    • Dies Hilft bei der Abwehr von non-DNS DDoS Angriffen gegen einen autoritativen DNS Resolver

9.16.3 UDP Pakete Verwerfen mit Ausnahme von DNS (mittels XDP/eBPF)

  • Installiere die BCC Tools und die Linux Kernel Header für den aktuell laufenden Kernel (wenn die Linux Kernel Header nicht gefunden werden, ist der laufende Kernel nicht mehr aktuell. In diesem Fall die virtuelle Maschine neu starten/reboot und danach die Linux Kernel Header installieren)
% apt install bpfcc-introspection  bpfcc-tools python3-bpfcc
% apt install linux-headers-$(uname -r)
  • BIND 9 DNS Server installieren
    virtuelle maschine% apt install bind9
    
  • Speichere den folgenden C Quellcode in die Datei drop-non-dns-udp.c. Dieses eBPF Programm wird
    • für jeden Netzwerkpaket ausgeführt
    • gibt den Text got a packet für jedes Paket aus
    • extrahiert die IP und UDP Header aus dem Netzwerkpaket
    • wenn weder der UDP Quell-Port noch der UDP Ziel-Port den Wert 53 beinhalten (DNS) wird der Return-Code XDP_DROP zurückgegeben und damit das Paket verworfen. Eine Nachricht informiert über das Verwerfen des Pakets
    • alle anderen Netzwerk-Pakete werden mit dem Rückgabewert XDP_PASS an den Linux TCP/IP Stack weitergeleitet
    • XDP Programme in Produktions-Qualität sollten statt der Funktion bpf_trace_printk die ePBF Map-Strukturen zum Informationsaustausch mit dem User-Space Programm benutzen
#define KBUILD_MODNAME "filter"
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <linux/udp.h>

int udpfilter(struct xdp_md *ctx) {
  bpf_trace_printk("got a packet\n");
  void *data = (void *)(long)ctx->data;
  void *data_end = (void *)(long)ctx->data_end;
  struct ethhdr *eth = data;
  if ((void*)eth + sizeof(*eth) <= data_end) {
    struct iphdr *ip = data + sizeof(*eth);
    if ((void*)ip + sizeof(*ip) <= data_end) {
      if (ip->protocol == IPPROTO_UDP) {
        struct udphdr *udp = (void*)ip + sizeof(*ip);
        if ((void*)udp + sizeof(*udp) <= data_end) {
          if ((udp->dest != ntohs(53)) && (udp->source != ntohs(53))) {
            if (udp->dest != udp->source) {
              bpf_trace_printk("drop udp src/dest port %d/%d\n", ntohs(udp->source), ntohs(udp->dest));
              return XDP_DROP;
            }
          }
        }
      }
    }
  }
  return XDP_PASS;
}
  • Speichere das eBPF Ladeprogramm (geschrieben in Python) in die Datei drop-non-dns-udp.py.
    • Dieses Ladeprogramm compiliert das obige ePBF Programm, lädt dieses in den Linux-Kernel und verbindet es mit der Loopback Netzwerk-Schnittstelle (lo)
    • Während des Übersetzen des C-Quellcodes in eBPF werden Warnungen welche wir für dieses Beispiel ignorieren können
    • Der virtio Netzwerk-Treiber auf den virtuellen Lab-Maschinen erlaubt keine eBPF Programme auf den Netzwerk-Schnittstellen eth0 und eth1, daher testen wir mit der Loopback lo Schnittstelle
#!/usr/bin/env python3

from bcc import BPF
import time

device = "lo"
b = BPF(src_file="drop-non-dns-udp.c")
fn = b.load_func("udpfilter", BPF.XDP)
b.attach_xdp(device, fn, 0)

try:
  b.trace_print()
except KeyboardInterrupt:
  pass

b.remove_xdp(device, 0)
  • Das Ladeprogramm ausführbar machen
% chmod +x drop-non-dns-udp.py
  • Das Python Lade-Programm ausführen (die Warnungen können bei diesem Beispiel ignoriert werden)
% ./drop-non-dns-udp.py
  • Verbinde Dich mit der Lab-Maschine mit einer neuen Sitzung (via tmux oder mit einem weiteren Login)
  • Starte DNS Anfragen an den BIND 9 DNS Server über die Loopback Schnittstelle. Diese Anfragen sollten nicht geblockt werden.
% dig @localhost isc.org
  • Senden wir UDP Pakete (DNS oder andere Protokolle) an einen Port ungleich 53 so werden diese Pakete noch vor dem Linux TCP/IP Stack verworfen
% dig -p 5353 @localhost isc.org

9.16.4 XDP Firewall

  • Die XDP Firewall ist ein neues Projekt welches mittels XDP und eBPF eine generische Firewall erzeugt
interface = "eth0";
updatetime = 15;

filters = (
    {
        enabled = true,
        action = 0,
        udp_enabled = true,
        udp_dport = 53
    }
);

9.17 Literatur und Links

9.17.1 Buch: Linux Observability with BPF

Von David Calavera, Lorenzo Fontana (November 2019)

book1.png

9.17.2 Buch: Systems Performance (2nd ed.)

Von Brendan Gregg (Dezember 2020)

book2.jpg

9.17.3 Buch: BPF Performance Tools

Von Brendan Gregg (Dezember 2019)

book3.jpg

9.17.4 Links

  1. eBPF
  2. BCC
  3. bpftrace
  4. Network Scripts
  5. eBPF Prometheus exporter
  6. eXpress Data Path (XDP)

10 DNS Rebind protection

  • Authoritative DNS Servers in the Internet should not return private IPv4 (RFC 1918) or private IPv6 Addresses (link-local, local multicast or unique-local addresses)
  • see Protecting browsers from dns rebinding attacks https://dl.acm.org/doi/10.1145/1315245.1315298
  • BIND 9 supports rebind protection since Version 9.7.0.
  • the option deny-answer-addresses lists the address ranges that are considered private and should not be returned as part of an A/AAAA response, except for the zones listed in except-from:
options {
 [...]
 deny-answer-addresses {
    10.0.0.0/8;        // RFC 1918
    172.16.0.0/12;     // RFC 1918
    192.168.0.0/16;    // RFC 1918
    127.0.0.0/8;       // Loopback IPv4 (careful!)
    ::1/128;           // Loopback IPv6 (careful!)
    fe80::/10;         // IPv6 Link-Local
    fc00::/7;          // IPv6 Unique-Local
   }
   except-from {
     "example.com";
     "internal.example.net";
     "mylocalnet.example";
   };
};
  • Care should be particularly taken if using this option for addresses within 127.0.0.0/8. These addresses are obviously "internal", but many applications conventionally rely on a DNS mapping from some name to such an address. Filtering out DNS records containing this address spuriously can break such applications.

11 Extern sichtbare Informationen über interne Netzwerk-Strukturen

11.1 AMASS / Discovery (Attack Surface Mapping and Asset Discovery)

11.1.2 AMASS unter Debian installieren

wget https://github.com/OWASP/Amass/releases/download/v3.13.4/amass_linux_amd64.zip
unzip amass_linux_amd64.zip
cd ~/amass_linux_amd64
sudo cp amass /usr/local/bin

11.1.3 AMASS Beispiele

  • Scan von Domain-Namen
amass enum -ip -d example.com,example.de -active -brute  -src
  • AMASS Ergebnisse mit NMAP prüfen
amass enum -d example.com,example.de | tee ./example.domains
nmap -iL example.domains -sL -sn -oN - --dns-servers 172.22.1.1 | grep -v "Failed" | grep -v "Other addresses" | cut -d ' ' -f 5 | tee example_activehosts_ipv4.txt
nmap -6 -iL example.domains -sL -sn -oN - --dns-servers 172.22.1.1 | grep -v "Failed" | grep -v "Other addresses" | cut -d ' ' -f 5 | tee example_activehosts_ipv6.txt
sudo nmap -O -v -sV -sC -p80,8080,443,8443,8181 -Pn -iL example_activehosts_ipv4.txt
sudo nmap -6 -O -v -sV -sC -p80,8080,443,8443,8181 -Pn -iL example_activehosts_ipv6.txt

12 CAA Record

  • der CAA-DNS-Record (RFC 6844) beschreibt, welche Zertifizierungsstelle (CA) für eine Domain x509 Zertifikate ausstellen darf
  • der CAA-Record listet den Namen der CA für "normale" und für Wildcard Zertifikate
; <<>> DiG 9.11.11-RedHat-9.11.11-1.fc31 <<>> defaultroutes.de caa
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 64650
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;defaultroutes.de.                  IN      CAA

;; ANSWER SECTION:
defaultroutes.de.           0       IN      CAA     128 issue "letsencrypt.org"
defaultroutes.de.           0       IN      CAA     128 issuewild ";"
defaultroutes.de.           0       IN      CAA     128 iodef "mailto:security@defaultroutes.de"

;; Query time: 35 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Free Dez 06 07:26:59 CET 2019
;; MSG SIZE  rcvd: 146

  • der CAA-Record wird von der CA zum Zeitpunkt des Austellen des Zertifikats geprüft
  • Goolge hat die Prüfung des CAA-Records für CAs ab September 2017 für verbindlich erklärt
  • Mit dem IODEF Parameter können verstösse gegen die Policy an den Besitzer der Domain gemeldet werden, dies ist jedoch bei den meisten CAs nicht implementiert
  • Existiert kein CAA-Record im DNS, so darf jede CA ein x509 Zertifikat für die Domain ausstellen
  • ein CAA-Record verhindert, das Zertifikate von unbefugten Personen oder von Zertifizierungstsellen mit schlechter Identitätsprüfung des Domain-Besitzers ein Zertifikat austellen kann
  • die Domain mit CAA-Records sollte mit DNSSEC abgesichert sein

13 IPSec mit StrongSWAN

13.1 Installation

  • für die Übung die iptables/nftables Firewall auf dem router ausschalten, Masquerading NAT für die Paketinstallation aber angeschaltet lassen
  • OpenVPN (wenn aktiv) auf Client/Server beenden
  • in einem separaten Terminal-Fenster auf dem Router ein tcpdump für icmp laufen lassen, um die Kommunikation zwischen Client und Server zu beobachten
router# tcpdump -i enp0s8 icmp
  • auf dem Client und auf dem Server das Paket strongswan installieren
apt install strongswan

13.2 Konfiguration auf dem Client

  • Host-to-Host Tunnel Mode (Client) /etc/ipsec.conf
config setup

conn %default
     ikelifetime=60m
     keylife=20m
     rekeymargin=3m
     keyingtries=1
     keyexchange=ikev2
     authby=secret

conn host-host
     left=172.16.x.2
     right=100.64.x.2
     auto=add
  • Passwort (PSK) Client /etc/ipsec.secrets
: PSK 'sharedsecret'

13.3 Konfiguration auf dem Server

  • Host-to-Host Tunnel Mode (Server) /etc/ipsec.conf
config setup

conn %default
     ikelifetime=60m
     keylife=20m
     rekeymargin=3m
     keyingtries=1
     keyexchange=ikev2
     authby=secret

conn host-host
     left=100.64.x.2  # Konfig auf dem Server
     right=172.16.x.2 # Konfig auf dem Server
     auto=add
  • Passwort (PSK) Server /etc/ipsec.secrets
: PSK 'sharedsecret'

13.4 IPSec starten

  • IPSec Daemon starten (auf beiden Rechnern Server/Client)
ipsec restart
  • Status der Verbindungen abfragen (derzeit ist noch keine Verbindung aktiv)
ipsec status
ipsec statusall
  • IPSec Verbindung starten
ipsec up host-host
ipsec status
ipsec statusall
  • ein ping zwischen Client und Server VM sollte weiterhin möglich sein, jetzt aber auf dem Router mit tcpdump nicht mehr sichtbar sein
  • IPSec Status aus dem Linux-Kernel anzeigen
ip xfrm state

13.5 ESP Pakete anschauen

  • auf dem Router sollten wir nun ESP (IPSec Encasulated Security Payload) sehen
# tcpdump -i enp0s8  not port ssh

13.6 IPSec Daten im Wireshark entschlüsseln

  • Um per IPSec geschützte Daten im Wireshark entschlüsseln zu können, werden für jede Richtung (Security Association) benötigt: Quell-Adresse, Ziel-Adresse, SPI, Verschlüsselungs-Algorithmus, Verschlüsselungs-Schlüssel, Authentisierungs-Algorithmus und Schlüssel für die Authentisierung. Diese Daten werden auf einem Linux-Rechner mit aktiver IPSec Verbindung mit dem Befehl ip xfrm state angezeigt:
root@client05:~# ip xfr state
src 172.16.5.2 dst 100.64.5.2
        proto esp spi 0xc3d57e0e reqid 3 mode tunnel
        replay-window 0 flag af-unspec
        auth-trunc hmac(sha256) 0xebd9e051b05a07c89e51d4840ab2df87d8ca107385cee1bc291d0dd442293dd3 128
        enc cbc(aes) 0xd5719f2b10cd766bb7562099c0ee4bb5
        anti-replay context: seq 0x0, oseq 0x5, bitmap 0x00000000
src 100.64.5.2 dst 172.16.5.2
        proto esp spi 0xcf12dd2b reqid 3 mode tunnel
        replay-window 32 flag af-unspec
        auth-trunc hmac(sha256) 0xc82360181bc299d6931cc8061508740d5d25216f6b31ca93c436b65d27072c2e 128
        enc cbc(aes) 0x4e8a18060003b6727d8f8509e98310c5
        anti-replay context: seq 0x5, oseq 0x0, bitmap 0x0000001f
  • Diese Daten im Wireshark unter Edit -> Preferences -> Protocols -> ESP -> ESP SAs -> Edit eintragen. Alle Felder müssen gefüllt werden. Die Schlüssel werden in der hexadezimalen Schreibweise mit vorangestelltem 0x (wie vom ip xfrm state Befehl ausgegeben) eingetragen.
  • Der Packet Trace im Wireshark wird nun dekodiert, die per IPSec verschlüsselten Daten von unverschlüsselten Protokollen (z.B. HTTP, SMTP etc) können analysiert werden.

14 OpenVPN

  • 30 Minuten
  • wir arbeiten wieder auf den Client/Server VMs
  • sicherstellen das die Firewall-Filter-Regeln auf dem Router nicht aktiv ist, das Masquerading nach außen in die Linuxhotel Netz aber aktiv ist (wird für die Installation von OpenVPN über den Paketmanager benötigt):
iptables -A POSTROUTING -t nat -o router3-eth0 -j MASQUERADE
  • Sicherstellen, das in der Datei /etc/resolv.conf auf dem Server und dem Client ein funktionierender DNS-Resolver eingetragen ist (z.B. nameserver 8.8.8.8 oder nameserver 1.1.1.1)
  • OpenVPN kann verschiedene Arten der Autentifizierung (Assymmetrische Schlüssel, x509 Zertifikate) verwenden, in unserem Beispiel benutzen wir einfache kryptografische Schlüssel (PSK oder Pre-Shared-Keys)
  • In unserem Beispiel werden die IP-Adressen der OpenVPN Teilnehmer manuell verteilt. Es ist auch möglich, die IP-Adressen der VPN-Clients über das OpenVPN-Gateway zu verteilen.
  • Schritt 1: OpenVPN auf dem Client und auf dem Server installieren
    • Auf dem Server
server% apt install openvpn
  • Auf dem Client
client% apt install openvpn
  • Die Schlüssel erzeugen und per gesicherten Kanal auf beide Endpunkte (hier zusätzlich zum Client auch auf den Server) des VPN-Tunnels bringen (z.B. per Secure Copy "scp")
client% cd /etc/openvpn
client% openvpn --genkey --secret static.key
client% scp static.key nutzer@100.64.1.2:/tmp
  • Server-Konfiguration /etc/openvpn/server.conf (auf dem Server-Rechner)
dev tun
ifconfig 10.0.1.1 10.0.1.2
secret /etc/openvpn/static.key
  • Auf dem Server den geheimen Schlüssel aus dem Verzeichnis /tmp in das OpenVPN Verzeichnis verschieben
server% mv /tmp/static.key /etc/openvpn/
  • Client-Konfiguration /etc/openvpn/client.conf (auf dem Client-Rechner)
remote 100.64.1.2
dev tun
ifconfig 10.0.1.2 10.0.1.1
secret /etc/openvpn/static.key
  • Treiber für Tunnel-Interface laden und OpenVPN start (Server)
server% modprobe tun
server% openvpn /etc/openvpn/server.conf &
  • Auf dem Server einen Webserver (Port 8000) starten
server% python3 -m http.server 8000
  • OpenVPN start (Client)
client% modprobe tun
client% openvpn /etc/openvpn/client.conf &
  • Tunnel-Interfaces anzeigen (Es sollte eine Netzwerkschnittstelle tun0 mit der 10.0.1.x Adresse existieren)
client% ip a
  • OpenVPN per ICMP ping und HTTP-Anfrage testen (mit tcpdump auf dem Router die Pakete anschauen tcpdump -i router2-eth0 port 80 und tcpdump -i router2-eth0 icmp )
client% ping 10.0.1.1
client% links http://10.0.1.1:8000

15 WireGuard

  • Plan: VPN vom Laptop zur VM im Internet, alle Pakete vom Laptop sollen über das VPN und die VM im Internet gesendet werden

15.1 Wireguard Debian 10 Installation (VPN-Client/VPN-Server)

  • Installationsinformationen für Wireguard https://www.wireguard.com/install/
  • Seit Kernel 5.6 ist Wireguard im Linux-Kernel verfügbar (unser Debian 10 läuft aber noch mit einem 4.x Linux Kernel)
  • der Laptop ist unser Wireguard-Client
  • auf dem Laptop und auf dem VPS Server im Internet das Wireguard Repository hinzufügen, Paketlisten updaten und Wireguard-Pakete installieren
    • der Reboot nach dem apt upgrade ist notwendig wenn ein neuer Linux-Kernel installiert wurde, da die Wireguard-Module abhängig von der Linux-Kernel Version sind und die Wireguard-Installation (Schritt nach dem Reboot) mit dem aktuellen Kernel erfolgen muss
echo "deb http://deb.debian.org/debian/ unstable main" > /etc/apt/sources.list.d/unstable.list
printf 'Package: *\nPin: release a=unstable\nPin-Priority: 90\n' > /etc/apt/preferences.d/limit-unstable
apt update
apt upgrade
reboot
apt install wireguard linux-headers-$(uname -r)

15.2 Wireguard Debian 11 Installation

  • Unter Debian 11 können wir Wireguard direkt aus den Paketquellen installieren
apt install wireguard

15.3 Konfiguration Wireguard auf dem VPN-Server (Gateway)

  • User-Mask für neue Dateien und Verzeichnisse setzen, ein Verzeichnis für die Wireguard-Dateien erstellen
umask 077
mkdir -p /etc/wireguard
cd /etc/wireguard
  • einen privaten Schlüssel für den Wireguard-Dienst erstellen
wg genkey > private.key
  • Wireguard-Interface erstellen
ip link add dev wg0 type wireguard
  • IP-Adresse auf dem neuen Wireguard-Interface wg0 setzen
ip addr add 10.0.0.2/24 dev wg0
  • Privaten Schlüssel in das wg0 Interface laden
wg set wg0 private-key ./private.key
  • Interface wg0 aktivieren
ip link set wg0 up
  • Konfiguration der Schnittstelle anzeigen
wg show wg0

15.4 Konfiguration Wireguard auf dem VPN-Client

  • Der 2te virtuelle Maschine ist unser VPN-Client
  • Wireguard installieren.
apt install wireguard
  • Verzeichnis für die Wireguard-Dateien erstellen
umask 077
mkdir /etc/wireguard
cd /etc/wireguard
  • Einen privaten Schlüssel für den Wireguard-Dienst erstellen
wg genkey > private.key
  • Wireguard-Interface erstellen
ip link add dev wg0 type wireguard
  • IP-Adresse auf das neue Wireguard-Interface wg0 setzen
ip addr add 10.0.0.1/24 dev wg0
  • Privaten Schlüssel in das wg0 Interface laden
wg set wg0 private-key ./private.key
  • Interface wg0 aktivieren
ip link set wg0 up
  • öffentlicher Schlüssel, erlaubte IP-Adressen und IP/Port des VPN-Servers eintragen
wg set wg0 peer <schlüssel> allowed-ips 0.0.0.0/0 endpoint <vpn-server>:<port>
  • Konfiguration der Schnittstelle anzeigen
wg show wg0
  • Konfiguration in die Konfigurationsdatei speichern
wg showconf wg0 > /etc/wireguard/wg0.conf
chmod 0600 /etc/wireguard/wg0.conf
  • Konfigurationsdatei anpassen, Address einfügen und Abschnitt Peer
[Interface]
ListenPort = <port>
PrivateKey = <private-key>
Address = 10.0.0.1/24

[Peer]
PublicKey = <public-key-of-vpn-server>
AllowedIPs = 0.0.0.0/0
Endpoint = <ip-of-vpn-server>:<port>

15.5 Client auf dem Server erlauben

  • Auf dem VPN-Server, die Wireguard-Configuration speichern
wg showconf wg0 > /etc/wireguard/wg0.conf
chmod 0600 /etc/wireguard/wg0.conf
  • Konfigurationsdatei anpassen (Address und Abschnitt Peer)
[Interface]
ListenPort = <port>
PrivateKey = <private-key>
Address = 10.0.0.2/24

[Peer]
PublicKey = <public-key-of-client>
AllowedIPs = 10.0.0.1/32

15.6 Wireguard manuell aktivieren

  • auf dem VPN-Gateway
vm0X:/etc/wireguard# wg-quick down wg0
[#] ip link delete dev wg0
vm0X:/etc/wireguard# wg-quick up wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip link set mtu 1420 up dev wg0
[#] ip route add 10.0.0.1/32 dev wg0
  • auf dem VPN-Client
vm0Y% /etc/wireguard# wg-quick down wg0
[#] ip link delete dev wg0
vm0Y%/etc/wireguard# wg-quick up wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip link set mtu 1420 up dev wg0
[#] wg set wg0 fwmark 51820
[#] ip -4 route add 0.0.0.0/0 dev wg0 table 51820
[#] ip -4 rule add not fwmark 51820 table 51820
[#] ip -4 rule add table main suppress_prefixlength 0

15.7 VPN Test

  • Ping vom VPN-Client Wireguard-IP (Laptop) zum VPN-Server
ping 10.0.0.2

15.8 Wireguard per systemd starten

  • die folgenden Schritte sowohl auf dem VPN-Gateway, als auch auf dem Client ausführen
  • manuell gestartetes Wireguard beenden
wg-quick down wg0
  • Wireguard per Systemd aktivieren
systemctl enable wg-quick@wg0
systemctl start wg-quick@wg0
systemctl status wg-quick@wg0
● wg-quick@wg0.service - WireGuard via wg-quick(8) for wg0
   Loaded: loaded (/lib/systemd/system/wg-quick@.service; enabled; vendor preset: enabled)
   Active: active (exited) since Mon 2019-05-06 16:18:02 EDT; 1s ago
     Docs: man:wg-quick(8)
	   man:wg(8)
	   https://www.wireguard.com/
	   https://www.wireguard.com/quickstart/
	   https://git.zx2c4.com/WireGuard/about/src/tools/man/wg-quick.8
	   https://git.zx2c4.com/WireGuard/about/src/tools/man/wg.8
  Process: 13492 ExecStart=/usr/bin/wg-quick up wg0 (code=exited, status=0/SUCCESS)
 Main PID: 13492 (code=exited, status=0/SUCCESS)

May 06 16:18:02 router05 systemd[1]: Starting WireGuard via wg-quick(8) for wg0...
May 06 16:18:02 router05 wg-quick[13492]: [#] ip link add wg0 type wireguard
May 06 16:18:02 router05 wg-quick[13492]: [#] wg setconf wg0 /dev/fd/63
May 06 16:18:02 router05 wg-quick[13492]: [#] ip address add 10.0.0.1/24 dev wg0
May 06 16:18:02 router05 wg-quick[13492]: [#] ip link set mtu 1420 up dev wg0
May 06 16:18:02 router05 wg-quick[13492]: [#] wg set wg0 fwmark 51820
May 06 16:18:02 router05 wg-quick[13492]: [#] ip -4 route add 0.0.0.0/0 dev wg0 table 51820
May 06 16:18:02 router05 wg-quick[13492]: [#] ip -4 rule add not fwmark 51820 table 51820
May 06 16:18:02 router05 wg-quick[13492]: [#] ip -4 rule add table main suppress_prefixlength 0
May 06 16:18:02 router05 systemd[1]: Started WireGuard via wg-quick(8) for wg0.

15.9 Optional: Alternatives Routing auf dem Client ohne wg-quick

  • Alle Pakete (mit Aussnahme der Wireguard Pakete) sollen durch den VPN-Tunnel gesendet werden. Eine Split-Default-Route sorgt dafür, das wir die original Default-Route nicht verlieren
ip route add <ip-des-VPN-servers>/32 via 192.168.1.5 dev eth0
ip route add  0.0.0.0/1 via 10.0.0.2 dev wg0
ip route add  128.0.0.0/1 via 10.0.0.2 dev wg0
  • Test
# tracepath google.com
 1?: [LOCALHOST]                                         pmtu 1420
 1:  10.0.0.2                                             15.453ms
 1:  10.0.0.2                                             15.446ms
 2:  no reply
 3:  45.63.118.33                                         18.164ms
 4:  no reply
 5:  ffm-b12-link.telia.net                               20.718ms
 6:  ffm-bb4-link.telia.net                               15.889ms
 7:  ffm-b1-link.telia.net                                16.062ms
 8:  google-ic-319727-ffm-b1.c.telia.net                  15.997ms asymm 10
[...]

15.10 Optional: Wireguard mit alternativen Routing auf dem Client ausschalten

  • Routen löschen
ip r del 0.0.0.0/1 via 10.0.0.2 dev wg0
ip r del 128.0.0.0/1 via 10.0.0.2 dev wg0
ip r del <ip-des-vpn-servers> via 192.168.1.5 dev eth0
  • Wireguard Interface ausschalten
ip link set dev wg0 down

15.11 Links

16 TINC

16.1 TINC Funktionen

  • Projekt-Webseite: https://www.tinc-vpn.org/
  • TINC ist ein Mesh-VPN
  • TINC kann im Router (Layer 3) oder Switch Modus (Layer 2) betrieben werden
  • TINC bietet Verschlüsselung, Authentisierung und Komprimierung von Netzwerk-Daten
  • TINC kann auch von Systemen hinter NAT benutzt werden
  • Unterstützt Linux, FreeBSD, OpenBSD, NetBSD, OS X, Solaris, Windows
  • Funktioniert über IPv4 und IPv6, im VPN können IPv4 und IPv6 benutzt werden

16.2 Layout der TINC Konfigurationsdateien

  • Die Konfigurationsdateien einer TINC VPN Konfiguration befindet sich unter /etc/tinc/<vpn-name>.
  • In den Beispielen und Aufgaben in diesem Kapitel wird der Namen lhvpn für das TINC VPN benutzt

16.3 Haupt-Konfiguration eines VPN

  • Zuerst erstellen wir das Verzeichnis für das erste TINC VPN. TINC kann mehere unabhängige VPN Verbindungen erstellen, die jeweils eine separate Konfiguration in einem eigenen Konfigurationsverzeichnis besitzen
  • Wir benutzen den VPN-Namen lhvpn und erstellen das Verzeichnis:
    mkdir -p /etc/tinc/lhvpn
    
  • Inhalt der Konfigurationsdatei unter /etc/tinc/lhvpn/tinc.conf:
    name=notebookXX
    AddressFamily=any
    Device=/dev/net/tun
    Mode=switch
    ConnectTo=vmXX
    ConnectTo=vmYY
    ConnectTo=vmZZ
    

16.4 Host-Definitions-Dateien

  • Jeder TINC VPN-Knoten (Node) bekommt eine Definitionsdatei unter /etc/tinc/lhvpn/hosts/
  • Diese Host-Definitionsdateien müssen als Dateiname den in der TINC-Konfigurationsdatei angegebenen symbolischen Namen besitzen
  • In der Datei werden zwei Parameter konfiguriert:
    • Die IP-Adresse (ausserhalb des VPN) oder der DNS-Domain-Name des Knoten-Rechners
    • Eine IP-Subnetzdefinition für das Netzwerk innerhalb des VPN. In unseren Beispielen benutzen wir das CGN-Netzwerk 100.64.0.0/24 (Carrier-Grade-NAT)
  • Die Hosts-Dateien sind in der Regel auf allen TINC-Knoten Rechters eines VPN identisch
  • Beispiel /etc/tinc/lhvpn/hosts/notebook26
    Address = notebook26.linuxhotel.de
    Subnet = 100.64.0.0/24
    
  • Beispiel /etc/tinc/lhvpn/hosts/vm01
    Address = 46.101.197.220
    Subnet = 100.64.0.0/24
    
  • Zuerst erstellen wir eine Host-Datei für den jeweiligen TINC-VPN-Knoten

16.5 TINC Scripte

  • Ist der VPN Tunnel aufgebaut so ruft TINC das Script tinc-up auf. Dieses Skript konfiguriert die VPN-Netzwerkschnittstelle (die Netzwerkkonfiguration innerhalb des VPN-Tunnels) mit der IP-Adresse. Dieses Skript kann jedoch beliebige Konfigurationsaufgaben im System erledigen
  • Beispiel für eine tinc-up Script-Datei (die IP-Adresse der VPN-Schnittstelle ist auf jedem Knoten unterschiedlich!)
    #!/bin/sh
    ip addr add 100.64.0.1/24 dev $INTERFACE
    ip link set dev $INTERFACE up
    
  • Das Script tinc-down de-konfiguriert die VPN-Schnittstelle wenn der VPN-Tunnel abgebaut wurde
    #!/bin/sh
    ip link set dev $INTERFACE down
    
  • Nicht vergessen die Skripte ausführbar zu machen

16.6 Ein Schlüsselpaar für das VPN erzeugen

  • Ein RSA-Schlüsselpaar für jeden VPN-Knoten wird erzeugt. Dabei wird der private Schlüssel unter /etc/tinc/lhvpn abgelegt, und der öffentliche Schlüssel in die Hosts-Datei des VPN-Knotens geschrieben
    # tincd -n lhvpn -K
    Generating 2048 bits keys:
    .................................+++++ p
    ............................................................+++++ q
    Done.
    Please enter a file to save private RSA key to [/etc/tinc/lhvpn/rsa_key.priv]:
    Please enter a file to save public RSA key to [/etc/tinc/lhvpn/hosts/notebook26]:
    
  • Der private Schlüssel wurde unter /etc/tinc/lhvpn/rsa_key.priv abgelegt:
    # ls -l
    insgesamt 24
    drwxr-xr-x 2 root root 4096 Jul 25 23:15 hosts
    -rw------- 1 root root 3360 Jul 25 23:15 rsa_key.priv
    -rw-r--r-- 1 root root  103 Jul 25 23:15 tinc.conf
    -rwxr-xr-x 1 root root   43 Jul 25 23:10 tinc-down
    -rwxr-xr-x 1 root root   81 Jul 25 23:08 tinc-up
    
  • der öffentliche Schlüssel wurde in die Hosts-Datei des VPN-Knoten geschrieben:
    # cat /etc/tinc/lhvpn/hosts/notebook26
    Address = notebook26.linuxhotel.de
    Subnet = 100.64.0.0/24
    
    -----BEGIN RSA PUBLIC KEY-----
    MIIBCgKCAQEAxIaU7OwiwFa8eXmpAIUhljDPL8MgEf7aZ7KTj2TfFdGMU79/zt5w
    DY1PoXOE7DsBqAtqnYw+nMJ0AORihCrM3OaZCO5dEjbCXpXxR+OSfSXAKNWVpEJO
    NWzgMloG69WB6m2Zpe8s/Jnh7KSaiYZ0zxQ6MR5huVC8EwM4y6dR0hf6aUIdQnEA
    HFxr+wn5tGo9VZnfxatiY9G+YmDvsCqG7AD/fnWRjrWjlJBccmWHtWdyjst1qsjA
    znfZ8hFDDESfEdaSyrxdufJEs9Pqj4pSQFWYHgOdfmiveG+h+Z0zRAmDJwYjnSQ0
    AA6D0EUrP4EYEGlwIbbzlgXwwwaWwzIrzQIDAQAB
    -----END RSA PUBLIC KEY-----
    
  • Nun die Hosts-Dateien mit den öffentlichen Schlüsseln jeweils auf alle anderen VPN-Knoten kopieren

16.7 Den TINC Daemon manuell starten

  • Zur ersten Fehlersuche starten wir den TINC-Daemon auf allen Nodes im Debug-Modus. Der Parameter -n wählt die VPN-Konfiguration aus
    # tincd -Dd -n lhvpn
    tincd 1.0.35 starting, debug level 1
    /dev/net/tun is a Linux tun/tap device (tap mode)
    Listening on 0.0.0.0 port 655
    Listening on :: port 655
    Ready
    Trying to connect to vm01 (46.101.197.220 port 655)
    Connected to vm01 (46.101.197.220 port 655)
    Got ALRM signal
    Flushing event queue
    Connection with vm01 (46.101.197.220 port 655) activated
    
  • Erfolgreich konfigurierte VPN-Schnittstelle unter Linux:
    # ip a
    [...]
    5: lhvpn: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 1000
     link/ether b2:40:71:84:65:77 brd ff:ff:ff:ff:ff:ff
     inet 100.64.0.1/24 scope global lhvpn
        valid_lft forever preferred_lft forever
     inet6 fe80::b040:71ff:fe84:6577/64 scope link
        valid_lft forever preferred_lft forever
    
  • Wurde die Verbindung zwischen den VPN-Knoten erfolgreich erstellt, so sollte nun eine IP-Kommunikation über die VPN-Schnittstelle lhvpn möglich sein (z.B. per ping).
    # ping 100.64.0.2
    

16.8 Den TINC Daemon via systemd starten

  • Konnte das Mesh-VPN zwischen allen Knoten-Rechnern erfolreich aufgebaut werden, so kann nun TINC per Systemd aktiviert werden:
    # systemctl enable --now tinc@lhvpn
    Created symlink /etc/systemd/system/tinc.service.wants/tinc@lhvpn.service → /lib/systemd/system/tinc@.service.
    
  • Systemd-Status abfragen:
    # systemctl status tinc@lhvpn
    ● tinc@lhvpn.service - Tinc net lhvpn
    Loaded: loaded (/lib/systemd/system/tinc@.service; enabled; vendor preset: enabled)
    Active: active (running) since Sun 2021-07-25 23:35:29 CEST; 10s ago
      Docs: info:tinc
            man:tinc(8)
            man:tinc.conf(5)
            http://tinc-vpn.org/docs/
    Main PID: 4879 (tincd)
     Tasks: 1 (limit: 4915)
    Memory: 1.2M
    CGroup: /system.slice/system-tinc.slice/tinc@lhvpn.service
            └─4879 /usr/sbin/tincd -n lhvpn -D
    
    Jul 25 23:35:29 notebook26 systemd[1]: Started Tinc net lhvpn.
    Jul 25 23:35:29 notebook26 tincd[4879]: tincd 1.0.35 starting, debug level 0
    Jul 25 23:35:29 notebook26 tincd[4879]: /dev/net/tun is a Linux tun/tap device (tap mode)
    Jul 25 23:35:29 notebook26 tincd[4879]: Ready
    Jul 25 23:35:30 notebook26 tincd[4879]: Got ALRM signal
    Jul 25 23:35:30 notebook26 tincd[4879]: Flushing event queue
    

17 TOR - The Onion Router

17.1 TOR Installieren

  • auf dem Router und auf dem Laptop die Tor-Software installieren
apt install tor

17.2 TOR-Browser

  • auf dem Laptop bei installiertem Tor-Dienst den Socksv5-Proxy im Firefox-Browser auf Port 9050 einstellen
  • Alternativ kann der Tor-Browser des Tor-Projekt installiert werden ( http://www.torproject.org ). Der Tor-Browser hat den Tor-Daemon schon eingebaut, dieser muss dann nicht extra installiert sein

17.3 TOR-Onion-Services

  • auf dem Router in der Tor-Konfiguration einen Onion Hidden-Service auf Localhost Port 8000 einrichten
$EDITOR /etc/tor/torrc
[...]
HiddenServiceDir /var/lib/tor/hidden_service/
HiddenServicePort 80 127.0.0.1:8000
[...]
  • Tor neu starten
systemctl restart tor
systemctl status tor
  • auf dem Router einen Webserver Service starten (einfacher Python Webserver auf Port 8000)
python -m SimpleHTTPServer
  • auf dem Router den Onion-Namen herausfinden (Datei /var/lib/tor/hidden_service/hostname) und an einen der Teilnehmer weitergeben oder selbst im Firefox (bei eingeschaltetem Socks5-Proxy auf Port 9050)
  • den Onion-Dienst mit diesem "Domain"-Namen ansprechen, per Socks-Proxy aus dem Firefox oder mit dem Tor-Browser

17.4 Programme ohne Socks-Support mit Tor benutzen

  • einige Programme (wie ssh oder links) haben keinen Support für Socks5 Proxies
  • mit dem Programm torify können Programm für Tor eingerichtet werden
torify links http://<onion-adresse>.onion

17.5 Sicherheitsprobleme bei Diensten über TOR

  • Einige Server-Dienste schalten spezielle Admin-Funktionen frei, wenn der Server über eine Loopback-Adresse (127.0.0.1) angesprochen wird (z.B. der Apache Webserver –> https://www.heise.de/security/meldung/Apache-verpetzt-moeglicherweise-Tor-Hidden-Services-3090218.html). Bei Bennutzung als Tor-Dienst auf einer Loopback-Adresse sind diese Admin-Funktionen von aussen über das Tor-Netzwerk erreichbar. Lösung: den Dienst auf einer nicht-Loopback Adresse konfigurieren, oder die speziellen Admin-Funktionen in der Server-Konfiguration ausschalten.

17.6 Client-Authentisierung mit Tor Onion Service V3

  • seit Version 0.3.5 der Tor-Software wird ein neues, sicheres Protokoll für die Onion-Services verwendet. Die Konfiguration der Authentisierung von Tor-Clients mit der Version 3 der Onion-Services wird im folgenden beschrieben

17.6.1 Schlüssel erstellen

  • es gibt mehrere Wege, ein Schlüsselpaar (ED25519) für einen Onion-Service zu erstellen
  1. per Python Programm
    • wir installieren python-nacl, eine Python-Bibliothek mit Krypto-Funktionen und laden dann von Github ein kleines Python-Script, welches eine Schlüssel-Paar für einen V3 Onion-Service erstellt:
    apt install python-nacl
    
    #!/usr/bin/env python3
    import base64
    try:
        import nacl.public
    except ImportError:
        print('PyNaCl is required: "pip install pynacl" or similar')
        exit(1)
    
    
    def key_str(key):
        # bytes to base 32
        key_bytes = bytes(key)
        key_b32 = base64.b32encode(key_bytes)
        # strip trailing ====
        assert key_b32[-4:] == b'===='
        key_b32 = key_b32[:-4]
        # change from b'ASDF' to ASDF
        s = key_b32.decode('utf-8')
        return s
    
    
    def main():
        priv_key = nacl.public.PrivateKey.generate()
        pub_key = priv_key.public_key
        print('public:  %s' % key_str(pub_key))
        print('private: %s' % key_str(priv_key))
    
    
    if __name__ == '__main__':
        exit(main())
    
    • ein Aufruf dieses Programms erstellt ein Schlüsselpaar und gibt dieses auf den Bildschirm aus
    # python x25519-gen.py
    public:  5U3RTWSB2BVYSC5UQTNRYHH5COACEJJLNCMDZBETEGGFZZFHIAPQ
    private: KRPY7WFH322QJL4KC3WHXDGV7WAMNVYLIUA65GUEWOOLMUHEIJIA
    

17.6.2 per OpenSSL

  • alternativ kann ein Schlüsselpaar mit OpenSSL erstellt werden
  • zuerst der private Schlüssel
openssl genpkey -algorithm x25519 -out key.private.pem
cat key.private.pem | grep -v " PRIVATE KEY" | base64 -d | tail --bytes=32 | base32 | sed 's/=//g' > private.key
  • und dann der öffentliche Schlüssel
openssl pkey -in key.private.pem -pubout | grep -v " PUBLIC KEY" | base64 -d | tail --bytes=32 | base32 | sed 's/=//g' > public.key

17.6.3 Authentisierung auf der Server-Seite einrichten

  • auf der Seite des Tor-Onion-Dienstes, im Unterverzeichnis authorized_clients des Dienstes eine Datei mit der Endung .auth anlegen. In unserem Beispiel wir die Datei /var/lib/tor/hidden_service/authorized_clients/nutzer.auth angelegt
cd /var/lib/tor/hidden_service/authorized_clients/
$EDITOR nutzer.auth
  • diese Datei enthält des öffentlichen Schlüssel des Clients im Format descriptor:x25519:<public-key>
  • den Tor-Dienst neu starten
systemctl restart tor
  • Nun kann nur noch ein Client, welcher den entsprechenden privaten Schlüssel zu den öffentlichen Schlüsseln des Dienstes hat, auf den Dienst zugreifen. Alle anderen Clients können nicht sehen, ob der Dienst existiert.

17.6.4 Authentisierung auf der Client-Seite einrichten

  • auf dem Tor-Client ein Verzeichnis für die Client-Schlüssel anlegen (hier /var/lib/tor/auth) und die Rechte für die Tor-Software setzen
mkdir /var/lib/tor/auth
chmod 700 /var/lib/tor/auth
chown toranon /var/lib/tor/auth/
  • in diesem Verzeichnis eine Text-Datei mit der Endung .auth_private anlegen. In diese Datei den privaten Schlüsseln für jeden Tor-Dienst eintragen (je eine Zeile pro Dienst). Jeder Eintrag hat das Format <onion-address>:descriptor:x25519:<base32-encoded-private-key>
$EDITOR /var/lib/tor/auth/nutzer.auth_private
  • das Verzeichnis mit den Authentisierungs-Datei(en) auf dem Client in die Konfiguration der Tor-Software eintragen. Datei /etc/tor/torrc:
ClientOnionAuthDir /var/lib/tor/auth

17.7 Vor- und Nachteile von Tor-Onion-Diensten

17.7.1 Vorteile

  • keine Änderungen an der (Heim-)Firewall notwendig. Keine Port-Forwardings etc.
  • kein DynDNS notwendig, um eine wechselne IP-Adresse mit einen Namen zu verbinden. Der Onion-Name wird immer im Tor-Netz gefunden
  • Datenverkehr ist verschlüsselt (Curve25519)
  • Datenverkehr ist anonymisiert

17.7.2 Nachteile

  • Client muss die Tor-Software als Proxy benutzen
  • hohe Latenz (Verzögerung) bei den Paketlaufzeiten

17.8 Links

18 DNSSEC Einführung

18.1 Unbound DNS-Resolver mit DNSSEC

  • wir arbeiten auf der virtuellen Maschine
  • Unbound und die DNS-Tools installieren
apt install unbound dnsutils
  • DNS-Abfrage manuell testen, bei einer bekannt DNSSEC geschützten Domain muss im Header das AD-Flag (Authentic Data) angezeigt werden. Das AD-Flag ist das Signal, das die DNS-Daten DNSSEC abgesichert sind und korrekt überprüft (validiert) wurden:
dig @127.0.0.1 linuxhotel.de a
  • Die Linux-Resolver Konfiguration anpassen, damit alle Anwendungen des Servers über den Unbound DNS-Resolver gehen
echo "nameserver 127.0.0.1" > /etc/resolv.conf
  • DNSSEC Vertrauenskette mit dem Tool drill aus dem Paket ldnsutils anzeigen
apt install ldnsutils
  • Den Root-KSK Schlüssel besorgen und lokal abspeichern
dig @a.root-servers.net dnskey . | grep 257 > root.key
  • DNSSEC Vertrauenskette prüfen und anzeigen
drill -SD -k root.key www.linuxhotel.de a