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 berisimodules.dep
file kosong . Saya akan mencari-cari bantuan Linode khusus dan mencari tahu apakah itu seharusnya.Jawaban:
Anda bisa memulai tautan OpenVPN di dalam namespace dan kemudian menjalankan setiap perintah yang Anda ingin gunakan tautan OpenVPN di dalam namespace. Detail tentang cara melakukannya (bukan pekerjaan saya) di sini:
http://www.naju.se/articles/openvpn-netns.html
Saya mencobanya dan berhasil; idenya adalah untuk menyediakan skrip khusus untuk melakukan fase naik dan naik-naik koneksi OpenVPN di dalam namespace tertentu alih-alih yang global. Saya mengutip dari tautan di atas kalau-kalau offline di masa depan:
Satu-satunya tangkapan adalah bahwa Anda harus menjadi root untuk memanggil
ip netns exec ...
dan mungkin Anda tidak ingin aplikasi Anda dijalankan sebagai root. Solusinya sederhana:sumber
Ternyata Anda dapat menempatkan antarmuka terowongan ke namespace jaringan. Seluruh masalah saya karena kesalahan dalam membuka antarmuka:
Masalahnya adalah "scope link", yang saya salah pahami hanya mempengaruhi routing. Ini menyebabkan kernel untuk mengatur alamat sumber dari semua paket yang dikirim ke terowongan
0.0.0.0
; mungkin server OpenVPN kemudian akan membuangnya sebagai tidak valid per RFC1122; bahkan jika tidak, tujuan jelas tidak dapat menjawab.Semuanya berfungsi dengan benar tanpa adanya ruang nama jaringan karena skrip konfigurasi jaringan bawaan openvpn tidak membuat kesalahan ini. Dan tanpa "tautan ruang lingkup", skrip asli saya juga berfungsi.
(Bagaimana saya menemukan ini, Anda bertanya? Dengan menjalankan
strace
pada proses openvpn, setel ke hexdump semua yang dibacanya dari descriptor tunnel, dan kemudian secara manual decoding header paket.)sumber
Kesalahan dalam mencoba membuat perangkat veth disebabkan oleh perubahan cara
ip
menafsirkan argumen baris perintah.Doa yang benar
ip
untuk membuat sepasang perangkat adalah(
name
instaddev
)Sekarang, bagaimana cara mendapatkan traffic dari namespace ke terowongan VPN? Karena Anda hanya memiliki perangkat tun yang Anda inginkan, "host" harus merutekan. Yaitu membuat pasangan veth dan menempatkan satu ke dalam namespace. Hubungkan yang lain melalui perutean ke terowongan. Dengan demikian, aktifkan penerusan, dan kemudian tambahkan rute yang diperlukan.
Sebagai contoh anggaplah itu
eth0
adalah antarmuka utama Anda,tun0
adalah antarmuka terowongan VPN Anda, danveth0
/veth1
sepasang antarmuka yangveth1
ada di namespace. Di dalam namespace Anda menambahkan hanya rute default untukveth1
.Pada host yang Anda butuhkan untuk menggunakan routing kebijakan, lihat di sini misalnya. Apa yang kamu butuhkan:
Tambahkan / tambahkan entri seperti
untuk
/etc/iproute2/rt_tables
. Dengan ini, Anda dapat memanggil tabel (belum dibuat) dengan nama.Kemudian gunakan pernyataan berikut:
Saya tidak dapat mencobanya di sini dengan pengaturan seperti milik Anda, tetapi ini harus melakukan apa yang Anda inginkan. Anda dapat menambahkannya dengan aturan filter paket sehingga vpn atau "guest" net tidak terganggu.
NB Pindah
tun0
ke namespace di tempat pertama sepertinya hal yang tepat untuk dilakukan. Tapi sepertimu, aku tidak berhasil. Perutean kebijakan sepertinya hal yang harus dilakukan selanjutnya. Solusi Mahendra berlaku jika Anda tahu jaringan di belakang VPN dan semua aplikasi lain tidak akan pernah mengakses jaringan itu. Tetapi kondisi awal Anda ("semua lalu lintas, dan hanya lalu lintas, ke / dari proses tertentu melewati VPN") terdengar seolah-olah yang terakhir tidak dapat dijamin.sumber
Jika jaringan yang Anda akses melalui VPN diketahui, Anda dapat mengedit tabel perutean untuk mencapai apa yang Anda inginkan.
Perhatikan rute default Anda saat ini.
# ip route | grep default default via 192.168.43.1 dev wlo1 proto static metric 1024
Jalankan VPN dan ini akan memperkenalkan entri perutean.
Hapus rute default saat ini (yang ditambahkan oleh VPN) di mana sebagai rute default sebelumnya menjadi entri default pertama dalam tabel.
# 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
Tambahkan rute khusus ke jaringan yang ada di VPN untuk merutekan melalui tun0.
# ip route add <net1>/16 dev tun0
# ip route add <net2>/24 dev tun0
Sekarang semua koneksi net1 dan net2 akan melalui VPN dan reset akan langsung (melalui wlo1 dalam contoh ini).
sumber