Sie möchten ein VPN für die Arbeit (Zugriff auf das Unternehmens-LAN über Tunnel) und gleichzeitig soll Netflix Ihre französische IP sehen, damit es den FR-Katalog bereitstellt. Oder Sie möchten, dass Steam direkt herunterlädt (1 Gbps ISP), während alles andere über das VPN läuft. Lösung: Split-Tunnel über Linux-Routing-Tabellen, leistungsstärker als WireGuards AllowedIPs-Split.
Dieser Leitfaden implementiert das fwmark + ip rule + ip route-Muster, das spezifische Anwendungen durch das VPN und andere direkt routet, ohne den WireGuard-Tunnel selbst zu berühren.
Warum Linux-Routing-Table-Split-Tunnel den AllowedIPs-Modus übertrifft
Die WireGuard-Option AllowedIPs ist elegant, wenn Sie ganze IP-Bereiche vom Tunnel ausschließen möchten — typischerweise Ihr lokales LAN (192.168.1.0/24), um NAS- und Druckerzugriff ohne VPN zu behalten. Das reicht für 80% der Heimnutzung. Aber sobald Sie das Routing nach Anwendung oder Systembenutzer differenzieren möchten, kann AllowedIPs das nicht. Steam und Netflix haben keine festen IPs — sie nutzen CDNs mit dynamischen, global verteilten Bereichen. Die Liste der Steam-IPs in AllowedIPs manuell zu pflegen, ist unüberschaubar und bricht bei jedem Akamai- oder Fastly-CDN-Refresh.
Fwmark-Routing löst dies, indem es Pakete markiert, sobald sie den Benutzerprozess verlassen (über cgroup oder uid), bevor die IP-Routing-Entscheidung getroffen wird. Die Routing-Tabelle 200 (VPN) oder main (direkter ISP) wird basierend auf der Markierung gewählt, unabhängig vom Ziel. So können Sie sagen: "Alle Daten vom Benutzer steam gehen über den direkten ISP, alles andere über wg0", ohne jemals eine IP aufzulisten. Das macht Mullvad intern in ihrer Linux-App seit 2022, und das nutzt Tor für ihren Bridge-Modus.
Ein weiterer Vorteil: Die fwmark überlebt Netzwerkänderungen. Wenn Sie von Heim-WLAN zu mobilem 4G wechseln, ohne die Verbindung zu trennen, bleibt Ihr Split-Tunnel funktionsfähig, weil die Routing-Tabelle 200 auf wg0 zeigt (die WireGuard-Schnittstelle, unabhängig von der zugrunde liegenden physischen Schnittstelle). Mit AllowedIPs hätten Sie auch kein Problem, aber mit einem klassischen Netfilter-Kill-Switch könnten Sie mit iptables-Regeln enden, die auf eth0 verweisen, das auf mobilem 4G nicht existiert (Schnittstelle wird wwan0).
Konzept in 4 Schritten
- Markieren Sie den Verkehr einer App mit
iptables -j MARK(basierend auf cgroup, Benutzer oder Ziel). - Erstellen Sie eine alternative Routing-Tabelle für markierten Verkehr (z.B. Tabelle 200).
- Fügen Sie eine ip-rule hinzu, die sagt: "Verkehr mit Markierung X → verwende Tabelle 200".
- Füllen Sie Tabelle 200 mit einer anderen Standardroute (entweder
wg0oder direkteth0).
Ergebnis: Der Kernel prüft jedes ausgehende Paket und wählt Tabelle 200 (VPN) oder Haupttabelle (ISP direkt).
Grundlegende Einrichtung
Voraussetzung: ein konfigurierter, aktiver WireGuard-Tunnel. Siehe den Contabo WireGuard-Einrichtungsleitfaden, wenn Sie von Grund auf neu beginnen.
Überprüfen Sie zuerst, ob iproute2 installiert ist (sollte auf jeder aktuellen Distribution vorhanden sein):
ip -V
# iproute2-6.x.x
Fall A — Steam vom VPN ausschließen (direkte ISP-Downloads)
Steam identifiziert sich über eine cgroup, wenn es von systemd gestartet wird. Wir routen seinen Verkehr über die ISP-Schnittstelle eth0 statt wg0.
Schritt 1 — Erstellen Sie eine cgroup für Steam
sudo mkdir -p /sys/fs/cgroup/net_cls/steam
sudo bash -c 'echo 0x42 > /sys/fs/cgroup/net_cls/steam/net_cls.classid'
Schritt 2 — Markieren Sie ausgehende Pakete aus dieser cgroup
sudo iptables -t mangle -A OUTPUT -m cgroup --cgroup 0x42 -j MARK --set-mark 200
Schritt 3 — Erstellen Sie Routing-Tabelle 200 mit ISP-Route
echo "200 isp_direct" | sudo tee -a /etc/iproute2/rt_tables
ISP_GW=$(ip route | awk '/default/ && /eth0/ {print $3}')
sudo ip route add default via $ISP_GW dev eth0 table isp_direct
sudo ip rule add fwmark 200 table isp_direct
Schritt 4 — Starten Sie Steam in dieser cgroup
cgexec -g net_cls:steam /usr/games/steam
Oder über systemd .slice für Flatpak / Steam Snap Setups.
Steam lädt nun direkt über den ISP (bis zu 1 Gbps Glasfaser), während der Rest Ihres Rechners im WireGuard-Tunnel bleibt.
Fall B — Netflix über ISP für FR-Katalog routen
Sie möchten, dass Netflix Ihre französische IP sieht. Ansatz nach Ziel-IP, da Netflix mehrere CDNs verwendet.
Schritt 1 — Holen Sie sich die Netflix BGP-Bereiche
Netflix veröffentlicht seine Präfixe: AS2906. Sie können sie mit whois -h whois.radb.net -- '-i origin AS2906' abrufen oder eine gepflegte Liste verwenden:
curl -s https://api.bgpview.io/asn/2906/prefixes | jq -r '.data.ipv4_prefixes[].prefix' > /tmp/netflix-ranges.txt
Schritt 2 — Markieren Sie den Verkehr zu diesen Bereichen
while read range; do
sudo iptables -t mangle -A OUTPUT -d "$range" -j MARK --set-mark 200
done < /tmp/netflix-ranges.txt
Schritt 3 — Verwenden Sie die gleiche isp_direct-Tabelle (bereits in Fall A eingerichtet).
Wenn netflix.com aufgelöst wird, verlässt das TCP-Paket mit mark 200 den Kernel über das direkte eth0, Netflix sieht Ihre französische IP und liefert den richtigen Katalog.
Fall C — Umgekehrt: Nur Slack/Zoom durch VPN erzwingen
Sie arbeiten aus dem Ausland und möchten, dass der Arbeitsverkehr (Slack, Zoom, GitHub) über das Unternehmens-VPN läuft, während alles andere direkt geht.
Symmetrische Einrichtung:
- Markierung Standard = direkt
- Markierung Arbeits-Apps = über VPN
echo "201 work_vpn" | sudo tee -a /etc/iproute2/rt_tables
sudo ip route add default dev wg0 table work_vpn
sudo ip rule add fwmark 201 table work_vpn
# Marker für Slack (nach Ziel)
for d in $(dig +short slack.com slack-edge.com app.slack.com); do
sudo iptables -t mangle -A OUTPUT -d "$d" -j MARK --set-mark 201
done
# Zoom (Bereiche, die Zoom in ihren Admin-Dokumenten veröffentlicht)
sudo iptables -t mangle -A OUTPUT -d 3.7.35.0/25 -j MARK --set-mark 201
sudo iptables -t mangle -A OUTPUT -d 3.21.137.128/25 -j MARK --set-mark 201
# ... vervollständigen mit offizieller Zoom-Liste
Wichtig: Die Standardroute der Haupttabelle muss auf eth0 (ISP direkt) und nicht auf wg0 zeigen. Überprüfen Sie dies mit ip route show table main.
Praktisch: Vollständiges split-tunnel.sh-Skript
Um das erneute Eingeben bei jedem Start zu vermeiden, hier ein idempotentes Skript, das mit systemd verbunden ist:
#!/usr/bin/env bash
# /usr/local/bin/split-tunnel.sh
set -euo pipefail
ISP_IF="eth0"
VPN_IF="wg0"
ISP_GW=$(ip route | awk -v iface="$ISP_IF" '/default/ && $0 ~ iface {print $3; exit}')
grep -q '^200 isp_direct' /etc/iproute2/rt_tables || echo "200 isp_direct" >> /etc/iproute2/rt_tables
grep -q '^201 work_vpn' /etc/iproute2/rt_tables || echo "201 work_vpn" >> /etc/iproute2/rt_tables
ip route show table isp_direct | grep -q default || ip route add default via "$ISP_GW" dev "$ISP_IF" table isp_direct
ip route show table work_vpn | grep -q default || ip route add default dev "$VPN_IF" table work_vpn
ip rule show | grep -q "fwmark 0xc8 lookup isp_direct" || ip rule add fwmark 200 table isp_direct
ip rule show | grep -q "fwmark 0xc9 lookup work_vpn" || ip rule add fwmark 201 table work_vpn
mkdir -p /sys/fs/cgroup/net_cls/steam
echo 0x42 > /sys/fs/cgroup/net_cls/steam/net_cls.classid
iptables -t mangle -C OUTPUT -m cgroup --cgroup 0x42 -j MARK --set-mark 200 2>/dev/null \
|| iptables -t mangle -A OUTPUT -m cgroup --cgroup 0x42 -j MARK --set-mark 200
echo "Split-Tunnel-Regeln angewendet."
systemd-Einheit:
# /etc/systemd/system/split-tunnel.service
[Unit]
Description=Split-Tunnel-Routing-Regeln
After=wg-quick@wg0.service
Wants=wg-quick@wg0.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/split-tunnel.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now split-tunnel.service
Fall D — Split nach Linux-Benutzer
Sie möchten, dass der gesamte Verkehr des Benutzers kid (das Konto Ihres Kindes) mit elterlicher Filterung durch das VPN geht, und Ihr Hauptkonto eric direkt.
sudo iptables -t mangle -A OUTPUT -m owner --uid-owner kid -j MARK --set-mark 201
# Markierung 201 wird über VPN geroutet (oben konfiguriert)
Jeder curl, firefox, apt, der unter UID kid gestartet wird, verlässt über wg0. Auch praktisch für einen Testbrowser (Chromium in einem anderen Benutzer), der immer eine andere VPN-IP anzeigt.
Diagnose und Debugging
Aktive Regeln anzeigen
ip rule show
# Sollte Ihre fwmark 200/201 Regel mit der richtigen Priorität anzeigen
ip route show table isp_direct
# Sollte Standard über X dev eth0 anzeigen
ip route show table work_vpn
# Sollte Standard dev wg0 anzeigen
Routing eines Pakets nachverfolgen
# Aus der App-Shell zum Debuggen (oder in cgexec einwickeln)
ip route get 1.1.1.1 mark 200
# Sollte "via 192.168.1.1 dev eth0 table isp_direct" anzeigen
ip route get 1.1.1.1 mark 201
# Sollte "dev wg0 table work_vpn" anzeigen
Die Markierung in tcpdump sehen
sudo tcpdump -i any -nn -e 'iptables' 2>/dev/null
# Oder einfacher:
sudo nft monitor trace
Markierung nicht angewendet?
Überprüfen Sie die Reihenfolge der iptables-Ketten. Die mangle-Tabelle OUTPUT-Kette läuft vor dem Routing. Wenn Ihre Regel in iptables -t mangle -L OUTPUT -v -n angezeigt wird, die Markierung aber nicht haftet, liegt es normalerweise an einer nicht geerbten cgroup (Start über explizites cgexec).
Grenzen und Fallstricke
IPv6
Jeder oben stehende Befehl ist nur für IPv4. Für IPv6 spiegeln Sie mit:
sudo ip6tables -t mangle -A OUTPUT ...
sudo ip -6 rule add fwmark 200 table isp_direct
sudo ip -6 route add default via FE80::1 dev eth0 table isp_direct
Wenn Sie auch unseren Kill-Switch-Leitfaden verwenden, denken Sie daran, dass die Kill-Switch-OUTPUT-DROP-Regeln den direkten Split-Tunnel-Verkehr blockieren können. Lösung: Markierungen 200/201 von der globalen DROP-Regel ausnehmen.
DNS-Leck im Split-Tunnel
Wenn Sie nach Ziel-IP splitten, wird DNS immer noch von Ihrem Haupt-DNS aufgelöst. Wenn dieses DNS im Tunnel ist, kann Netflix auf eine UK-IP auflösen, wenn Sie FR wollten. Lösung: Konfigurieren Sie ein direktes DNS für Split-Domains (dnsmasq mit serverspezifischen Resolvern).
Kernel speichert Routen im Cache
Nach der Änderung einer ip rule führen Sie aus:
sudo ip route flush cache
Andernfalls behalten bereits geöffnete Verbindungen ihr altes Routing.
Docker / Container-Kompatibilität
Docker manipuliert iptables aggressiv. Wenn Sie Split-Tunnel + Docker gemeinsam nutzen, erwarten Sie, dass Regeln bei jedem Docker-Neustart überschrieben werden. Lösung: Starten Sie split-tunnel.service mit After=docker.service oder verwenden Sie nftables über iptables.
Fazit
Split-Tunnel über fwmark + ip rule ist die flexibelste Lösung auf Linux. Sie können nach Ziel, Benutzer, cgroup, Prozess routen. Kosten: ~10 Zeilen Konfiguration und ein systemd-Dienst. Weit überlegen gegenüber WireGuards AllowedIPs-Split, das nur nach Ziel-IP filtert.
Empfohlene Heimkonfiguration:
- VPN-Standard für alles
- Steam, Netflix, Time Machine → markiert
200→ direkter ISP - Netfilter-Kill-Switch mit Markierung 200-Ausnahme
Empfohlene Remote-Arbeitskonfiguration:
- Direkter ISP-Standard
- Slack, Zoom, GitHub Enterprise, Jumphost → markiert
201→ über Unternehmens-VPN
Und für die Grundlage: WireGuard auf Contabo-Leitfaden, vorbereitete Vorlagen und Prometheus + Grafana Monitoring, um in Echtzeit zu verfolgen, was über welche Schnittstelle ausgeht.
Bevor Sie den Split-Tunnel erstellen, verwenden Sie unseren WireGuard-Konfigurationsgenerator, um die Basis wg0.conf richtig zu erstellen — er setzt AllowedIPs und PostUp korrekt, sodass Sie sich auf die Routing-Table-Ebene konzentrieren können. Und wenn Sie zwischen Selbsthosting und einem kommerziellen VPN aus Budgetgründen entscheiden, macht unser Selbstgehosteter VPN-Kostenrechner die 3-Jahres-Rechnung für Sie.
★ Nürnberger DSGVO-Rechenzentrum · ✓ Dedizierte IPv4 inklusive · 200+ Mbps garantiert
Self-host your VPN on your own VPS → ContaboFull root access · public IPv4 · pick your region→