Rute hanya lalu lintas khusus melalui VPN

11

Saya telah mencari orang-orang sebelumnya yang mengajukan pertanyaan serupa, tetapi belum mendapatkan jawaban langsung yang akan bekerja untuk situasi saya, jadi begini ..

Saya berjalan di Linux (Fedora 22) dan memiliki layanan VPN yang saya bayar, namun saya hanya perlu program khusus untuk menggunakan VPN untuk lalu lintas internet mereka, dan dapat menggunakan koneksi ISP standar saya untuk yang lainnya (mis., Penjelajahan web, dll)

Kami akan membuat ini sederhana dan membatasi ke program yang paling sering digunakan, World of Warcraft, yang dijalankan melalui WINE.

Sekarang, saya memiliki pengaturan VPN melalui antarmuka jaringan sehingga semua lalu lintas saya melalui enp10s0 (nama aneh komputer saya untuk eth0) dapat disalurkan melalui layanan VPN, namun, saya hanya perlu program khusus (atau port yang digunakan oleh program tersebut, untuk spesifik) untuk melalui VPN.

Bagaimana cara mengatur terowongan, dan membuatnya hanya merutekan port yang dibutuhkan melalui VPN, sambil menjaga semua yang lain tidak dialihkan?

Josh Raymond
sumber
Bisakah Anda menjelaskan bagaimana jawaban lain tidak mengatasi masalah ini? Apa yang unik tentang pengaturan Anda?
Paul
1
Hampir setiap dari mereka memiliki cara yang berbeda untuk mencapai ini, dan tidak satupun dari mereka yang membuatnya sama sekali sederhana. Beberapa menggunakan namespace terpisah untuk aplikasi, beberapa menggunakan antarmuka terowongan, beberapa melakukannya langsung melalui openvpn di terminal, namun tidak satupun dari mereka yang saya temukan telah memberi saya semacam metode yang dapat diuraikan untuk melakukan semua ini.
Josh Raymond
Silakan lihat Edit saya
MariusMatutiae

Jawaban:

17

Apa yang Anda minta tidak ada. Inilah sebabnya mengapa Anda tidak puas dengan jawaban yang Anda temukan (beberapa dari mereka, mungkin, menjadi milik saya): semuanya telah menyarankan solusi , bukan solusi asli, baik sederhana atau kompleks.

Biarkan saya jelaskan. Routing di semua OS ditentukan oleh alamat tujuan: Anda mungkin memiliki beberapa rute, tetapi pilihan di antara mereka tidak didasarkan pada aplikasi yang memanggil koneksi, tetapi hanya berdasarkan alamat tujuan. Titik.

Biarkan saya memberi Anda contoh non-sepele. Ketika klien VPN telah membuat koneksi ke servernya, masih mungkin untuk merutekan koneksi ke situs yang diberikan, misalnya example.org, di luar VPN. Tetapi semua aplikasi yang mencoba mencapai alamat khusus itu akan dialihkan ke luar VPN: Anda tidak dapat memiliki beberapa aplikasi yang menuju example.org melalui VPN sementara aplikasi lain lewat di luar VPN.

Situasi menjadi lebih kaya dengan kernel Linux, yang memungkinkan perutean sumber: ini berarti Anda dapat memiliki dua atau lebih tabel perutean, dan pilihan di antara mereka didasarkan pada alamat sumber, bukan alamat tujuan.

Contoh non-sepele: pc saya memiliki dua jalur luar, dengan dua IP publik yang berbeda. Itu dapat dihubungi melalui salah satu antarmuka, dan penting bahwa balasan saya untuk koneksi yang diberikan pergi melalui antarmuka yang sama bahwa koneksi masuk melalui: jika tidak, mereka akan dibuang sebagai tidak relevan ketika mereka mencapai orang yang memulai koneksi. Ini adalah sumber routing.

Cukup adil, bagaimana dengan koneksi yang kita mulai? Beberapa aplikasi memungkinkan Anda menentukan alamat bind, seperti klien openssh :

-b bind_address

Gunakan bind_address pada mesin lokal sebagai alamat sumber koneksi. Hanya berguna pada sistem dengan lebih dari satu alamat.

Bagi mereka, tidak ada masalah dalam memiliki satu instance melalui VPN (katakanlah, tabel routing 1) sementara instance lain akan keluar dari VPN (katakanlah tabel routing 2). Tetapi aplikasi lain, seperti Firefox, tidak hanya terkenal sulit untuk diikat ke alamat ip sumber tertentu (tetapi lihat di sini untuk solusi yang sangat cerdas), tetapi juga kejam dan jahat karena mereka tidak akan memungkinkan Anda untuk memiliki dua salinan sendiri berjalan secara bersamaan, masing-masing terikat ke alamat sumber yang berbeda. Dengan kata lain, meskipun berkat trik yang dirujuk di atas Anda dapat mewajibkan satu contoh untuk mengikat ke alamat sumber pilihan Anda, maka Anda tidak dapat memiliki versi lain mengikat ke alamat sumber lain.

Ini menjelaskan mengapa kami menggunakan solusi: mereka semua didasarkan pada ide yang sama, bahwa mereka bekerja dengan tumpukan jaringan yang terpisah dari sisa pc. Jadi Anda dapat memiliki, dalam mengurangi urutan kompleksitas, VM, buruh pelabuhan, wadah, ruang nama. Di masing-masing dari mereka Anda akan memiliki satu atau lebih tabel routing, tetapi Anda dapat memiliki beberapa contoh masing-masing (VM / buruh pelabuhan / wadah / ruang nama) dan Anda juga dapat mencampurnya secara bebas, masing-masing dari mereka menjalankan aplikasi sendiri seperti Firefox dipisahkan dengan senang hati dari yang lain.

Mungkin Anda masih tertarik dengan salah satu solusinya?

EDIT:

Solusi paling sederhana adalah namespace jaringan. Script di bawah ini menangani semua aspek yang diperlukan dari NNS: memasukkannya ke dalam file (Anda memilih nama Anda, saya biasanya menggunakan newns, tetapi Anda melakukan apa pun yang Anda inginkan) di /usr/local/bin, kemudian chmod 755 FILE_NAME, dan Anda dapat menggunakannya sebagai berikut:

       newns NAMESPACE_NAME start
       newns NAMESPACE_NAME stop

Ini akan membuka xtermuntuk Anda (itu karena saya suka xterm bekerja, tetapi Anda dapat mengubahnya jika Anda ingin menggunakan yang lain), yang termasuk dalam namespace baru. Dari dalam xterm, Anda dapat, jika ingin, memulai vpn Anda, dan kemudian memulai gim Anda. Anda dapat dengan mudah memeriksa bahwa Anda menggunakan VPN melalui perintah berikut:

    wget 216.146.38.70:80 -O - -o /dev/null | cut -d" " -f6 | sed 's/<\/body><\/html>//'

yang mengembalikan Anda IP publik Anda. Setelah mengatur VPN di xterm, Anda dapat memeriksa bahwa IP publik Anda berbeda di windows Anda yang lain. Anda dapat membuka hingga 254 xterms, dengan 254 NNSes berbeda, dan koneksi berbeda.

#!/bin/bash

#
# This script will setup an internal network 10.173.N.0/24; if this causes
# any conflict, change the statement below.

export IP_BASE=10.173

# It will open an xterm window in the new network namespace; if anything
# else is required, change the statement below.

export XTERM=/usr/bin/xterm

# The script will temporarily activate ip forwarding for you. If you
# do not wish to retain this feature, you will have to issue, at the 
# end of this session, the command
# echo 0 > /proc/sys/net/ipv4/ip_forward 
# yourself. 

 ###############################################################################

 WHEREIS=/usr/bin/whereis

 # First of all, check that the script is run by root:


 [ "root" != "$USER" ] && exec sudo $0 "$@"

 if [ $# != 2 ]; then
    echo "Usage $0 name action"
    echo "where name is the network namespace name,"
    echo " and action is one of start| stop| reload."
    exit 1
 fi

 # Do we have all it takes?

 IERROR1=0
 IERROR2=0
 IERROR3=0
 export IP=$($WHEREIS -b ip | /usr/bin/awk '{print $2}')
 if [ $? != 0 ]; then
    echo "please install the iproute2 package"
    IERROR1=1
 fi

 export IPTABLES=$($WHEREIS -b iptables | /usr/bin/awk '{print $2}')
 if [ $? != 0 ]; then
    echo "please install the iptables package"
    IERROR2=1
 fi

 XTERM1=$($WHEREIS -b $XTERM | /usr/bin/awk '{print $2}')
 if [ $? != 0 ]; then
    echo "please install the $XTERM package"
    IERROR3=1
 fi
 if [ IERROR1 == 1 -o IERROR2 == 1 -o IERROR3 == 1 ]; then
    exit 1
 fi

 prelim() {

 # Perform some preliminary setup. First, clear the proposed 
 # namespace name of blank characters; then create a directory
 # for logging info, and a pid file in it; then determine 
 # how many running namespaces already exist, for the purpose
 # of creating a unique network between the bridge interface (to 
 # be built later) and the new namespace interface. Lastly, 
 # enable IPv4 forwarding. 

    VAR=$1
    export NNSNAME=${VAR//[[:space:]]}

    export OUTDIR=/var/log/newns/$NNSNAME

    if [ ! -d $OUTDIR ]; then
            /bin/mkdir -p $OUTDIR
    fi
    export PID=$OUTDIR/pid$NNSNAME

    # Find a free subnet

    ICOUNTER=0
    while true; do
            let ICOUNTER=ICOUNTER+1
            ip addr show | grep IP_BASE.$ICOUNTER.1 2>&1 1> /dev/null
            if [ ! $? == 0 -a $ICOUNTER -lt 255 ]; then
                    export Nns=$ICOUNTER
                    break
            elif [ ! $? == 0 -a $ICOUNTER -gt 254 ]; then
                    echo "Too many open network namespaces"
                    exit 1
            fi
    done
    if [ $Nns == 1 ]; then
            echo 1 > /proc/sys/net/ipv4/ip_forward
    fi

 }

 start_nns() {

 # Check whether a namespace with the same name already exists. 

    $IP netns list | /bin/grep $1 2> /dev/null
    if [ $? == 0 ]; then
            echo "Network namespace $1 already exists,"
            echo "please choose another name"
            exit 1
    fi

    # Here we take care of DNS

    /bin/mkdir -p /etc/netns/$1
    echo "nameserver 8.8.8.8" > /etc/netns/$1/resolv.conf
    echo "nameserver 8.8.4.4" >> /etc/netns/$1/resolv.conf


    # The following creates the new namespace, the veth interfaces, and
    # the bridge between veth1 and a new virtual interface, tap0.
    # It also assigns an IP address to the bridge, and brings everything up

    $IP netns add $1
    $IP link add veth-a$1 type veth peer name veth-b$1
    $IP link set veth-a$1 up
    $IP tuntap add tap$1 mode tap user root
    $IP link set tap$1 up
    $IP link add br$1 type bridge
    $IP link set tap$1 master br$1
    $IP link set veth-a$1 master br$1
    $IP addr add $IP_BASE.$Nns.1/24 dev br$1
    $IP link set br$1 up

    # We need to enable NAT on the default namespace

    $IPTABLES -t nat -A POSTROUTING -j MASQUERADE

    # This assigns the other end of the tunnel, veth2, to the new 
    # namespace, gives it an IP address in the same net as the bridge above, 
    # brings up this and the (essential) lo interface, sets up the 
    # routing table by assigning the bridge interface in the default namespace
    # as the default gateway, creates a new terminal in the new namespace and 
    # stores its pid for the purpose of tearing it cleanly, later. 

    $IP link set veth-b$1 netns $1
    $IP netns exec $1 $IP addr add $IP_BASE.$Nns.2/24 dev veth-b$1
    $IP netns exec $1 $IP link set veth-b$1 up
    $IP netns exec $1 $IP link set dev lo up
    $IP netns exec $1 $IP route add default via $IP_BASE.$Nns.1
    $IP netns exec $1 su -c $XTERM $SUDO_USER &
    $IP netns exec $1 echo "$!" > $PID



}

stop_nns() {

# Check that the namespace to be torn down really exists

    $IP netns list | /bin/grep $1 2>&1 1> /dev/null
    if [ ! $? == 0 ]; then
            echo "Network namespace $1 does not exist,"
            echo "please choose another name"
            exit 1
    fi

    # This kills the terminal in the separate namespace, 
    # removes the file and the directory where it is stored, and tears down
    # all virtual interfaces (veth1, tap0, the bridge, veth2 is automatically
    # torn down when veth1 is), and the NAT rule of iptables. 

    /bin/kill -TERM $(cat $PID) 2> /dev/null 1> /dev/null
    /bin/rm $PID
    /bin/rmdir $OUTDIR
    $IP link set br$1 down
    $IP link del br$1
    $IP netns del $1
    $IP link set veth-a$1 down
    $IP link del veth-a$1
    $IP link set tap$1 down
    $IP link del tap$1
    $IPTABLES -t nat -D POSTROUTING -j MASQUERADE
    /bin/rm /etc/netns/$1/resolv.conf
    /bin/rmdir /etc/netns/$1

}


case $2 in
    start)
            prelim "$1"
            start_nns $NNSNAME
            ;;
    stop)
            prelim "$1"
            stop_nns $NNSNAME
            ;;
    reload)
            prelim "$1"
            stop_nns $NNSNAME
            prelim "$1"
            start_nns $NNSNAME
            ;;
    *)
 # This removes the absolute path from the command name

            NAME1=$0
            NAMESHORT=${NAME1##*/}

            echo "Usage:" $NAMESHORT "name action,"
            echo "where name is the name of the network namespace,"
            echo "and action is one of start|stop|reload"
            ;;
 esac

Jika Anda mau, Anda bahkan dapat memulai seluruh desktop di dalam namespace jaringan baru, dengan cara

            sudo startx -- :2 

maka Anda dapat mencarinya menggunakan Alt+ Ctrl+ Fn, di mana Fn adalah salah satu dari F1, F2, ....-

Saya perlu menambahkan satu peringatan: penanganan DNS di dalam namespace agak buggy, bersabarlah.

MariusMatutiae
sumber
1
Dan akhirnya saya memiliki penjelasan yang sangat sederhana namun terperinci tentang mengapa apa yang saya coba capai bukanlah hal yang mudah atau biasa! Terima kasih! Setelah membaca ini, saya yakin solusinya akan tepat, karena hanya lalu lintas dari program tertentu yang saya inginkan dialihkan, dan saya selalu ingin program dialihkan. Contoh: Saya ingin VideoGameA dialihkan melalui VPN, tetapi saya tidak ingin program lain dialihkan melalui VPN. Apakah ada cara sederhana untuk mengikat port tertentu agar hanya melalui antarmuka VPN? Jika demikian, bagaimana cara saya mengatur dan terhubung dengan benar ke antarmuka tersebut?
Josh Raymond
@ JoshRaymond Ok. Untuk memilih solusi yang paling sederhana, Anda harus memberi posting tabel routing Anda dengan VPN, dan harus memberi tahu saya apakah VideoGameA menggunakan port UDP sama sekali.
MariusMatutiae
Ini menggunakan kedua port TCP dan UDP 443, 3724 dan 1119 Rute akan diposting di commend berikutnya
Josh Raymond
$ route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 0.0.0.0 0.0.0.0 U 50 0 0 ppp0 0.0.0.0 192.168.1.1 0.0.0.0 UG 100 0 0 enp10s0 1.0.0.1 0.0.0.0 255.255.255.255 UH 0 0 0 ppp0 192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 enp10s0 192.168.1.0 0.0.0.0 255.255.255.0 U 100 0 0 enp10s0 199.168.112.120 192.168.1.1 255.255.255.255 UGH 100 0 0 enp10s0
Josh Raymond
Saya bertanya-tanya mengapa dalam jawaban @MariusMatutiae menciptakan ketukan dan jembatan? Tampaknya berfungsi dengan baik hanya menggunakan perangkat veth.
Ian Kelling