J'essaie de configurer un VPN (en utilisant OpenVPN) de telle sorte que tout le trafic, et seulement le trafic, vers / depuis des processus spécifiques passe par le VPN; les autres processus devraient continuer d'utiliser directement le périphérique physique. Je crois comprendre que la façon de procéder sous Linux est d'utiliser des espaces de noms réseau.
Si j'utilise OpenVPN normalement (c.-à-d. Acheminer tout le trafic du client via le VPN), cela fonctionne bien. Plus précisément, je démarre OpenVPN comme ceci:
# openvpn --config destination.ovpn --auth-user-pass credentials.txt
(Une version expurgée de destination.ovpn se trouve à la fin de cette question.)
Je suis coincé à l'étape suivante, j'écris des scripts qui limitent le périphérique tunnel aux espaces de noms. J'ai essayé:
Mettre le périphérique tunnel directement dans l'espace de noms avec
# ip netns add tns0 # ip link set dev tun0 netns tns0 # ip netns exec tns0 ( ... commands to bring up tun0 as usual ... )
Ces commandes s'exécutent avec succès, mais le trafic généré à l'intérieur de l'espace de noms (par exemple avec
ip netns exec tns0 traceroute -n 8.8.8.8
) tombe dans un trou noir.En supposant que " vous ne pouvez [encore] affecter que des interfaces Ethernet virtuelles (veth) à un espace de noms de réseau " (ce qui, si cela est vrai, remporte le prix de cette année pour la restriction API la plus ridiculement inutile), créant une paire de veth et un pont, et mettre une extrémité de la paire veth dans l'espace de noms. Cela ne va même pas jusqu'à laisser tomber la circulation sur le sol: cela ne me permettra pas de mettre le tunnel dans le pont! [EDIT: Cela semble être dû au fait que seuls les appareils de prise peuvent être mis en pont. Contrairement à l'incapacité de placer des périphériques arbitraires dans un espace de noms de réseau, cela a du sens, les ponts étant un concept de couche Ethernet; malheureusement, mon fournisseur VPN ne prend pas en charge OpenVPN en mode tap, j'ai donc besoin d'une solution de contournement.]
# 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
Les scripts à la fin de cette question sont pour l'approche veth. Les scripts pour l'approche directe se trouvent dans l'historique des modifications. Les variables dans les scripts qui semblent être utilisées sans les avoir définies au préalable sont définies dans l'environnement par le openvpn
programme - oui, c'est bâclé et utilise des noms en minuscules.
Veuillez offrir des conseils spécifiques sur la façon de faire fonctionner cela. Je suis douloureusement conscient que je programme par cargo culte ici - quelqu'un a -t-il écrit une documentation complète pour ce genre de choses? Je ne trouve aucun - donc la révision générale du code des scripts est également appréciée.
Au cas où cela compte:
# 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
Le noyau a été construit par mon fournisseur d'hébergement virtuel ( Linode ) et, bien que compilé avec CONFIG_MODULES=y
, n'a pas de modules réels - le seul CONFIG_*
ensemble variable m
selon la /proc/config.gz
était CONFIG_XEN_TMEM
, et je ne fait que ce module (le noyau est stocké en dehors de mon système de fichiers; /lib/modules
est vide et /proc/modules
indique qu'il n'a pas été chargé comme par magie). Extraits de /proc/config.gz
fournis sur demande, mais je ne veux pas coller le tout ici.
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
ne répertorie rien, mais je ne sais pas si c'est concluant. Les instances de Linode n'ont pas de noyau installé dans la partition du système d'exploitation, donc je ne suis pas sûr de pouvoir de toute façon charger un module manquant.
lsmod
une sortie? Existe-t-il un répertoire /lib/modules
?
lsmod: command not found
. Il y en a un /lib/modules
, mais il ne contient aucun module , juste un tas de répertoires par noyau contenant des modules.dep
fichiers vides . Je vais fouiller dans l'aide spécifique à Linode et voir si c'est comme ça que ça devrait être.