Encaminhe apenas tráfego específico através da VPN

11

Procurei pessoas anteriores fazendo perguntas semelhantes, mas ainda não obtive uma resposta direta que funcione para a minha situação, então aqui vai ..

Estou executando no Linux (Fedora 22) e tenho um serviço VPN pelo qual pago, no entanto, só preciso de programas específicos para usar a VPN no tráfego da Internet e posso usar minha conexão ISP padrão para todo o resto (por exemplo, navegação na Web, etc)

Vamos simplificar e limitar o programa mais usado, World of Warcraft, que está sendo executado no WINE.

Agora, eu tenho uma configuração de VPN via interface de rede para que todo o meu tráfego através do enp10s0 (nome estranho de eth0 dos meus computadores) possa ser encapsulado através do serviço VPN, no entanto, eu só preciso de programas específicos (ou portas que esses programas usam, para seja específico) para passar pela VPN.

Como faço para configurar o túnel e fazer com que ele apenas roteie as portas necessárias através da VPN, mantendo todo o restante não roteado?

Josh Raymond
fonte
Você pode explicar como as outras respostas não abordam esse problema? O que é único na sua configuração?
Paul
1
Quase todos eles têm uma maneira diferente de conseguir isso, e nenhum deles torna tudo simples. Alguns usam um espaço para nome separado para o aplicativo, outros usam uma interface de encapsulamento, outros o utilizam diretamente através do openvpn em um terminal, mas nenhum deles que eu encontrei me deu algum tipo de método decifrável para executar qualquer um desses.
Josh Raymond
Por favor, veja o meu Editar
MariusMatutiae

Respostas:

17

O que você está pedindo não existe. É por isso que você está insatisfeito com as respostas que encontrou (algumas delas possivelmente minhas): todas sugeriram soluções alternativas , não uma solução genuína, simples ou complexa.

Deixe-me explicar. O roteamento em todos os sistemas operacionais é determinado pelo endereço de destino: você pode muito bem ter várias rotas, mas a escolha entre elas não se baseia no aplicativo que está chamando a conexão, mas simplesmente no endereço de destino. Ponto final.

Deixe-me dar um exemplo não trivial. Quando um cliente VPN estabelece uma conexão com seu servidor, ainda é possível rotear uma conexão para um determinado site, por exemplo, example.org, fora da VPN. Mas todos os aplicativos que tentam alcançar esse endereço especial serão roteados para fora da VPN: você não pode ter alguns aplicativos acessando example.org através da VPN enquanto outros aplicativos passam para fora da VPN.

A situação se torna mais rica com o kernel do Linux, que permite o roteamento de origem: isso significa que você pode ter duas ou mais tabelas de roteamento, e a escolha entre elas é baseada no endereço de origem, não no endereço de destino.

Um exemplo não trivial: meu pc tem duas linhas externas, com dois IPs públicos distintos. Ele pode ser contatado por qualquer uma das interfaces e é importante que minhas respostas a uma determinada conexão passem pela mesma interface em que a conexão veio: caso contrário, elas serão descartadas como irrelevantes quando chegarem à pessoa que iniciou a conexão. Este é o roteamento de origem.

Justo, o que dizer das conexões que começamos? Alguns aplicativos permitem especificar o endereço de ligação, como o cliente openssh :

-b bind_address

Use bind_address na máquina local como o endereço de origem da conexão. Útil apenas em sistemas com mais de um endereço.

Para eles, não há problema em ter uma instância passando pela VPN (por exemplo, tabela de roteamento 1) enquanto outra instância sairá da VPN (por exemplo, tabela de roteamento 2). Mas outros aplicativos, como o Firefox, não são notoriamente difíceis de vincular a um endereço IP de origem específico (mas veja aqui uma solução muito inteligente), mas também são maus e desagradáveis, pois não permitirão que você tenha duas cópias deles mesmos executando simultaneamente, cada um vinculado a um endereço de origem diferente. Em outras palavras, embora, graças ao truque mencionado acima, você possa obrigar uma instância a se vincular a um endereço de origem de sua escolha, não poderá ter outra versão dela vinculando-se ao outro endereço de origem.

Isso explica por que usamos soluções alternativas: todas elas são baseadas na mesma idéia, de que trabalham com uma pilha de rede separada do que o resto do PC. Assim, você pode ter, em ordem aproximada de complexidade, VMs, janelas de encaixe, contêineres, namespaces. Em cada uma delas, você terá uma ou mais tabelas de roteamento, mas poderá ter várias instâncias de cada (VM / dockers / containers / namespaces) e também pode misturá-las livremente, cada uma delas executando seu próprio aplicativo como o Firefox, felizmente separado dos outros.

Talvez você ainda esteja interessado em uma das soluções alternativas?

EDITAR:

A solução mais simples é um espaço para nome da rede. O script abaixo lida com todos os aspectos necessários de um NNS: coloque-o em um arquivo (você escolhe seu nome, eu geralmente uso newns, mas você faz o que preferir) /usr/local/bine então chmod 755 FILE_NAMEpode usá-lo da seguinte maneira:

       newns NAMESPACE_NAME start
       newns NAMESPACE_NAME stop

Ele abrirá um xtermpara você (é porque eu gosto do xterm para funcionar, mas você pode alterá-lo se desejar usar qualquer outra coisa), que pertence ao novo espaço para nome. De dentro do xterm, você pode, se desejar, iniciar seu vpn e, em seguida, iniciar seu jogo. Você pode facilmente verificar se está usando a VPN através do seguinte comando:

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

que retorna seu IP público. Depois de configurar a VPN no xterm, você pode verificar se o seu IP público é diferente nas outras janelas. Você pode abrir até 254 xterms, com 254 NNSes diferentes e conexões diferentes.

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

Se desejar, você pode até iniciar uma área de trabalho inteira dentro do novo espaço para nome da rede, por meio de

            sudo startx -- :2 

então você pode procurá-lo usando Alt+ Ctrl+ Fn, onde Fn é um de F1, F2, ....

Eu preciso adicionar uma ressalva: o tratamento de DNS dentro dos espaços para nome é um pouco complicado, seja paciente.

MariusMatutiae
fonte
1
E assim, finalmente, tenho uma explicação muito simples, mas detalhada, de por que o que estou tentando realizar não é fácil nem comum! Obrigado! Depois de ler isso, acredito que uma solução alternativa seria apropriada, pois é apenas o tráfego de um programa específico que eu quero roteado, e eu sempre desejaria que esse programa fosse roteado. Exemplo: quero que o VideoGameA seja roteado pela VPN, mas não quero que outros programas sejam roteados por ela. Existe uma maneira simples de ligar portas específicas para passar apenas pela interface VPN? Em caso afirmativo, como devo configurar e conectar-me adequadamente à referida interface?
21420 Josh Raymond
@JoshRaymond Ok. Para escolher a solução mais simples, você deve publicar sua tabela de roteamento com a VPN e informar se o VideoGameA usa portas UDP.
MariusMatutiae
Ele usa tanto portas TCP e UDP 443, 3724 e 1119 Route serão publicadas na próxima commend
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
Gostaria de saber por que, na resposta @MariusMatutiae, cria uma torneira e uma ponte? Parece funcionar bem apenas usando os dispositivos veth.
quer