Vincular programa unix à interface de rede específica

40

Pergunta: Como inicio um programa, assegurando que seu acesso à rede seja vinculado por meio de uma interface de rede específica?

Caso: desejo acessar duas máquinas distintas com o mesmo IP (192.168.1.1), mas acessíveis por duas interfaces de rede diferentes (eth1 e eth2).

Exemplo:

net-bind -D eth1 -exec {Program 192.168.1.1}
net-bind -D eth2 -exec {Program 192.168.1.1}

A descrição acima é uma aproximação do que eu gostaria, inspirada na ligação de hardware feita via primusrun e optirun .

Desafio: Conforme sugerido em um encadeamento relacionado , as interfaces usadas não são escolhidas pelo programa, mas pelo kernel (daí a sintaxe de pré-ligação no exemplo acima).

Encontrei algumas soluções relacionadas que são insatisfatórias. Eles são baseados em interfaces de rede vinculadas por meio de lista negra de rede específica do usuário; ou seja, executando o processo como um usuário que pode acessar apenas uma única interface de rede específica.

Skeen
fonte
Você está sugerindo que sua máquina está conectada a duas redes diferentes, ambas 192.168.1.0? Como é a sua tabela de roteamento? Se você deseja restringir as interfaces visíveis de um processo, a solução mais leve seria cgroups, um contêiner mais pesado.
lgeorget
Sim, duas redes diferentes, ambas no mesmo intervalo de IP. Não tenho certeza de que não quero restringir as interfaces visíveis, apenas dite qual delas usar como padrão? :)
Skeen
3
O que você pede é difícil por um bom motivo: ter duas redes interconectadas usando o mesmo domínio IP é como ter um elevador em um prédio com dois andares com o mesmo número. O intervalo de IP é o que identifica o domínio, não a interface de saída. No entanto, deve haver uma maneira de contornar seu projeto de rede defeituoso com o iptables.
lgeorget
Estou conectando meu sistema a duas infra-estruturas in-loco diferentes, pois essas nunca foram projetadas para interagir e, portanto, o design da rede é defeituoso nesse aspecto.
Skeen
11
Meu argumento em relação aos NATs era que espaços de endereço inteiros geralmente estão ocultos atrás do NAT e, como estou conectando duas infraestruturas NAT'ted, a colisão ocorre. - Não estou em posição de modificar a infraestrutura. - Tentei usar namespaces de rede com pares de interfaces de rede virtual (um no namespace, um no namespace raiz) fazendo a ponte entre o namespace raiz e a interface física e executando programas no namespace da rede. - Parece estar funcionando, mas não estou obtendo acesso além do espaço para nome raiz (ou seja, nenhum acesso fora da própria máquina).
Skeen

Respostas:

35

Para Linux, isso já foi respondido no Superusuário - Como usar diferentes interfaces de rede para diferentes processos? .

A resposta mais popular usa um LD_PRELOADtruque para alterar a ligação de rede para um programa, mas os kernels modernos oferecem suporte a um recurso muito mais flexível chamado 'namespaces de rede', que é exposto pelo ipprograma. Esta resposta mostra como usar isso. De minhas próprias experiências, fiz o seguinte (como root):

# Add a new namespace called test_ns
ip netns add test_ns

# Set test to use eth0, after this point eth0 is not usable by programs
# outside the namespace
ip link set eth0 netns test_ns

# Bring up eth0 inside test_ns
ip netns exec test_ns ip link set eth0 up

# Use dhcp to get an ipv4 address for eth0
ip netns exec test_ns dhclient eth0

# Ping google from inside the namespace
ip netns exec test_ns ping www.google.co.uk

Também é possível gerenciar os namespaces da rede em certa medida com os comandos unsharee nsenter. Isso permite também criar espaços separados para PIDs, usuários e pontos de montagem. Para mais informações, consulte:

Graeme
fonte
"após este ponto, eth0 não pode ser usado por programas fora do namespace" - Então, eu estou cortando a conexão para todos os outros problemas ao fazer isso?
Skeen
11
@ Skeen, sim, presumivelmente, você pressionaria uma interface que nenhum outro programa está usando no espaço para nome e usaria a principal normalmente.
Graeme
11
@Graerne; Ambas as interfaces estão sendo usadas ativamente; Não posso me dar ao luxo de derrubar as interfaces.
Skeen
e o que com gateway padrão? wvdialpor exemplo, não parece configurá-lo de maneira alguma ... portanto, ele deve ser definido no próprio espaço para nome #
Flash Thunder
Você poderia incluir instruções sobre como desfazer isso? Você apenas ip netns remove test_nsvolta ao normal? Ou você tem que fazer algo especial?
Multihunter
17

Estou aceitando a resposta de Graeme; isso é simplesmente um acompanhamento para explicar as alterações que fiz na sugestão dele para resolver meu problema.

Em vez de vincular a interface física ao espaço para nome, criei um par de interfaces de rede virtual, com uma extremidade no espaço para nome da rede e outra na raiz. Os pacotes são roteados por essa rede virtual a partir do namespace, para o namespace raiz e depois para a interface física. - Como tal, sou capaz de executar todas as minhas transferências de dados comuns e, além disso, iniciar processos que só podem acessar uma interface específica.

# Create the eth0 network namespace
ip netns add eth0_ns

# Create the virtual network pair
ip link add v_eth0a type veth peer name v_eth0b

# Move v_eth0a to the eth0_ns namespace, the virtual pair is now split
# between two network namespaces.
ip link set v_eth0a netns eth0_ns

# Configure the ends of the virtual network pairs
ip netns exec eth0_ns ifconfig v_eth0a up {{NAMESPACE_IP}} netmask {{NAMESPACE_NETMASK}}
ifconfig v_eth0b up {{ROOT_NS_IP}} netmask {{ROOT_NS_NETMASK}}

# Setup routing from namespace to root
ip netns exec eth0_ns route add default gw {{ROOT_NS_IP}} dev v_eth0a

# Setup IP forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -s {{ROUTE_SOURCE}}/24 -o {{NETWORK_INTERFACE}} -j SNAT --to-source {{ROUTE_TARGET}}

Depois que as interfaces são configuradas para eth0 e eth1, com seus respectivos namespaces eth0_ns e eth1_ns, os programas podem ser executados na interface especificada via;

ip netns exec eth0_ns fish
ip netns exec eth1_ns fish
Skeen
fonte
4
Bem feito! Eu acho que você também pode criar um dispositivo de ponte e conectar o namespace padrão e o par virtual a um dos dispositivos físicos. Isso parece equivalente.
Graeme
11
Eu tentei conectar o dispositivo virtual e físico; Não consegui acessar a rede externa usando essa solução.
Skeen
2
Eu fiz, mas apenas a partir do novo espaço para nome. Acho que o problema que eu estava tendo estava relacionado ao gerenciador de rede, mas não descobri ou teria atualizado minha resposta.
Graeme
Eu tenho o mesmo problema, mas não consegui usar essa solução. O que exatamente devo inserir em {{ROUTE_SOURCE}} e {{ROUTE_TARGET}} na última etapa?
litov 4/12/16
@Graeme, pelo menos no Ubuntu eu era capaz de voltar a conectividade tanto no namespace bem como o espaço global, emitindo dhclient <bridge>por aqui .
Chris caça
4

Solução I: pré-carregando uma biblioteca específica

  • App-Route-Jail : use ld_preload para forçar o gateway da interface (ótima idéia, mas requer recursos de raiz ou de marcas) o uso é detalhado nas notas abaixo

  • Proxybound : use ld_preload para forçar um proxy para um aplicativo específico (isso está usando proxy em vez de interface)

  • Force-Bind : possui muitos recursos, mas o bind vaza (não é confiável)

  • Bind-Interface-IP : conexões muito simples e vazadas (não confiáveis)

  • Bind-IP : conexões muito simples e vazamento (não confiáveis)

Solução II: espaço de usuário do Linux

  • IP-netns clássicos do espaço para usuário linux : a melhor solução, mas requer raiz e interface, só pode existir em um único espaço de usuário

  • Firejail : o Firejail pode forçar um aplicativo a usar uma rede específica, mas a compatibilidade é limitada (por exemplo, não é compatível com as interfaces tun). firejail não requer rootfirejail --dns=8.8.8.8 --noprofile --net=eth0 --ip=192.168.1.1 app-command

  • Firejail com netns : o Firejail pode forçar um aplicativo a usar um espaço de usuário específico que foi criado separadamente. Isso permite nomear espaços sem raizfirejail --dns=8.8.8.8 --noprofile --netns=nameOfyourNS app-command

  • Firejail com mascarada e ponte : o Firejail pode forçar um aplicativo a usar uma interface específica com o mascarada de iptables , isso é ótimo e não requer raiz, mas exige ip_forward e pode implicar em impacto na segurançafirejail --net=br0 firefox

Solução III: Linux iptables

Os iptables podem ser usados ​​para essa finalidade, mas isso requer ip_forward e pode implicar em um impacto na segurança se não estiver configurado corretamente, exemplo 1 , exemplo 2 , exemplo 3 , exemplo 4

Soluções (I, II e III) observa:

Wireguard

Se você estiver usando uma VPN (especialmente wireguard) e quiser aplicar esta solução a uma interface wireguard ( wireguard com espaço do usuário ), poderá seguir o link instruído para criar um espaço de usuário contendo uma interface wg (e, portanto, limitado a uma interface VPN) ) também pode ser combinado com isso firejail --netns=containerpara poder usar o espaço do usuário sem raiz.

Como encontrar o gateway de interface

Existem muitas soluções para encontrar o gateway. Aqui estão alguns comandos que permitem encontrar o gateway usado

$ route
$ route -n
$ ip rule list
$ ip route show
$ netstat -rn
$ cat /etc/network/interfaces
$ cat /etc/sysconfig/network-scripts/ifcfg-eth0
$ traceroute www.google.com
$ ip route show 0.0.0.0/0 dev eth0

Como usar o App-Route-Jail

  • Compilar App-Route-Jail
git clone https://github.com/Intika-Linux-Network/App-Route-Jail.git
cd Approute-Utils
chown 755 make.sh
./make.sh
  • Adicionar uma rota para os futuros pacotes marcados (para o aplicativo preso) neste exemplo 192.168.1.1é usado como gateway forçado; essa regra de rota não afeta outros aplicativos; essa manipulação deve ser feita apenas uma vez na inicialização do sistema, por exemplo, se você desejar use esta solução diariamente
ip rule add fwmark 10 table 100
ip route add default via 192.168.1.1 table 100
  • Inicie o aplicativo que você deseja prender
MARK=10 LD_PRELOAD=./mark.so firefox
  • Testando o endereço IP da WAN
MARK=10 LD_PRELOAD=./mark.so wget -qO- ifconfig.me
intika
fonte