iptables: combina SNAT com remapeamento de rede para o OpenVPN

2

[Desculpas pelo longo prelúdio; pergunta no meio do caminho.]

Eu tenho uma configuração OpenVPN em funcionamento em que um servidor VPN envia uma rota de volta para um cliente (doravante chamado de "roteador") que pode expor sua própria sub-rede à máquina que executa o servidor, bem como a outras máquinas executando a VPN cliente. Isso é feito apenas fazendo com que o roteador use o SNAT target de iptables . Por exemplo, digamos que o servidor VPN e outros clientes indistintos estejam na rede 10.0.77.0/24, a VPN cria uma interface tun0 abrangendo 192.168.252.0/24 e a sub-rede privada é 192.168.33.0/24. A configuração do servidor OpenVPN diz (entre outras coisas)

client-to-client
route 192.168.33.0 255.255.255.0
push "route 192.168.33.0 255.255.255.0"

Quando a máquina "roteador" Linux, 192.168.33.10, digamos, se conecta à VPN, ela recebe uma rota, então sua tabela se parece com

192.168.33.0    *               255.255.255.0   U     0      0        0 eth1
192.168.252.0   192.168.252.5   255.255.255.0   UG    0      0        0 tun0
192.168.252.5   *               255.255.255.255 UH    0      0        0 tun0

e depois é configurado para executar

sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -s 192.168.252.0/24 -j SNAT --to-source 192.168.33.10

Ele também pode adicionar algumas regras iptables para criar um firewall, mas o acima é suficiente para permitir que a máquina rodando o servidor OpenVPN (ou outro cliente) se conecte a, por exemplo, 192.168.33.11: os pacotes são enviados via tun0 para o roteador, que usa SNAT para definir o IP de origem para seu próprio 192.168.33.10 e, em seguida, encaminhar os pacotes para sua máquina de irmão 192.168.33.11. Os pacotes de resposta são enviados para o roteador, que os encaminha de volta pelo túnel e tudo está bem. Então, por exemplo, em 192.168.33.11 eu posso

nc -l localhost 9999

e do 10.0.77.13 (algum outro cliente VPN) eu posso

nc 192.168.33.11 9999

e faça uma conexão. Até aí tudo bem.

Observe que nenhuma alteração está sendo feita na máquina roteadora física em qualquer rede; o "roteador" entre aspas (uma máquina aleatória executando um cliente VPN) precisa usar o SNAT para que outras máquinas em sua rede possam enviar pacotes de resposta de volta através da VPN. Para os fins desta questão, não estou "no controle" de nenhuma das duas redes: só posso adicionar algumas máquinas com suas próprias regras de roteamento e clientes ou servidores VPN.

Agora, para o problema: digamos que as duas redes (nem na Internet pública) de fato se sobrepõem em suas faixas reais. Portanto, neste exemplo, a sub-rede privada não é 192.168.33.11, mas também 10.0.77.0/24. E vamos supor que renumerar qualquer rede simplesmente não seja uma opção. Portanto, além de usar o SNAT para permitir que o roteador encaminhe os pacotes, preciso remapear a rede privada do roteador para um intervalo de IP diferente da perspectiva do servidor OpenVPN. Digamos que eu escolha 10.0.78.0/24 como o intervalo da rede virtual:

route 10.0.78.0 255.255.255.0
push "route 10.0.78.0 255.255.255.0"

e da máquina do servidor OpenVPN eu quero conexões feitas a 10.0.78.11 para passar pelo túnel e serem processadas com SNAT como antes, mas também quero que o endereço de destino na sub-rede do roteador seja 10.0.77.11. Como posso configurar o iptables para fazer isso?

O NETMAP target parecia como era o que eu queria, mas não consegui fazê-lo funcionar:

iptables -t nat -A PREROUTING -i tun0 -j NETMAP --to 10.0.77.0/24
iptables -t nat -A POSTROUTING -s 192.168.252.0/24 -j SNAT --to-source 10.0.77.10

A partir da máquina do servidor OpenVPN eu posso pingar 10.0.78.10 (ou seja, o endereço remapeado do roteador) quando o NETMAP target é adicionado, então ele está fazendo algo.

tcpdump -i tun0

executado no roteador durante esse ping mostra ICMP echo request e ICMP echo reply como esperado. Mas se eu tentar pingar 10.0.78.11 (ou seja, o endereço remapeado de uma máquina de irmão na sub-rede do roteador), eu não recebo respostas, e tcpdump no roteador mostra solicitações, mas não respostas; tcpdump em 10.0.77.11 (a máquina irmão) não mostra nenhum pacote. Também executando no roteador:

iptables -t nat -L -v

mostra pacotes sendo processados por NETMAP , mas nenhum por SNAT . Então, parece que o NETMAP alvo está de alguma forma substituindo o SNAT alvo?

Essencialmente, o que eu quero é que, quando o roteador receber um pacote em tun0 , por exemplo, 192.168.252.5 (o endereço do servidor OpenVPN no túnel) destinado a 10.0.78.11, ele é reescrito em duas maneiras: o endereço de destino é alterado para 10.0.77.11, e o endereço de origem é alterado para 10.0 .77.10 (com uma nova porta de origem sendo selecionada para que o SNAT possa controlar qual conexão é essa). Então, quando 10.0.77.11 envia uma resposta para a porta falsa em 10.0.77.10, o roteador deve reverter o processo, enviando um pacote de volta para 192.168.252.5 em tun0 com o endereço de origem falso de 10.0.78.11. Pode NETMAP fazer isso, ou qualquer outro alvo em iptables pode fazer isso?

Outras coisas que eu tentei sem sucesso: configurar a máquina do roteador para proxy ARP; adicionando o intervalo de rede virtual à tabela de roteamento. Mas parece que essas coisas não deveriam ser necessárias.

Atualização: Eu não me importo com DNS neste contexto - apenas um punhado de máquinas na “sub-rede privada” precisam ser contatadas, então usar endereços IP é aceitável.

    
por Jesse Glick 18.09.2013 / 00:02

3 respostas

2

Essa é uma grande bagunça e, sinceramente, você seria mais bem servido por renumerar do que criando uma rede de NATs feios com endereços conflitantes. As outras soluções que você precisará para suportar isso (por exemplo, DNS split-horizon com múltiplas zonas, possivelmente com atualização automática) serão difíceis e confusas e toda pessoa subsequente que tiver que lidar com esta rede irá amaldiçoar seu nome para sempre e queimar efígies de você e sua equipe.

No entanto, parece que o problema que você está tendo é que os hosts em um lado do NAT (ver? Eu não posso nem descrever os lados do NAT corretamente, porque é confuso) não tem um caminho de volta para os anfitriões do outro lado. Você tem que adicionar uma regra NETMAP para o caminho de retorno também (pacotes recebidos de eth1 eu presumo).

Mas espere! Isso é feito na cadeia de pré-lançamento. Assim, o endereço de destino será definido antes da decisão de roteamento ser feita, o que é praticamente o mesmo que tem que funcionar ... mas o roteador tem duas rotas para a rede "conflitante". Assim, ele priorizará pelos meios usuais (prefixo de correspondência mais curto primeiro; métrica para quebrar os vínculos), fazendo com que alguns pacotes sejam refletidos de volta na rede de onde vieram, em vez de serem roteados através do NAT. Eles também serão fonte-natted. Infelizmente, o endereço de origem não é usado no roteamento, portanto você não pode usar o endereço de origem. A interface de entrada não é usada no roteamento, e uma vez que a decisão de roteamento é feita, você não pode reescrever o endereço de destino novamente.

Então, você descobre que isso não pode ser feito. Você tem duas opções. Preferencialmente, você renumeraria uma das sub-redes com o conflito de endereço de rede, porque você é um bom engenheiro de rede, e não um incompetente, e faz redes que não são desnecessariamente complicadas. A renumeração de sub-redes VPN tende a ser uma tarefa descomplicada que na pior das hipóteses exigirá o uso de sed , mas talvez você tenha uma dessas situações estranhas em que renumerar qualquer sub-rede é uma tarefa desumanamente difícil. OK.

Se, por qualquer motivo, você não puder fazer isso, precisará de um roteador adicional para usar o seu DNS de horizonte dividido. Configure um roteador para cada sub-rede (na verdade, isso significa um roteador para os clientes VPN e um roteador para os hosts na rede que, infelizmente, usa o prefixo que você pifou para a VPN). Escolha outro intervalo de endereços (e teria que ser outro que você poderia renumerar uma das sub-redes para ...) para ser o "estrangeiro".

Agora, suponha que chamamos a rede com o lado A dos hosts VPN e a rede com os hosts não-VPN B. Suponha que no roteador A você escolha o prefixo "estrangeiro" como 192.0.2.0/24 e roteador B, é 198.51.100.0/24. Esses serão os prefixos falsos que você usa para os hosts dessa sub-rede para contatar os hosts com o prefixo conflitante na outra sub-rede (não use esses locais para fins de documentação no RFC5737).

As regras abaixo são complicadas porque você não pode usar a interface de entrada como um predicado na cadeia POSTROUTING, e a sub-rede de origem não é exclusiva, portanto, precisamos impedir a falsificação usando uma regra de filtro. Também é confuso porque o NETMAP decide se altera o endereço de origem ou de destino com base na cadeia em que está.

No roteador A e B, adicione uma regra para o tráfego de entrada no link ponto a ponto entre os roteadores que eu chamarei ptp0:

router-a # iptables -t nat -A PREROUTING  -i ptp0 -d 198.51.100.0/24 -j NETMAP --to 10.0.77.0/24
router-a # iptables -t nat -A POSTROUTING         -d 10.0.77.0/24    -j NETMAP --to 192.0.2.0/24
router-a # iptables -t filter -A FORWARD -i \!ptp0 -s 10.0.77.0/24 -j DROP
router-b # iptables -t nat -A PREROUTING  -i ptp0 -d 192.0.2.0/24    -j NETMAP --to 10.0.77.0/24
router-b # iptables -t nat -A POSTROUTING         -d 10.0.77.0/24    -j NETMAP --to 198.51.100.0/24
router-b # iptables -t filter -A FORWARD -i \!ptp0 -s 10.0.77.0/24 -j DROP

Agora, essa parte do problema foi solucionada porque você não tem mais um único roteador que tenha os dois prefixos na tabela de roteamento. Nenhuma solução que requer que você tenha o mesmo prefixo referente a duas redes diferentes com dois hosts diferentes na mesma tabela de roteamento pode funcionar; é intrinsecamente impossível devido a como o roteamento funciona.

Ah, e eu quase me esqueci - acredito que sua regra SNAT não está sendo processada porque nada está combinando, mas é importante lembrar que uma vez que uma conexão é armazenada na tabela de estado NAT, ela não é mais processada pelo SNAT regra e não é mais contado nas estatísticas, se bem me lembro.

Se você realmente não exige que as sub-redes sejam separadas, basta usar a ponte da camada 2. Isso tornará sua vida muito mais fácil.

    
por 18.09.2013 / 08:39
1

Então eu fiz o trabalho de criar um snat-routing-demo autônomo usando o Vagrant. Como controle, inicialmente usei SNAT simples, com os clientes VPN se referindo diretamente ao IP real do serviço, enquanto eu resolvia os detalhes da configuração do OpenVPN e assim por diante. Em seguida, introduzi uma sub-rede virtual e adicionei NETMAP antes de SNAT à tabela:

iptables -t nat -A PREROUTING -d <virtual-subnet> -j NETMAP --to <actual-subnet>

que funcionou. Achei que já havia tentado essa regra relativamente simples em meu ambiente real, mas talvez não tivesse, ou alguma outra coisa tivesse sido quebrada; De qualquer forma, posso fazer com que a regra análoga também funcione "de verdade". Nenhum uso de marcas parece ser necessário, e

iptables -t nat -L -v

agora mostra as duas regras sendo aplicadas.

As regras de firewall que limitam quais hosts e portas na sub-rede em ponte podem ser acessadas a partir dos clientes VPN também parecem funcionar sem modificações, usando os intervalos IP reais, por exemplo,

iptables -A FORWARD -p tcp -d <actual-host> --dport <some-port> -j ACCEPT
iptables -A FORWARD -s <actual-subnet> -j ACCEPT
iptables -P FORWARD DROP
    
por 19.09.2013 / 18:59
0

Eu acredito na minha outra resposta que eu poderia ter entendido mal sua intenção, venha a pensar nisso.

Se eu entendi corretamente, você tem um link de VPN entre dois sites e deve direcionar o tráfego entre os dois. Mas, infelizmente, os dois sites (anteriormente independentes) acabaram usando o mesmo endereço RFC1918 para uma sub-rede, certo? Esta é uma das propriedades emergentes mais irritantes do RFC1918.

Ainda assim, a melhor solução é renumerar uma das sub-redes. No entanto, se você não puder, essa coisa de mapeamento que você está tentando fazer não é uma solução ruim.

Existem, no entanto, alguns erros nas suas regras.

Sua regra SNAT não está registrando muito tráfego porque não corresponde a nada além dos próprios pacotes do roteador do cliente openvpn. O endereço de origem dos pacotes provenientes do link VPN está no intervalo 10.0.77.0/24. Assim, os pacotes atravessam a VPN e saem do outro lado, mas as respostas a eles são enviados para o segmento local, em vez de voltarem pela VPN, e assim são perdidos.

Da mesma maneira que você precisa criar um prefixo de rede para diferenciar o endereço de destino no caminho de encaminhamento, o endereço do caminho inverso também precisa ser diferenciável, o que eu acho que é por que você está usando o SNAT. Então, você deve alterar suas regras (e ser mais específico sobre o que você NETMAP também, assim você não reescrever os primeiros 3 octetos de cada endereço de destino em cada sub-rede que vem através da VPN):

iptables -t mangle -A PREROUTING -i tun0 -j MARK --set-mark 1
iptables -t nat -A PREROUTING -i tun0 -d 10.0.78.0/24 -j NETMAP --to 10.0.77.0/24
iptables -t nat -A POSTROUTING -s 10.0.77.0/24 -m mark --mark 1 -j SNAT --to-source 10.0.77.10

A regra do mangle é necessária porque você não pode usar a interface de entrada como um predicado em uma regra POSTROUTING, então você precisa de alguma outra maneira de determinar que o pacote veio do link VPN e precisa da fonte NAT.

    
por 18.09.2013 / 11:28