Estoy tratando de configurar una VPN (usando OpenVPN) de modo que todo el tráfico, y solo el tráfico, hacia / desde procesos específicos pase por la VPN; otros procesos deberían continuar usando el dispositivo físico directamente. Tengo entendido que la forma de hacer esto en Linux es con espacios de nombres de red.
Si uso OpenVPN normalmente (es decir, canalizando todo el tráfico del cliente a través de la VPN), funciona bien. Específicamente, comienzo OpenVPN así:
# openvpn --config destination.ovpn --auth-user-pass credentials.txt
(Una versión redactada de destination.ovpn se encuentra al final de esta pregunta).
Estoy atascado en el siguiente paso, escribiendo scripts que restringen el dispositivo del túnel a espacios de nombres. Yo he tratado:
Poner el dispositivo de túnel directamente en el espacio de nombres con
# ip netns add tns0 # ip link set dev tun0 netns tns0 # ip netns exec tns0 ( ... commands to bring up tun0 as usual ... )
Estos comandos se ejecutan con éxito, pero el tráfico generado dentro del espacio de nombres (por ejemplo, con
ip netns exec tns0 traceroute -n 8.8.8.8
) cae en un agujero negro.Suponiendo que " todavía puede [todavía] asignar interfaces virtuales Ethernet (veth) a un espacio de nombres de red " (lo que, de ser cierto, se lleva el premio de este año por la restricción API más ridículamente innecesaria), creando un par veth y un puente, y poniendo un extremo del par veth en el espacio de nombres. Esto ni siquiera llega a dejar caer el tráfico en el piso: ¡no me permitirá poner el túnel en el puente! [EDITAR: Esto parece deberse a que solo los dispositivos de tap se pueden poner en puentes. A diferencia de la incapacidad de colocar dispositivos arbitrarios en un espacio de nombres de red, eso realmente tiene sentido, ya que los puentes son un concepto de capa Ethernet; desafortunadamente, mi proveedor de VPN no admite OpenVPN en modo tap, por lo que necesito una solución 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
Los guiones al final de esta pregunta son para el enfoque veth. Los scripts para el enfoque directo se pueden encontrar en el historial de edición. El programa configura las variables en los scripts que parecen usarse sin establecerlas primero en el entorno openvpn
; sí, es descuidado y usa nombres en minúsculas.
Ofrezca consejos específicos sobre cómo hacer que esto funcione. Soy dolorosamente consciente de que estoy programando por el culto de carga aquí. ¿ Alguien ha escrito documentación exhaustiva para estas cosas? No puedo encontrar ninguno, por lo que también se agradece la revisión general del código de los scripts.
En caso de que importe:
# 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
El kernel fue creado por mi proveedor de alojamiento virtual ( Linode ) y, aunque está compilado CONFIG_MODULES=y
, no tiene módulos reales: la única CONFIG_*
variable establecida de m
acuerdo con /proc/config.gz
fue CONFIG_XEN_TMEM
, y en realidad no tengo ese módulo (el kernel se almacena fuera de mi sistema de archivos; /lib/modules
está vacío e /proc/modules
indica que no se cargó mágicamente de alguna manera). Extractos /proc/config.gz
proporcionados a pedido, pero no quiero pegar todo aquí.
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
no enumera nada, pero no sé si eso es concluyente. Las instancias de Linode no tienen un núcleo instalado dentro de la partición del sistema operativo, por lo que no estoy seguro de poder cargar un módulo faltante de todos modos.
lsmod
Produce alguna salida? ¿Hay un directorio /lib/modules
?
lsmod: command not found
. Hay un /lib/modules
, pero no tiene ningún módulo , solo un montón de directorios por núcleo que contienen modules.dep
archivos vacíos . Buscaré ayuda específica de Linode y averiguaré si así es como se supone que debe ser.