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 desmodules.dep
fichiers vides . Je vais fouiller dans l'aide spécifique à Linode et voir si c'est comme ça que ça devrait être.Réponses:
Vous pouvez démarrer le lien OpenVPN à l'intérieur d'un espace de noms, puis exécuter toutes les commandes que vous souhaitez utiliser ce lien OpenVPN à l'intérieur de l'espace de noms. Détails sur la façon de le faire (pas mon travail) ici:
http://www.naju.se/articles/openvpn-netns.html
Je l'ai essayé et ça marche; l'idée est de fournir un script personnalisé pour effectuer les phases de montée et de route de la connexion OpenVPN à l'intérieur d'un espace de noms spécifique au lieu du global. Je cite le lien ci-dessus au cas où il se déconnecterait à l'avenir:
Le seul inconvénient est que vous devez être root pour invoquer
ip netns exec ...
et peut-être que vous ne voulez pas que votre application s'exécute en tant que root. La solution est simple:la source
Il s'avère que vous pouvez mettre une interface de tunnel dans un espace de noms de réseau. Tout mon problème était dû à une erreur lors de l'affichage de l'interface:
Le problème est le "lien de portée", que j'ai mal compris comme n'affectant que le routage. Il oblige le noyau à définir l'adresse source de tous les paquets envoyés dans le tunnel à
0.0.0.0
; vraisemblablement le serveur OpenVPN les rejetterait alors comme invalides selon RFC1122; même si ce n'était pas le cas, la destination ne serait évidemment pas en mesure de répondre.Tout fonctionnait correctement en l'absence d'espaces de noms réseau car le script de configuration réseau intégré à openvpn n'a pas fait cette erreur. Et sans "lien de portée", mon script d'origine fonctionne également.
(Comment ai-je découvert cela, demandez-vous? En exécutant
strace
le processus openvpn, définissez sur hexdump tout ce qu'il a lu dans le descripteur de tunnel, puis décodez manuellement les en-têtes de paquets.)la source
L'erreur sur la tentative de création des périphériques veth est provoquée par un changement de la façon dont
ip
interprète les arguments de la ligne de commande.L'invocation correcte de
ip
pour créer une paire de périphériques Veth est(au
name
lieu dedev
)Maintenant, comment acheminer le trafic de l'espace de noms vers le tunnel VPN? Comme vous ne disposez que de périphériques tun, votre "hôte" doit être acheminé. C'est-à-dire créer la paire veth et en mettre une dans l'espace de noms. Connectez l'autre via le routage au tunnel. Ainsi, activez le transfert, puis ajoutez les itinéraires nécessaires.
Par exemple, supposons qu'il
eth0
s'agit de votre interface principale, detun0
votre interface de tunnel VPN etveth0
/ dontveth1
la paire d'interfaces seveth1
trouve dans l'espace de noms. Dans l'espace de noms, vous ajoutez juste une route par défaut pourveth1
.Sur l'hôte dont vous avez besoin pour utiliser le routage de stratégie, voir ici par exemple. Qu'as tu besoin de faire:
Ajouter / ajouter une entrée comme
à
/etc/iproute2/rt_tables
. Par cela, vous pouvez appeler la table (à créer) par son nom.Utilisez ensuite les instructions suivantes:
Je ne peux pas essayer cela ici avec une configuration comme la vôtre, mais cela devrait faire exactement ce que vous voulez. Vous pouvez augmenter cela par des règles de filtrage de paquets telles que ni le vpn ni le réseau "invité" ne soient perturbés.
NB En
tun0
premier lieu, entrer dans l'espace de noms semble être la bonne chose à faire. Mais comme vous, je ne l'ai pas fait fonctionner. Le routage des stratégies semble être la prochaine chose à faire. La solution de Mahendra est applicable si vous savez que les réseaux derrière le VPN et toutes les autres applications n'accéderont jamais à ces réseaux. Mais votre condition initiale ("tout le trafic, et seulement le trafic vers / depuis des processus spécifiques passe par le VPN") semble que ce dernier ne peut pas être garanti.la source
Si les réseaux auxquels vous accédez via le VPN sont connus, vous pouvez modifier votre table de routage pour obtenir ce que vous voulez.
Notez votre itinéraire par défaut actuel.
# ip route | grep default default via 192.168.43.1 dev wlo1 proto static metric 1024
Exécutez VPN et cela introduira une entrée de routage.
Supprimez la route par défaut actuelle (qui est ajoutée par le VPN) où comme la route par défaut précédente pour être la première entrée par défaut dans le tableau.
# ip route | grep default default dev tun0 scope link default via 192.168.43.1 dev wlo1 proto static metric 1024
# ip route del default dev tun0 scope link
Ajoutez des itinéraires personnalisés aux réseaux qui se trouvent dans le VPN pour acheminer via tun0.
# ip route add <net1>/16 dev tun0
# ip route add <net2>/24 dev tun0
Maintenant, toutes les connexions net1 et net2 passeront par le VPN et la réinitialisation se fera directement (via wlo1 dans cet exemple).
la source