web-dev-qa-db-ja.com

VPNを介して特定のトラフィックのみをルーティングする

私は同様の質問をする以前の人々を調べましたが、私の与えられた状況でうまくいく正解をまだ得ていないので、ここに行きます。

私はLinux(Fedora 22)で実行しており、VPNサービスを利用していますが、インターネットトラフィックにVPNを使用するために特定のプログラムのみが必要で、他のすべてのもの(つまり、Webブラウジング、等)

これを簡単にして、WINEで実行されている最も使用されているプログラム、World of Warcraftに限定します。

今、私はネットワークインターフェイスを介してVPNを設定しているので、enp10s0(私のコンピューターのeth0の奇妙な名前)を介したすべてのトラフィックはVPNサービスを介してトンネルできますが、特定のプログラム(またはこれらのプログラムが使用するポート)のみが必要です具体的には)VPNを通過する。

トンネルをセットアップし、VPNを介して必要なポートのみをルーティングし、他はすべてルーティングしないようにするにはどうすればよいですか?

11
Josh Raymond

あなたが求めているものは存在しません。これが、あなたが見つけた答えに不満を持っている理由です(それらのいくつか、おそらく私のものです):すべてのそれらが回避策を提案しており、単純または複雑な本物の解決策ではありません。

説明させてください。すべてのOSでのルーティングは、宛先アドレスによって決定されます。いくつかのルートがある場合がありますが、それらの間の選択は、接続を呼び出すアプリケーションに基づくのではなく、単に宛先アドレスに基づいています。フルストップ。

簡単な例を挙げましょう。 VPNクライアントがサーバーへの接続を確立した後も、VPNの外部の特定のサイト(example.orgなど)に接続をルーティングすることが可能です。ただし、その特別なアドレスに到達しようとするすべてのアプリケーションはVPNの外部にルーティングされます。他のアプリケーションがVPNの外部を通過する間、一部のアプリケーションはVPNを介してexample.orgにアクセスできません。

Linuxカーネルで状況がより豊かになり、ソースルーティングが可能になります。これは、2つ以上のルーティングテーブルを持つことができ、それらの間の選択は宛先アドレスではなくソースアドレスに基づくことを意味します。

重要な例:私のPCには2つの外線があり、2つの異なるパブリックIPがあります。どちらのインターフェースからでも連絡できます。特定の接続への私の返信は、接続が届いたのと同じインターフェースを経由することが重要です。それ以外の場合は、接続を開始した人に到達したときに無関係として破棄されます。これはソースルーティングです。

十分に公平ですが、私たちが開始する接続についてはどうですか?一部のアプリでは、バインドアドレスを指定できます opensshクライアント など:

-b bind_address

接続のソースアドレスとしてローカルマシンのbind_addressを使用します。複数のアドレスを持つシステムでのみ役立ちます。

それらの場合、1つのインスタンスがVPNを通過する(たとえば、ルーティングテーブル1)一方で、別のインスタンスがVPNの外部に移動する(たとえば、ルーティングテーブル2)ことは問題ありません。しかし、Firefoxなどの他のアプリは、特定の送信元IPアドレスにバインドするのが非常に難しいだけでなく(非常にスマートな回避策については here を参照)、それらは意地悪で厄介ですnotを使用すると、それぞれが異なるソースアドレスにバインドされた2つのコピーを同時に実行できます。つまり、上記のトリックのおかげで、1つのインスタンスに選択した送信元アドレスにバインドするように強制できますが、別のバージョンのインスタンスを他の送信元アドレスにバインドすることはできません。

これは、私たちが回避策を使用する理由を説明します。それらはすべて同じ考え方に基づいており、他のPCとは別のネットワークスタックで動作するということです。そのため、複雑さの大まかな順序で、VM、Docker、コンテナー、名前空間を持つことができます。それぞれに1つ以上のルーティングテーブルがありますが、それぞれのインスタンス(VM/dockers/containers/namespaces)を複数持つことができ、それらを自由に混ぜることができ、それぞれがFirefoxのような独自のアプリを実行して幸せに分離されています他のものから。

多分あなたはまだ回避策の1つに興味がありますか?

編集:

最も簡単な回避策は、ネットワーク名前空間です。以下のスクリプトは、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、...のいずれかです。

注意点を1つ追加する必要があります。名前空間内のDNS処理は少しバグが多いので、しばらくお待ちください。

17
MariusMatutiae