我正在尝试建立一个VPN(使用OpenVPN),以使去往/来自特定进程的所有流量(仅流量)通过VPN。其他进程应继续直接使用物理设备。据我了解,在Linux中这样做的方法是使用网络名称空间。
如果我正常使用OpenVPN(即通过VPN 集中来自客户端的所有流量),则可以正常工作。具体来说,我像这样启动OpenVPN:
# openvpn --config destination.ovpn --auth-user-pass credentials.txt
(destination.ovpn的简化版本在此问题的结尾。)
我停留在下一步,编写将隧道设备限制为名称空间的脚本。我试过了:
使用以下命令将隧道设备直接放置在名称空间中
# ip netns add tns0 # ip link set dev tun0 netns tns0 # ip netns exec tns0 ( ... commands to bring up tun0 as usual ... )
这些命令执行成功,但是在名称空间内部生成的流量(例如,带有
ip netns exec tns0 traceroute -n 8.8.8.8
)陷入了一个黑洞。假设“ 您只能[仍]仅将虚拟以太网(veth)接口分配给网络名称空间 ”(如果为true,则会获得今年对最可笑的不必要API限制的奖项),从而创建了veth对和网桥,并且将veth对的一端放在名称空间中。这甚至还不至于使人流落在地板上:它不会让我把隧道放到桥上![编辑:这似乎是因为只能将分接头设备放入网桥。与无法将任意设备放入网络名称空间不同,这实际上是有道理的,网桥是以太网层的概念。不幸的是,我的VPN提供商不支持Tap模式的OpenVPN,因此我需要一种解决方法。]
# 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
该问题末尾的脚本适用于第七种方法。直接方法的脚本可以在编辑历史记录中找到。脚本中似乎没有先设置变量就使用的变量是由openvpn
程序在环境中设置的-是的,它很草率并且使用小写名称。
请提供有关如何使其正常工作的具体建议。我痛苦地意识到,我的货物邪教编程这里-已经有人写全面的文档这个东西?我找不到任何内容,因此也欢迎对脚本进行常规代码审查。
如果很重要:
# 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
内核是由我的虚拟主机提供商(Linode)构建的,尽管使用编译CONFIG_MODULES=y
,但没有实际的模块- 根据CONFIG_*
设置的唯一变量是,而我实际上没有该模块(内核存储在文件系统外部;为空,表示它不是以某种方式神奇地加载的)。摘录应要求提供,但我不想在此处粘贴整个内容。m
/proc/config.gz
CONFIG_XEN_TMEM
/lib/modules
/proc/modules
/proc/config.gz
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
没有列出任何内容,但我不知道这是否定论。Linode实例在OS分区内未安装内核,因此我不确定我是否仍可以加载缺少的模块。
lsmod
产生在所有的任何输出?有目录/lib/modules
吗?
lsmod: command not found
。有一个/lib/modules
,但其中没有任何模块,只有一堆包含空modules.dep
文件的每内核目录。我将在Linode特定的帮助中四处寻找,看看是否应该如此。