Saya mencoba mengatur VPN (menggunakan OpenVPN) sedemikian rupa sehingga semua lalu lintas, dan hanya lalu lintas, ke / dari proses tertentu berjalan melalui VPN; proses lain harus terus menggunakan perangkat fisik secara langsung. Ini adalah pemahaman saya bahwa cara untuk melakukan ini di Linux adalah dengan ruang nama jaringan.
Jika saya menggunakan OpenVPN secara normal (yaitu menyalurkan semua lalu lintas dari klien melalui VPN), itu berfungsi dengan baik. Secara khusus, saya memulai OpenVPN seperti ini:
# openvpn --config destination.ovpn --auth-user-pass credentials.txt
(Versi tujuan.ovpn yang sudah dihapus ada di akhir pertanyaan ini.)
Saya terjebak pada langkah berikutnya, menulis skrip yang membatasi perangkat terowongan ke ruang nama. Saya telah mencoba:
Menempatkan perangkat terowongan langsung di namespace dengan
# ip netns add tns0 # ip link set dev tun0 netns tns0 # ip netns exec tns0 ( ... commands to bring up tun0 as usual ... )
Perintah-perintah ini dijalankan dengan sukses, tetapi lalu lintas yang dihasilkan di dalam namespace (misalnya dengan
ip netns exec tns0 traceroute -n 8.8.8.8
) jatuh ke dalam lubang hitam.Dengan asumsi bahwa " Anda [masih] hanya dapat menetapkan antarmuka Ethernet (veth) virtual ke ruang nama jaringan " (yang, jika benar, mengambil penghargaan tahun ini untuk pembatasan API yang sangat tidak perlu), membuat pasangan veth dan jembatan, dan menempatkan salah satu ujung pasangan veth di namespace. Ini bahkan tidak sampai menjatuhkan lalu lintas di lantai: itu tidak akan membiarkan saya memasukkan terowongan ke jembatan! [EDIT: Ini sepertinya karena hanya perangkat tap yang dapat dimasukkan ke jembatan. Berbeda dengan ketidakmampuan untuk menempatkan perangkat yang sewenang-wenang ke dalam namespace jaringan, yang sebenarnya masuk akal, apalagi dengan jembatan yang menjadi konsep Ethernet-layer; sayangnya, penyedia VPN saya tidak mendukung OpenVPN dalam mode tap, jadi saya perlu solusinya.]
# 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
Skrip pada akhir pertanyaan ini adalah untuk pendekatan veth. Skrip untuk pendekatan langsung dapat ditemukan dalam riwayat edit. Variabel dalam skrip yang tampaknya digunakan tanpa mengaturnya terlebih dahulu diatur dalam lingkungan oleh openvpn
program - ya, itu ceroboh dan menggunakan nama huruf kecil.
Harap berikan saran khusus tentang cara agar ini berfungsi. Saya sangat menyadari bahwa saya memprogram dengan pemujaan kargo di sini - adakah yang menulis dokumentasi komprehensif untuk hal ini? Saya tidak dapat menemukan - jadi review kode umum dari skrip juga dihargai.
Dalam hal itu penting:
# 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
Kernel dibangun oleh penyedia hosting virtual saya ( Linode ) dan, walaupun dikompilasi dengan CONFIG_MODULES=y
, tidak memiliki modul yang sebenarnya - satu-satunya CONFIG_*
variabel yang diatur m
menurut /proc/config.gz
adalah CONFIG_XEN_TMEM
, dan saya tidak benar - benar memiliki modul itu (kernel disimpan di luar sistem file saya; /lib/modules
kosong, dan /proc/modules
menunjukkan bahwa itu entah bagaimana tidak dimuat secara ajaib). Kutipan dari yang /proc/config.gz
disediakan berdasarkan permintaan, tetapi saya tidak ingin menempel semuanya di sini.
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
tidak mencantumkan apa pun, tapi saya tidak tahu apakah itu meyakinkan. Instance Linode tidak memiliki kernel yang diinstal di dalam partisi OS, jadi saya tidak yakin saya dapat memuat modul yang hilang.
lsmod
menghasilkan output sama sekali? Apakah ada direktori /lib/modules
?
lsmod: command not found
. Ada /lib/modules
, tetapi tidak memiliki modul di dalamnya, hanya sekelompok direktori per-kernel yang berisi modules.dep
file kosong . Saya akan mencari-cari bantuan Linode khusus dan mencari tahu apakah itu seharusnya.