通过 VPN 仅路由特定流量

Jos*_*ond 17 linux fedora vpn routing

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

我在 Linux (Fedora 22) 上运行并且有一个我付费的 VPN 服务,但是我只需要特定的程序来使用 VPN 来处理他们的互联网流量,并且可以使用我的标准 ISP 连接来处理其他所有事情(即网页浏览、等等)

我们将使其变得简单并将其限制为通过 WINE 运行的最常用程序《魔兽世界》。

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

我如何设置隧道,让它只路由通过 VPN 所需的端口,同时保持其他所有未路由?

Mar*_*iae 19

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

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

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

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

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

很公平,我们开始的连接怎么样?某些应用程序允许您指定绑定地址,例如openssh 客户端

-b 绑定地址

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

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

这解释了为什么我们使用变通方法:它们都基于相同的想法,即它们使用单​​独的网络堆栈而不是 PC 的其余部分。因此,您可以按复杂性递减的大致顺序拥有 VM、docker、容器、命名空间。在它们中的每一个中,您将拥有一个或多个路由表,但您可以拥有每个实例的多个实例(VM/dockers/containers/namespaces),您也可以自由混合它们,每个实例都运行自己的应用程序,如 Firefox 愉快地分开从其他的。

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

编辑:

最简单的解决方法是使用网络命名空间。下面的脚本处理 NNS 的所有必要方面:将它放在一个文件中(你选择你的名字,我通常使用newns,但你可以做任何你喜欢的) in /usr/local/bin, then chmod 755 FILE_NAME,你可以按如下方式使用它:

       newns NAMESPACE_NAME start
       newns NAMESPACE_NAME stop
Run Code Online (Sandbox Code Playgroud)

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

    wget 216.146.38.70:80 -O - -o /dev/null | cut -d" " -f6 | sed 's/<\/body><\/html>//'
Run Code Online (Sandbox Code Playgroud)

这会返回您的公共 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
Run Code Online (Sandbox Code Playgroud)

如果你愿意,你甚至可以在新的网络命名空间内启动整个桌面,通过

            sudo startx -- :2 
Run Code Online (Sandbox Code Playgroud)

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

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