Tôi đang cố gắng thiết lập VPN (sử dụng OpenVPN) sao cho tất cả lưu lượng truy cập và chỉ lưu lượng truy cập đến / từ các quy trình cụ thể đi qua VPN; các quá trình khác nên tiếp tục sử dụng thiết bị vật lý trực tiếp. Theo hiểu biết của tôi thì cách để làm điều này trong Linux là với các không gian tên mạng.
Nếu tôi sử dụng OpenVPN bình thường (nghĩa là kênh tất cả lưu lượng truy cập từ máy khách thông qua VPN), nó hoạt động tốt. Cụ thể, tôi bắt đầu OpenVPN như thế này:
# openvpn --config destination.ovpn --auth-user-pass credentials.txt
(Một phiên bản được định nghĩa lại của Destination.ovpn nằm ở cuối câu hỏi này.)
Tôi bị kẹt ở bước tiếp theo, viết các tập lệnh giới hạn thiết bị đường hầm vào không gian tên. Tôi đã thử:
Đặt thiết bị đường hầm trực tiếp trong không gian tên với
# ip netns add tns0 # ip link set dev tun0 netns tns0 # ip netns exec tns0 ( ... commands to bring up tun0 as usual ... )
Các lệnh này thực thi thành công, nhưng lưu lượng được tạo bên trong không gian tên (ví dụ với
ip netns exec tns0 traceroute -n 8.8.8.8
) rơi vào lỗ đen.Giả định rằng " bạn chỉ có thể [chỉ] giao các giao diện Ethernet ảo (veth) cho một không gian tên mạng " (mà nếu đúng, sẽ nhận giải thưởng năm nay cho hầu hết các hạn chế API không cần thiết một cách lố bịch), tạo ra một cặp veth và cầu nối, và đặt một đầu của cặp veth vào không gian tên. Điều này thậm chí còn không đến mức giảm lưu lượng trên sàn: nó sẽ không cho phép tôi đặt đường hầm vào cầu! [EDIT: Điều này có vẻ là do chỉ các thiết bị nhấn có thể được đưa vào cầu. Không giống như việc không thể đặt các thiết bị tùy ý vào một không gian tên mạng, điều đó thực sự có ý nghĩa, với những cây cầu là một khái niệm lớp Ethernet; thật không may, nhà cung cấp VPN của tôi không hỗ trợ OpenVPN ở chế độ nhấn, vì vậy tôi cần một cách giải quyết.]
# 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
Các kịch bản ở cuối câu hỏi này là dành cho phương pháp veth. Các kịch bản cho cách tiếp cận trực tiếp có thể được tìm thấy trong lịch sử chỉnh sửa. Các biến trong các tập lệnh dường như được sử dụng mà không đặt chúng trước được đặt trong môi trường bởi openvpn
chương trình - vâng, nó cẩu thả và sử dụng tên viết thường.
Vui lòng cung cấp lời khuyên cụ thể về cách làm cho điều này để làm việc. Tôi đau đớn nhận ra rằng tôi đang lập trình bởi giáo phái vận chuyển hàng hóa ở đây - có ai đã viết tài liệu toàn diện cho công cụ này chưa? Tôi không thể tìm thấy bất kỳ - vì vậy đánh giá mã chung của các tập lệnh cũng được đánh giá cao.
Trong trường hợp nó quan trọng:
# 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
Hạt nhân được xây dựng bởi nhà cung cấp hosting ảo của tôi ( Linode ) và, mặc dù được biên soạn với CONFIG_MODULES=y
, không có module thực tế - chỉ CONFIG_*
bộ biến để m
theo /proc/config.gz
được CONFIG_XEN_TMEM
, và tôi không thực sự có rằng mô-đun (kernel được lưu trữ bên ngoài hệ thống tập tin của tôi; /lib/modules
trống rỗng và /proc/modules
chỉ ra rằng nó không được tải một cách kỳ diệu nào đó). Đoạn trích /proc/config.gz
được cung cấp theo yêu cầu, nhưng tôi không muốn dán toàn bộ ở đây.
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
đích.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
liệt kê không có gì, nhưng tôi không biết nếu đó là kết luận. Các phiên bản Linode không có kernel được cài đặt bên trong phân vùng HĐH, vì vậy tôi không chắc mình có thể tải một mô-đun bị thiếu hay không.
lsmod
sản xuất bất kỳ đầu ra ở tất cả? Có một thư mục /lib/modules
?
lsmod: command not found
. Có một /lib/modules
, nhưng nó không có bất kỳ mô-đun nào trong đó, chỉ là một loạt các thư mục per-kernel chứa modules.dep
các tệp trống . Tôi sẽ tìm kiếm sự giúp đỡ cụ thể của Linode và tìm hiểu xem đó có phải là sự thật không.