Linux eBPF und DNS

1 Unsere Lab-Umgebung für den Workshop

  • Jeder Teilnehmer bekommt eine virtuelle Maschinen mit Debian 11 vorinstalliert (minimale Installation)
  • Benutzer: user, Password: netzwerk-2022
  • Root-Shell per sudo -i und dem Passwort netzwerk-2022
  • IP Adressen der virtuellen Maschinen:
# Name URL
1 Andreas https://ebpf-001.defaultroutes.org/
2 Oliver https://ebpf-002.defaultroutes.org/
3 Daniel https://ebpf-003.defaultroutes.org/
4 Marko https://ebpf-004.defaultroutes.org/
5   https://ebpf-005.defaultroutes.org/
6   https://ebpf-006.defaultroutes.org/
7   https://ebpf-007.defaultroutes.org/
8   https://ebpf-008.defaultroutes.org/
9   https://ebpf-009.defaultroutes.org/
10   https://ebpf-010.defaultroutes.org/

1.1 Editoren auf den virtuellen Maschinen

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

2 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