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


11

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

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

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

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

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


他の回答がこの問題に対処していない方法を説明できますか?セットアップのユニークな点は何ですか?
ポール

1
彼らのほとんどすべてがこれを達成するための異なる方法を持っています、そしてそれらのどれもそれをまったく簡単にしてくれません。一部はアプリケーションに別の名前空間を使用し、一部はトンネルインターフェースを使用し、一部はターミナルのopenvpnを介して直接実行しますが、それらのどれも、これらのいずれかを行うための解読可能な方法を提供していません。
Josh Raymond

私の編集をご覧ください
MariusMatutiae

回答:


17

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

説明させてください。すべての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アドレスにバインドするのが非常に難しいだけでなく(非常にスマートな回避策についてはこちらを参照)、また、自分自身の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処理は少しバグが多いので、しばらくお待ちください。


1
そして、私がやろうとしていることが簡単でも一般的でもない理由について、非常にシンプルで詳細な説明がついにできました!ありがとうございました!これを読んだら、回避策は適切だと思います。ルーティングするのは特定のプログラムからのトラフィックのみであり、そのプログラムを常にルーティングする必要があるからです。例:VideoGameAをVPN経由でルーティングしたいが、他のプログラムはVPN経由でルーティングしたくありません。特定のポートをバインドしてVPNインターフェイスのみを通過する簡単な方法はありますか?もしそうなら、私はどのように適切にセットアップして、そのインターフェースに接続しますか?
Josh Raymond、

@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
Josh Raymond、

答えで@MariusMatutiaeがタップとブリッジを作成する理由を知りたいのですが?vethデバイスを使用するだけで問題なく動作するようです。
Ian Kelling 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.