Roteando a partir de contêineres do docker usando uma interface de rede física diferente e um gateway padrão

13

Informações básicas

Eu tenho um servidor com duas interfaces de rede executando o Docker. O Docker, como algumas ferramentas de virtualização, cria uma interface de ponte do Linux chamada docker0. Essa interface é configurada por padrão com um IP 172.17.42.1e todos os contêineres do Docker se comunicam com essa interface como gateway e recebem endereços IP no mesmo /16intervalo. Pelo que entendi, todo o tráfego de rede de / para contêineres passa por um NAT, de maneira que parece ser de saída 172.17.42.1e de entrada para o qual é enviado 172.17.42.1.

Minha configuração é assim:

                                          +------------+        /
                                          |            |       |
                            +-------------+ Gateway 1  +-------
                            |             | 10.1.1.1   |     /
                     +------+-------+     +------------+    |
                     |     eth0     |                      /
                     |   10.1.1.2   |                      |
                     |              |                      |
                     | DOCKER HOST  |                      |
                     |              |                      | Internet
                     |   docker0    |                      |
                     |   (bridge)   |                      |
                     |  172.17.42.1 |                      |
                     |              |                      |
                     |     eth1     |                      |
                     |  192.168.1.2 |                      \
                     +------+-------+     +------------+    |
                            |             |            |     \
                            +-------------+ Gateway 2  +-------
                                          | 192.168.1.1|       |
                                          +------------+            

O problema

Desejo rotear todo o tráfego de / para qualquer contêiner do Docker da segunda eth1 192.168.1.2interface para um gateway padrão 192.168.1.1, enquanto todo o tráfego da / para a máquina host sai da eth0 10.1.1.2interface para um gateway padrão de 10.1.1.1. Eu tentei várias coisas até agora sem sucesso, mas a única coisa que acho que é a mais próxima de corrigir é usar o iproute2 da seguinte maneira:

# Create a new routing table just for docker
echo "1 docker" >> /etc/iproute2/rt_tables

# Add a rule stating any traffic from the docker0 bridge interface should use 
# the newly added docker routing table
ip rule add from 172.17.42.1 table docker

# Add a route to the newly added docker routing table that dictates all traffic
# go out the 192.168.1.2 interface on eth1
ip route add default via 192.168.1.2 dev eth1 table docker

# Flush the route cache
ip route flush cache

# Restart the Docker daemon so it uses the correct network settings
# Note, I do this as I found Docker containers often won't be able
# to connect out if any changes to the network are made while it's     
# running
/etc/init.d/docker restart

Quando trago um contêiner, não consigo sair dele depois de fazer isso. Não tenho certeza se as interfaces de ponte são tratadas da mesma maneira que as interfaces físicas são para esse tipo de roteamento, e só quero uma verificação de sanidade, além de dicas de como eu posso realizar essa tarefa aparentemente simples.


fonte
Seu comentário sobre o NAT não está certo. Ele está definido para mascarar no endereço de origem 172.17.0.0/16 onde a interface de saída não é docker0. ie -A POSTROUTING -s 172.17.0.0/16! -o docker0 -j MASQUERADE. O que significa que, se o pacote originário de um contêiner do docker sair por meio de eth0 ou eth1, ele assumirá o endereço IP da interface, não o IP 172.17.xx.
Matt
Obrigado por seu comentário. Então isso mudaria minha abordagem para rotear para fora de uma interface específica, correto? Você sabe como eu poderia fazer isso? Tal como está, o Docker parece usar apenas o gateway padrão do sistema host, e esse não é o comportamento desejado. Quero que todas as coisas que o Docker direcione para fora de uma interface específica.
Não sei por que o que sugeri não está funcionando. Mas outra opção poderia ser usar tubulações. github.com/jpetazzo/pipework Também pode ser útil ler a seção de rede avançada da ajuda do docker. docs.docker.com/articles/networking
Matt
@ Matt Agradeço suas sugestões sobre isso. Pelo que li, a tubulação requer comandos adicionais depois que um contêiner é iniciado para obter acesso ao gateway correto, o que parece ser uma vulnerabilidade de segurança. Foi originalmente criado para conectar contêineres em vários hosts, o que realmente não é meu objetivo aqui. Quanto aos documentos avançados de rede do Docker, analisarei novamente, mas até agora não foi útil nessa situação (eu o li antes de postar aqui). Vou postar um link para este post na página do Docker Github Issues e ver como ele sai daqui.
Aqui está um link para a Emissão Github: github.com/docker/docker/issues/13762

Respostas:

1

Você pode ter que procurar mais na configuração do iptables também. O Docker mascara todo o tráfego originado da sub-rede do contêiner, digamos 172.17.0.0/16, para 0.0.0.0. Se você executar iptables -L -n -t nat, poderá ver a cadeia POSTROUTING na tabela nat, que faz isso -

Cadeia POSTROUTING (política ACEITAR)
destino de origem da proteção de destino
MASQUERADE tudo - 172.17.0.0/16 0.0.0.0/0

Agora, você pode remover esta regra e substituí-la por uma regra que mascara todo o tráfego originado da sub-rede dos contêineres para o IP - 192.168.1.2 da sua segunda interface, pois é isso que você deseja. A regra de exclusão será, assumindo que seja a primeira regra na cadeia POSTROUTING -

iptables -t nat -D POSTROUTING 1

Então você adiciona esta regra personalizada -

iptables -t nat -A POSTROUTING -s 172.17.0.0/16 -j SNAT - para fonte 192.168.1.2
Daniel t.
fonte
Na verdade, ele o combina na interface de saída. Essa parte de destino está apenas dizendo que qualquer tráfego originado da sub-rede de origem para qualquer destino será mascarado.
Matt
Infelizmente isso não funcionou. Meu processo completo era correr: ip rule add from 172.17.0.0/16 table docker ip route add default via 192.168.1.2 dev eth1 table docker ip route flush cache iptables -t nat -D POSTROUTING 1 iptables -t nat -A POSTROUTING -s 172.17.0.0/16 -j SNAT --to-source 192.168.1.2 /etc/init.d/docker restart. Tentei executar um ping e um traceroute de um contêiner e não consegui alcançar nada.
Eu tinha um requisito semelhante, o host possui uma configuração secundária de IP para acessar um IP público. E eu queria que o contêiner estivador seguisse o mesmo. Isso funcionou nesse caso: "iptables -t nat -I POSTROUTING 1 -s 172.17.0.0/16 -d PUBIP / 32 -j SNAT --to-source SECONDARYIP"
Ram
1

Um amigo e eu encontramos esse problema exato em que queríamos que o docker suporte várias interfaces de rede para atender a solicitações. Estávamos trabalhando especificamente com o serviço AWS EC2, onde também estávamos conectando / configurando / trazendo as interfaces adicionais. Neste projeto , há mais do que você precisa, então tentarei incluir apenas o que você precisa aqui.

Primeiro, o que fizemos foi criar uma tabela de rotas separada para eth1 :

ip route add default via 192.168.1.2 dev eth1 table 1001

Em seguida, configuramos a tabela mangle para definir algumas marcas de conexão provenientes de eth1:

iptables -t mangle -A PREROUTING -i eth1 -j MARK --set-xmark 0x1001/0xffffffff
iptables -t mangle -A PREROUTING -i eth1 -j CONNMARK --save-mark --nfmask 0xffffffff --ctmask 0xffffffff

Finalmente, adicionamos esta regra para todos os fwmarks usarem a nova tabela que criamos.

ip rule add from all fwmark 0x1001 lookup 1001

O iptablescomando abaixo restaurará a marca de conexão e permitirá que a regra de roteamento use a tabela de roteamento correta.

iptables -w -t mangle -A PREROUTING -i docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff

Acredito que isso é tudo o que é necessário em nosso exemplo mais complexo, onde (como eu disse) nosso projeto foi anexar / configurar / exibir o eth1 interface no momento da inicialização.

Agora, este exemplo não impediria conexões de eth0atender solicitações até, docker0mas acredito que você poderia adicionar uma regra de roteamento para impedir isso.

williamsbdev
fonte
@stuntmachine apenas curioso se você tivesse a chance de tentar isso? Se você já descobriu algo por conta própria, seria capaz de compartilhar sua solução?
williamsbdev
Oi @williamsbdev - post antigo e um tiro no escuro, mas você acha que essa solução também funcionaria para o meu problema no SO? stackoverflow.com/questions/51312310/…
Jolly Roger
2
Jolly Roger - Eu acredito que isso resolverá seu problema. Recentemente, outra equipe me perguntou sobre este post do blog (essencialmente a mesma solução que aqui) e eles disseram que funcionou muito bem. williamsbdev.com/posts/docker-connection-marking
williamsbdev
0

O baile de máscaras não é de 172.17.42.1, mas sim

 -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

O que significa que esta regra não funcionará corretamente.

ip rule add from 172.17.42.1 table docker

Experimente

ip rule add from 172.17.0.0/16 table docker
Matt
fonte
Infelizmente isso não funcionou. Meus contêineres ainda estavam roteando para o mesmo gateway padrão que o host. Aqui está o que eu corri: ip rule add from 172.17.0.0/16 table docker ip route add default via 192.168.1.2 dev eth1 table docker ip route flush cache /etc/init.d/docker restart De dentro do recipiente Corri um traceroute e o primeiro salto foi 10.1.1.1, quando deveria ter sido 192.168.1.1