VPNSmith
self-host-vpnINFO

VPN split-tunnel con tabelle di routing (ip-rule + ip-route) 2026

Split-tunnel Linux tramite fwmark + ip rule: instrada le app attraverso VPN o ISP diretto. Bypass Netflix + casi VPN lavorativi. Script di configurazione e migliori pratiche.

Di Eric Gerard · Fondateur · VPNSmith — Spécialiste self-host VPN & VPS GDPR9 min letturaPhoto via Unsplash

Vuoi una VPN per lavoro (accesso LAN aziendale tramite tunnel) e allo stesso tempo che Netflix veda il tuo IP francese per offrirti il catalogo FR. Oppure vuoi che Steam scarichi direttamente (1 Gbps ISP) mentre tutto il resto passa attraverso la VPN. Soluzione: split-tunnel tramite tabelle di routing Linux, più potente dello split di AllowedIPs di WireGuard.

Questa guida implementa il pattern fwmark + ip rule + ip route che instrada applicazioni specifiche attraverso la VPN e altre direttamente, senza toccare il tunnel WireGuard stesso.

Perché lo split-tunnel con tabelle di routing Linux supera la modalità AllowedIPs

L'opzione AllowedIPs di WireGuard è elegante quando vuoi escludere interi intervalli IP dal tunnel — tipicamente la tua LAN locale (192.168.1.0/24) per mantenere l'accesso a NAS e stampanti senza passare attraverso la VPN. È sufficiente per l'80% degli usi domestici. Ma appena vuoi differenziare il routing per applicazione o per utente di sistema, AllowedIPs non può farlo. Steam e Netflix non hanno IP fissi — usano CDN con intervalli dinamici distribuiti globalmente. Mantenere manualmente l'elenco degli IP di Steam in AllowedIPs è ingestibile e si rompe a ogni aggiornamento CDN di Akamai o Fastly.

Il routing fwmark risolve questo problema marcando i pacchetti mentre escono dal processo utente (tramite cgroup o uid), prima che venga presa la decisione di routing IP. La tabella di routing 200 (VPN) o principale (ISP diretto) viene scelta in base al marchio, indipendentemente dalla destinazione. Così puoi dire "tutto il traffico dall'utente steam esce tramite ISP diretto, tutto il resto passa attraverso wg0" senza mai elencare un IP. È ciò che Mullvad fa internamente nella loro app Linux dal 2022, e ciò che Tor usa per la loro modalità bridge.

L'altro vantaggio: il fwmark sopravvive ai cambiamenti di rete. Se passi dal WiFi di casa al 4G mobile senza disconnetterti, il tuo split-tunnel rimane operativo perché la tabella di routing 200 punta a wg0 (l'interfaccia WireGuard, indipendente dall'interfaccia fisica sottostante). Con AllowedIPs non avresti problemi neanche, ma con un classico kill switch netfilter potresti finire con regole iptables che fanno riferimento a eth0 che non esiste su 4G mobile (l'interfaccia diventa wwan0).

Concetto in 4 passaggi

  1. Marca il traffico di un'app con iptables -j MARK (basato su cgroup, utente o destinazione).
  2. Crea una tabella di routing alternativa per il traffico marcato (es. tabella 200).
  3. Aggiungi una ip-rule dicendo "traffico con marchio X → usa tabella 200".
  4. Popola la tabella 200 con un percorso predefinito diverso (o wg0 o diretto eth0).

Risultato: il kernel ispeziona ogni pacchetto in uscita e sceglie la tabella 200 (VPN) o la tabella principale (ISP diretto).

Configurazione di base

Prerequisito: un tunnel WireGuard configurato e attivo. Consulta la guida alla configurazione di WireGuard su Contabo se parti da zero.

Prima controlla che iproute2 sia installato (dovrebbe esserlo su qualsiasi distribuzione recente):

ip -V
# iproute2-6.x.x

Caso A — Escludere Steam dalla VPN (download diretti ISP)

Steam si identifica tramite un cgroup quando viene lanciato da systemd. Instradiamo il suo traffico tramite l'interfaccia ISP eth0 invece di wg0.

Passaggio 1 — Crea un cgroup per Steam

sudo mkdir -p /sys/fs/cgroup/net_cls/steam
sudo bash -c 'echo 0x42 > /sys/fs/cgroup/net_cls/steam/net_cls.classid'

Passaggio 2 — Marca i pacchetti in uscita da quel cgroup

sudo iptables -t mangle -A OUTPUT -m cgroup --cgroup 0x42 -j MARK --set-mark 200

Passaggio 3 — Crea la tabella di routing 200 con percorso ISP

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

Passaggio 4 — Avvia Steam in quel cgroup

cgexec -g net_cls:steam /usr/games/steam

Oppure tramite .slice di systemd per configurazioni Flatpak / Steam Snap.

Ora Steam scarica direttamente dall'ISP (fino a 1 Gbps in fibra) mentre il resto della tua macchina rimane sul tunnel WireGuard.

Caso B — Instradare Netflix tramite ISP per il catalogo FR

Vuoi che Netflix veda il tuo IP francese. Approccio per IP di destinazione, poiché Netflix utilizza più CDN.

Passaggio 1 — Ottieni gli intervalli BGP di Netflix

Netflix pubblica i suoi prefissi: AS2906. Puoi recuperarli con whois -h whois.radb.net -- '-i origin AS2906' o usare un elenco mantenuto:

curl -s https://api.bgpview.io/asn/2906/prefixes | jq -r '.data.ipv4_prefixes[].prefix' > /tmp/netflix-ranges.txt

Passaggio 2 — Marca il traffico verso quegli intervalli

while read range; do
  sudo iptables -t mangle -A OUTPUT -d "$range" -j MARK --set-mark 200
done < /tmp/netflix-ranges.txt

Passaggio 3 — Usa la stessa tabella isp_direct (già configurata nel caso A).

Successivamente netflix.com, il DNS risolve, il pacchetto TCP parte con mark 200, il kernel invia tramite diretto eth0, Netflix vede il tuo IP francese e offre il catalogo corretto.

Caso C — Inverso: forza solo Slack/Zoom attraverso la VPN

Codice sorgente in un editor di terminale
Codice sorgente in un editor di terminale

Lavori dall'estero, vuoi che il traffico lavorativo (Slack, Zoom, GitHub) passi attraverso la VPN aziendale, tutto il resto diretto.

Configurazione simmetrica:

  • Marca predefinito = diretto
  • Marca app lavorative = tramite 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

# Marcatore per Slack (per destinazione)
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 (intervalli pubblicati da Zoom nei loro documenti amministrativi)
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
# ... completa con l'elenco ufficiale di Zoom

Importante: il percorso predefinito della tabella principale deve puntare a eth0 (ISP diretto), non a wg0. Controlla con ip route show table main.

Pratico: script completo split-tunnel.sh

Per evitare di riscrivere a ogni avvio, ecco uno script idempotente collegato a systemd:

#!/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 "Regole split-tunnel applicate."

unità systemd:

# /etc/systemd/system/split-tunnel.service
[Unit]
Description=Regole di routing split-tunnel
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

Caso D — Split per utente Linux

Vuoi che tutto il traffico dall'utente kid (l'account di tuo figlio) passi attraverso la VPN con filtro parentale, e il tuo principale eric diretto.

sudo iptables -t mangle -A OUTPUT -m owner --uid-owner kid -j MARK --set-mark 201
# Marchio 201 instradato tramite VPN (configurato sopra)

Ogni curl, firefox, apt lanciato sotto UID kid esce tramite wg0. Utile anche per un browser di test (Chromium in un altro utente) che mostra sempre un IP VPN diverso.

Diagnostica e debug

Mostra le regole attive

ip rule show
# Dovrebbe mostrare la tua regola fwmark 200/201 con la giusta priorità
ip route show table isp_direct
# Dovrebbe mostrare default via X dev eth0
ip route show table work_vpn
# Dovrebbe mostrare default dev wg0

Traccia il routing di un pacchetto

# Dalla shell dell'app per il debug (o avvolgi in cgexec)
ip route get 1.1.1.1 mark 200
# Dovrebbe mostrare "via 192.168.1.1 dev eth0 table isp_direct"

ip route get 1.1.1.1 mark 201
# Dovrebbe mostrare "dev wg0 table work_vpn"

Vedi il marchio in tcpdump

sudo tcpdump -i any -nn -e 'iptables' 2>/dev/null
# O più semplice:
sudo nft monitor trace

Marchio non applicato?

Controlla l'ordine delle catene iptables. La catena OUTPUT della tabella mangle viene eseguita prima del routing. Se la tua regola appare in iptables -t mangle -L OUTPUT -v -n ma il marchio non si applica, di solito è un cgroup non ereditato (lancia tramite cgexec esplicito).

Limiti e avvertenze

IPv6

Ogni comando sopra è solo per IPv4. Per IPv6, rispecchia con:

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

Se usi anche la nostra guida al kill switch, ricorda che le regole OUTPUT DROP del kill switch possono bloccare il traffico diretto dello split-tunnel. Correzione: whitelista i marchi 200/201 dal DROP globale.

Perdita DNS nello split-tunnel

Se dividi per IP di destinazione, il DNS viene ancora risolto dal tuo DNS principale. Se quel DNS è nel tunnel, Netflix potrebbe risolvere a un IP UK quando volevi FR. Correzione: configura un DNS diretto per i domini split (dnsmasq con resolver specifici per server).

Il kernel memorizza nella cache i percorsi

Dopo aver modificato una ip rule, esegui:

sudo ip route flush cache

Altrimenti le connessioni già aperte mantengono il loro vecchio routing.

Compatibilità Docker / container

Docker manipola iptables in modo aggressivo. Se coesistono split-tunnel + Docker, aspettati che le regole vengano sovrascritte al riavvio di Docker. Correzione: lancia split-tunnel.service con After=docker.service o usa nftables al posto di iptables.

Verdetto

Lo split-tunnel tramite fwmark + ip rule è la soluzione più flessibile su Linux. Puoi instradare per destinazione, utente, cgroup, processo. Costo: ~10 righe di configurazione e un servizio systemd. Molto superiore allo split di AllowedIPs di WireGuard che filtra solo per IP di destinazione.

Configurazione domestica consigliata:

  • VPN predefinita per tutto
  • Steam, Netflix, Time Machine → marcati 200 → ISP diretto
  • Kill switch netfilter con eccezione per il marchio 200

Configurazione lavorativa remota consigliata:

  • ISP diretto predefinito
  • Slack, Zoom, GitHub Enterprise, jumphost → marcati 201 → tramite VPN aziendale

E per la base: guida a WireGuard su Contabo, template pronti per l'uso, e monitoraggio Prometheus + Grafana per tracciare in tempo reale cosa esce da quale interfaccia.

Prima di costruire lo split-tunnel, usa il nostro generatore di configurazione WireGuard per ottenere il wg0.conf di base corretto — imposta AllowedIPs e PostUp correttamente fin dall'inizio così puoi concentrarti sul livello delle tabelle di routing. E se stai decidendo tra self-hosting e una VPN commerciale per motivi di budget, il nostro calcolatore dei costi VPN self-hosted fa i conti per 3 anni per te.

★ Datacenter GDPR di Norimberga · ✓ IPv4 dedicato incluso · 200+ Mbps garantiti

Self-host your VPN on your own VPS → ContaboFull root access · public IPv4 · pick your region