通过VPN仅路由特定流量


11

我查过以前问过类似问题的人,但还没有得到一个可以在我给定情况下使用的直接答案,所以这里是。

我在Linux(Fedora 22)上运行,并有付费的VPN服务,但是我只需要特定的程序即可使用VPN进行互联网流量,并且可以将我的标准ISP连接用于其他所有操作(例如,网络浏览,等等)

我们将简化它,并将其限制为通过WINE运行的最常用的程序《魔兽世界》。

现在,我通过网络接口进行了VPN设置,因此通过enp10s0(我的计算机eth0的怪异名称)的所有流量都可以通过VPN服务进行隧道传输,但是,我只需要特定程序(或这些程序使用的端口)即可。具体而言)以通过VPN。

如何设置隧道,并使其仅通过VPN路由所需的端口,同时保持其他所有路由不变?


您能解释一下其他答案如何解决这个问题吗?您的设置有何独特之处?
保罗

1
几乎每个人都有不同的方法来完成此任务,而且没有一个人使之简单。有些人为应用程序使用单独的名称空间,有些人使用隧道接口,有些人直接通过终端中的openvpn进行操作,但是我发现它们都没有给我提供任何一种可解码的方法来执行这些操作。
2015年

请查看我的编辑
MariusMatutiae,2015年

Answers:


17

你所要求的并没有存在。这就是为什么您对找到的答案不满意的原因(其中一些可能是我的):所有这些都提出了建议的解决方法,而不是简单或复杂的真正解决方案。

让我解释。所有操作系统中的路由均由目标地址决定:您可能有数条路由,但是它们之间的选择不是基于调用连接的应用程序,而是仅基于目标地址。句号

让我举一个简单的例子。当VPN客户端建立与其服务器的连接时,仍然可以将连接路由到VPN外部的给定站点,例如example.org。但是,所有尝试到达该特殊地址的应用程序都将路由到VPN外部:您不能让某些应用程序通过VPN进入example.org,而其他应用程序则通过VPN之外。

Linux内核允许源路由,这种情况变得更加丰富:这意味着您可以拥有两个或更多路由表,并且它们之间的选择取决于源地址,而不是目标地址。

一个不平凡的例子:我的电脑有两条外线,有两个不同的公共IP。可以通过任一接口进行联系,并且重要的是,我对给定连接的答复将通过与连接进入的接口相同的接口:否则,当它们到达发起连接的人员时,它们将被视为无关。这是源路由。

公平地说,我们开始的联系如何?一些应用程序允许您指定绑定地址,例如openssh客户端

-b bind_address

使用本地计算机上的bind_address作为连接的源地址。仅在具有多个地址的系统上有用。

对于他们来说,让一个实例通过VPN(例如路由表1)而另一个实例将在VPN外部(例如路由表2)则没有问题。但其他应用程序,如Firefox,不仅是出了名的难绑定到特定的源IP地址(但看到这里的一个非常聪明的解决办法),但也有刻薄和肮脏的,因为它们不会让你有自己的两个副本同时运行,每个绑定到一个不同的源地址。换句话说,尽管由于上述技巧,您可以强制一个实例绑定到您选择的源地址,但是您不能让该实例的另一个版本绑定到另一个源地址。

这就解释了为什么我们使用变通方法:它们都基于相同的思想,即它们与PC的其余部分一起使用单独的网络堆栈。因此,您可以以降低复杂性的近似顺序获得虚拟机,泊坞窗,容器,名称空间。在它们的每一个中,您将都有一个或多个路由表,但是每个表都可以有多个实例(VM / docker /容器/命名空间),还可以自由地混合它们,其中的每一个都运行自己的应用程序,如Firefox从其他的。

也许您仍然对其中一种解决方法感兴趣?

编辑:

最简单的解决方法是网络名称空间。下面的脚本处理了NNS的所有必要方面:将其放在(然后newns选择)文件中(通常选择您的名字,但您可以根据自己的喜好进行选择)/usr/local/bin,然后chmod 755 FILE_NAME可以按以下方式使用它:

       newns NAMESPACE_NAME start
       newns NAMESPACE_NAME stop

它将xterm为您打开一个(属于我,因为xterm可以工作,但是如果您希望使用其他功能,则可以更改它),它属于新的命名空间。如果愿意,可以从xterm内部启动vpn,然后开始游戏。您可以通过以下命令轻松检查您是否正在使用VPN:

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

这将返回您的公共IP。在xterm中设置VPN后,您可以检查其他窗口中的公共IP是否不同。您最多可以打开254个xterm,其中包含254个不同的NNS和不同的连接。

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

如果需要,您甚至可以通过以下方式在新的网络名称空间内启动整个桌面:

            sudo startx -- :2 

那么您可以使用Alt+ Ctrl+ 搜索它Fn,其中Fn是F1,F2,....-之一

我需要添加一个警告:名称空间内的DNS处理有点麻烦,请耐心等待。


1
因此,对于为什么我要实现的目标既不容易也不常见,我终于有了一个非常简单而详细的解释!谢谢!读完这篇文章后,我相信有一种变通方法是适当的,因为它只是我要路由的特定程序的流量,而我始终希望该程序被路由。示例:我希望VideoGameA通过VPN路由,但是我不希望任何其他程序通过VPN路由。有没有简单的方法来绑定特定端口以仅通过VPN接口?如果是这样,我将如何正确设置并连接到所述接口?
乔什·雷蒙德

@JoshRaymond好的。为了选择最简单的解决方法,您应该在VPN中发布路由表,并告诉我VideoGameA是否完全使用UDP端口。
MariusMatutiae,2015年

它同时使用TCP和UDP端口443、3724和1119,路由将在下一条命令中发布
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
2015年

我想知道为什么在答案中@MariusMatutiae创建了水龙头和桥?仅使用veth设备似乎可以正常工作。
伊恩·凯林
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.