Sto cercando di configurare una VPN (usando OpenVPN) in modo tale che tutto il traffico, e solo il traffico, verso / da processi specifici passi attraverso la VPN; altri processi dovrebbero continuare a utilizzare direttamente il dispositivo fisico. Comprendo che il modo per farlo in Linux è con gli spazi dei nomi di rete.
Se utilizzo OpenVPN normalmente (ovvero incanalando tutto il traffico dal client attraverso la VPN), funziona perfettamente. In particolare, avvio OpenVPN in questo modo:
# openvpn --config destination.ovpn --auth-user-pass credentials.txt
(Una versione redatta di destination.ovpn è alla fine di questa domanda.)
Sono bloccato sul passaggio successivo, scrivendo script che limitano il dispositivo tunnel agli spazi dei nomi. Ho provato:
Mettere il dispositivo tunnel direttamente nello spazio dei nomi con
# ip netns add tns0 # ip link set dev tun0 netns tns0 # ip netns exec tns0 ( ... commands to bring up tun0 as usual ... )
Questi comandi vengono eseguiti correttamente, ma il traffico generato all'interno dello spazio dei nomi (ad es. Con
ip netns exec tns0 traceroute -n 8.8.8.8
) cade in un buco nero.Partendo dal presupposto che " è possibile [ancora] assegnare solo interfacce Ethernet virtuali (veth) a uno spazio dei nomi di rete " (che, se vero, si aggiudica il premio di quest'anno per la restrizione API ridicolmente superflua), creando una coppia veth e un bridge e mettendo un'estremità della coppia veth nello spazio dei nomi. Questo non arriva nemmeno a far cadere il traffico sul pavimento: non mi permetterà di mettere il tunnel nel ponte! [EDIT: questo sembra essere perché solo i dispositivi di tocco possono essere messi in bridge. A differenza dell'incapacità di inserire dispositivi arbitrari in uno spazio dei nomi di rete, ciò ha effettivamente senso, ad esempio se i bridge sono un concetto di livello Ethernet; sfortunatamente, il mio provider VPN non supporta OpenVPN in modalità tap, quindi ho bisogno di una soluzione alternativa.]
# ip addr add dev tun0 local 0.0.0.0/0 scope link # ip link set tun0 up # ip link add name teo0 type veth peer name tei0 # ip link set teo0 up # brctl addbr tbr0 # brctl addif tbr0 teo0 # brctl addif tbr0 tun0 can't add tun0 to bridge tbr0: Invalid argument
Gli script alla fine di questa domanda sono per l'approccio veth. Gli script per l'approccio diretto possono essere trovati nella cronologia delle modifiche. Le variabili negli script che sembrano essere utilizzate senza prima impostarle sono impostate nell'ambiente dal openvpn
programma - sì, è sciatto e usa nomi minuscoli.
Si prega di offrire consigli specifici su come farlo funzionare. Sono dolorosamente consapevole del fatto che sto programmando da cult cult qui - qualcuno ha scritto una documentazione completa per queste cose? Non ne trovo nessuno, quindi è apprezzata anche la revisione generale del codice degli script.
Nel caso in cui sia importante:
# uname -srvm
Linux 3.14.5-x86_64-linode42 #1 SMP Thu Jun 5 15:22:13 EDT 2014 x86_64
# openvpn --version | head -1
OpenVPN 2.3.2 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [PKCS11] [eurephia] [MH] [IPv6] built on Mar 17 2014
# ip -V
ip utility, iproute2-ss140804
# brctl --version
bridge-utils, 1.5
Il kernel è stato creato dal mio provider di hosting virtuale ( Linode ) e, sebbene compilato CONFIG_MODULES=y
, non ha moduli effettivi - l'unica CONFIG_*
variabile impostata su m
secondo /proc/config.gz
era CONFIG_XEN_TMEM
, e in realtà non ho quel modulo (il kernel è archiviato al di fuori del mio filesystem; /lib/modules
è vuoto e /proc/modules
indica che in qualche modo non è stato caricato magicamente). Estratti da /proc/config.gz
forniti su richiesta, ma non voglio incollare l'intera cosa qui.
netns-up.sh
#! /bin/sh
mask2cidr () {
local nbits dec
nbits=0
for dec in $(echo $1 | sed 's/\./ /g') ; do
case "$dec" in
(255) nbits=$(($nbits + 8)) ;;
(254) nbits=$(($nbits + 7)) ;;
(252) nbits=$(($nbits + 6)) ;;
(248) nbits=$(($nbits + 5)) ;;
(240) nbits=$(($nbits + 4)) ;;
(224) nbits=$(($nbits + 3)) ;;
(192) nbits=$(($nbits + 2)) ;;
(128) nbits=$(($nbits + 1)) ;;
(0) ;;
(*) echo "Error: $dec is not a valid netmask component" >&2
exit 1
;;
esac
done
echo "$nbits"
}
mask2network () {
local host mask h m result
host="$1."
mask="$2."
result=""
while [ -n "$host" ]; do
h="${host%%.*}"
m="${mask%%.*}"
host="${host#*.}"
mask="${mask#*.}"
result="$result.$(($h & $m))"
done
echo "${result#.}"
}
maybe_config_dns () {
local n option servers
n=1
servers=""
while [ $n -lt 100 ]; do
eval option="\$foreign_option_$n"
[ -n "$option" ] || break
case "$option" in
(*DNS*)
set -- $option
servers="$servers
nameserver $3"
;;
(*) ;;
esac
n=$(($n + 1))
done
if [ -n "$servers" ]; then
cat > /etc/netns/$tun_netns/resolv.conf <<EOF
# name servers for $tun_netns
$servers
EOF
fi
}
config_inside_netns () {
local ifconfig_cidr ifconfig_network
ifconfig_cidr=$(mask2cidr $ifconfig_netmask)
ifconfig_network=$(mask2network $ifconfig_local $ifconfig_netmask)
ip link set dev lo up
ip addr add dev $tun_vethI \
local $ifconfig_local/$ifconfig_cidr \
broadcast $ifconfig_broadcast \
scope link
ip route add default via $route_vpn_gateway dev $tun_vethI
ip link set dev $tun_vethI mtu $tun_mtu up
}
PATH=/sbin:/bin:/usr/sbin:/usr/bin
export PATH
set -ex
# For no good reason, we can't just put the tunnel device in the
# subsidiary namespace; we have to create a "virtual Ethernet"
# device pair, put one of its ends in the subsidiary namespace,
# and put the other end in a "bridge" with the tunnel device.
tun_tundv=$dev
tun_netns=tns${dev#tun}
tun_bridg=tbr${dev#tun}
tun_vethI=tei${dev#tun}
tun_vethO=teo${dev#tun}
case "$tun_netns" in
(tns[0-9] | tns[0-9][0-9] | tns[0-9][0-9][0-9]) ;;
(*) exit 1;;
esac
if [ $# -eq 1 ] && [ $1 = "INSIDE_NETNS" ]; then
[ $(ip netns identify $$) = $tun_netns ] || exit 1
config_inside_netns
else
trap "rm -rf /etc/netns/$tun_netns ||:
ip netns del $tun_netns ||:
ip link del $tun_vethO ||:
ip link set $tun_tundv down ||:
brctl delbr $tun_bridg ||:
" 0
mkdir /etc/netns/$tun_netns
maybe_config_dns
ip addr add dev $tun_tundv local 0.0.0.0/0 scope link
ip link set $tun_tundv mtu $tun_mtu up
ip link add name $tun_vethO type veth peer name $tun_vethI
ip link set $tun_vethO mtu $tun_mtu up
brctl addbr $tun_bridg
brctl setfd $tun_bridg 0
#brctl sethello $tun_bridg 0
brctl stp $tun_bridg off
brctl addif $tun_bridg $tun_vethO
brctl addif $tun_bridg $tun_tundv
ip link set $tun_bridg up
ip netns add $tun_netns
ip link set dev $tun_vethI netns $tun_netns
ip netns exec $tun_netns $0 INSIDE_NETNS
trap "" 0
fi
netns-down.sh
#! /bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
export PATH
set -ex
tun_netns=tns${dev#tun}
tun_bridg=tbr${dev#tun}
case "$tun_netns" in
(tns[0-9] | tns[0-9][0-9] | tns[0-9][0-9][0-9]) ;;
(*) exit 1;;
esac
[ -d /etc/netns/$tun_netns ] || exit 1
pids=$(ip netns pids $tun_netns)
if [ -n "$pids" ]; then
kill $pids
sleep 5
pids=$(ip netns pids $tun_netns)
if [ -n "$pids" ]; then
kill -9 $pids
fi
fi
# this automatically cleans up the the routes and the veth device pair
ip netns delete "$tun_netns"
rm -rf /etc/netns/$tun_netns
# the bridge and the tunnel device must be torn down separately
ip link set $dev down
brctl delbr $tun_bridg
destination.ovpn
client
auth-user-pass
ping 5
dev tun
resolv-retry infinite
nobind
persist-key
persist-tun
ns-cert-type server
verb 3
route-metric 1
proto tcp
ping-exit 90
remote [REDACTED]
<ca>
[REDACTED]
</ca>
<cert>
[REDACTED]
</cert>
<key>
[REDACTED]
</key>
grep veth /proc/modules
non elenca nulla, ma non so se sia conclusivo. Le istanze di Linode non hanno un kernel installato all'interno della partizione del sistema operativo, quindi non sono sicuro di poter comunque caricare un modulo mancante.
lsmod
produce alcun risultato a tutti? C'è una directory /lib/modules
?
lsmod: command not found
. C'è un /lib/modules
, ma non ha alcun modulo , solo un mucchio di directory per kernel contenenti modules.dep
file vuoti . Dare un'occhiata in aiuto specifico di Linode e scoprire se è così che dovrebbe essere.