Kieruj tylko określony ruch przez VPN

11

Sprawdziłem poprzednie osoby, które zadają podobne pytania, ale jeszcze nie otrzymałem prostej odpowiedzi, która zadziała w mojej sytuacji, więc proszę ...

Korzystam z systemu Linux (Fedora 22) i mam usługę VPN, za którą płacę, jednak potrzebuję tylko określonych programów do korzystania z VPN do ich ruchu internetowego i mogę używać mojego standardowego połączenia z usługodawcą internetowym do wszystkiego innego (tj. Przeglądania stron internetowych, itp)

Uprościmy to i ograniczymy do najczęściej używanego programu, World of Warcraft, który jest uruchamiany przez WINE.

Teraz mam konfigurację VPN za pośrednictwem interfejsu sieciowego, dzięki czemu cały mój ruch przez enp10s0 (moje komputery dziwnie nazywają się eth0) może być tunelowany przez usługę VPN, jednak potrzebuję tylko określonych programów (lub portów, których te programy używają, aby bądź konkretny), aby przejść przez VPN.

Jak skonfigurować tunel i pozwolić mu tylko kierować potrzebnymi portami przez VPN, jednocześnie utrzymując wszystko inne bez routingu?

Josh Raymond
źródło
Czy możesz wyjaśnić, w jaki sposób inne odpowiedzi nie rozwiązują tego problemu? Co jest wyjątkowego w twojej konfiguracji?
Paul,
1
Prawie każdy z nich ma inny sposób na osiągnięcie tego i żaden z nich wcale tego nie upraszcza. Niektórzy używają oddzielnej przestrzeni nazw dla aplikacji, niektórzy używają interfejsu tunelowego, inni robią to bezpośrednio przez openvpn w terminalu, ale żadna z nich nie dała mi żadnej odszyfrowalnej metody wykonania któregokolwiek z nich.
Josh Raymond,
Proszę zobaczyć moją Edycję
MariusMatutiae,

Odpowiedzi:

17

To, o co prosisz, nie istnieje. Dlatego jesteś niezadowolony z odpowiedzi, które znalazłeś (niektóre z nich, być może moje): wszystkie sugerują obejścia , a nie prawdziwe rozwiązanie, proste lub złożone.

Pozwól mi wyjaśnić. Trasowanie we wszystkich systemach operacyjnych zależy od adresu docelowego: możesz mieć kilka tras, ale wybór między nimi nie zależy od aplikacji wywołującej połączenie, ale po prostu od adresu docelowego. Kropka.

Dam ci nietrywialny przykład. Gdy klient VPN nawiązuje połączenie ze swoim serwerem, nadal można przekierować połączenie do danej witryny, np. Example.org, poza VPN. Ale wszystkie aplikacje próbujące dotrzeć do tego specjalnego adresu będą kierowane poza VPN: niektóre aplikacje nie mogą przechodzić do example.org przez VPN, podczas gdy inne aplikacje przechodzą poza VPN.

Sytuacja staje się bogatsza w jądrze Linuksa, które umożliwia routing źródła: oznacza to, że możesz mieć dwie lub więcej tabel routingu, a wybór między nimi zależy od adresu źródłowego, a nie adresu docelowego.

Nietrywialny przykład: mój komputer ma dwie zewnętrzne linie z dwoma odrębnymi publicznymi adresami IP. Można się z nim skontaktować za pośrednictwem dowolnego interfejsu i ważne jest, aby moje odpowiedzi na dane połączenie przechodziły przez ten sam interfejs, przez który nastąpiło połączenie: w przeciwnym razie zostaną odrzucone jako nieistotne, gdy dotrą do osoby, która zainicjowała połączenie. To jest routing źródła.

W porządku, co z połączeniami, które uruchamiamy? Niektóre aplikacje pozwalają określić adres powiązania, na przykład klient openssh :

-b adres_powiązania

Użyj adresu bind_address na komputerze lokalnym jako adresu źródłowego połączenia. Przydatny tylko w systemach z więcej niż jednym adresem.

Dla nich nie ma problemu z przejściem jednej instancji przez VPN (powiedzmy, tablica routingu 1), podczas gdy inna instancja wyjdzie poza VPN (powiedzmy tablica routingu 2). Ale inne aplikacje, takie jak Firefox, są nie tylko niezwykle trudne do przypisania do konkretnego źródłowego adresu IP (ale zobacz tutaj bardzo mądre obejście), ale są również złośliwe i wredne, ponieważ nie pozwalają ci mieć dwóch kopii siebie działające jednocześnie, każde powiązane z innym adresem źródłowym. Innymi słowy, chociaż dzięki sztuczce, o której mowa powyżej, możesz zobowiązać jedną instancję do powiązania z wybranym adresem źródłowym, nie możesz mieć innej wersji powiązania z innym adresem źródłowym.

To wyjaśnia, dlaczego używamy obejść: wszystkie oparte są na tym samym pomyśle, że działają z osobnym stosem sieciowym niż reszta komputera. Możesz więc mieć, zmniejszając przybliżoną kolejność złożoności, maszyny wirtualne, dokery, kontenery, przestrzenie nazw. W każdej z nich będziesz mieć jedną lub więcej tabel routingu, ale możesz mieć kilka instancji każdej z nich (VM / dokery / kontenery / przestrzenie nazw) i możesz również dowolnie je mieszać, każda z nich ma własną aplikację, taką jak Firefox, szczęśliwie oddzielona od pozostałych.

Być może nadal interesuje Cię jedno z obejść?

EDYTOWAĆ:

Najprostszym obejściem jest sieć nazw sieciowych. Poniższy skrypt obsługuje wszystkie niezbędne aspekty NNS: umieść go w pliku (wybierasz swoje imię, zwykle używam newns, ale robisz, co wolisz) /usr/local/bin, a następnie chmod 755 FILE_NAMEmożesz użyć go w następujący sposób:

       newns NAMESPACE_NAME start
       newns NAMESPACE_NAME stop

Otworzy się xtermdla ciebie (to dlatego, że lubię Xterm do pracy, ale możesz go zmienić, jeśli chcesz użyć czegoś innego), który należy do nowej przestrzeni nazw. Z wnętrza Xtermu możesz, jeśli chcesz, rozpocząć VPN, a następnie rozpocząć grę. Możesz łatwo sprawdzić, czy korzystasz z VPN, wykonując następujące polecenie:

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

który zwraca ci twoje publiczne IP. Po skonfigurowaniu VPN w xterm możesz sprawdzić, czy twój publiczny adres IP jest inny w innych oknach. Możesz otworzyć do 254 xtermów, z 254 różnymi NNS i różnymi połączeniami.

#!/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

Jeśli chcesz, możesz nawet uruchomić cały pulpit w nowej sieciowej przestrzeni nazw, za pomocą

            sudo startx -- :2 

możesz go wyszukać za pomocą Alt+ Ctrl+ Fn, gdzie Fn jest jednym z F1, F2, ....-

Muszę dodać jedno zastrzeżenie: obsługa DNS wewnątrz przestrzeni nazw jest trochę błędna, bądź cierpliwy.

MariusMatutiae
źródło
1
I w końcu mam bardzo proste, ale szczegółowe wyjaśnienie, dlaczego to, co próbuję osiągnąć, nie jest ani łatwe, ani powszechne! Dziękuję Ci! Po przeczytaniu tego uważam, że obejście byłoby odpowiednie, ponieważ jest to tylko ruch z określonego programu, który chcę trasować, i zawsze chciałbym, aby ten program był routowany. Przykład: Chcę, aby VideoGameA była kierowana przez VPN, ale nie chcę, aby inne programy były przez nią kierowane. Czy istnieje prosty sposób na powiązanie określonych portów, aby przejść tylko przez interfejs VPN? Jeśli tak, to jak prawidłowo skonfigurować i połączyć się z tym interfejsem?
Josh Raymond
@JoshRaymond Ok. Aby wybrać najprostsze obejście, powinieneś opublikować swoją tabelę routingu za pomocą VPN i powiedzieć, czy VideoGameA w ogóle używa portów UDP.
MariusMatutiae
Używa zarówno portów TCP, jak i UDP 443, 3724 i 1119 Trasa zostanie opublikowana w następnym poleceniu
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,
Zastanawiam się, dlaczego w odpowiedzi @MariusMatutiae tworzy kran i most? Wydaje się, że działa dobrze, używając tylko urządzeń veth.
Ian Kelling