forçar o tráfego de saída do contêiner através de outro contêiner (sem regras de iptables do host)

1

Eu tenho dois contêineres, A & B .

Eles devem existir em uma rede de pontes docker (privada) recém-criada, de tal forma que:

  • A pode falar com o mundo fora da ponte, mas somente via IP do gateway especificado
  • B não pode falar fora da bridge, apenas para A via ip + port específico
  • Prefira não exigir o uso de regras do iptables no host .
  • Prefira não exigir --privileged em A ou B , mas --device específica pode ser OK
  • Estou bem em usar --cap-drop em B para obter o isolamento desejado

Eu brinquei um pouco com docker network create bridge private-bridge e várias combinações de docker run -net=private-bridge , mas não consigo o comportamento que estou procurando.

Existe alguma maneira de:

  • faça isso com um docker bridge network ?, ou
  • faz isso com um tipo de rede padrão diferente ?, ou
  • devo procurar escrever um plug-in de rede de encaixe personalizado?
por David-SkyMesh 26.04.2016 / 15:31

1 resposta

1

Parece que alguns tipos de SDN foram necessários, mas link foi muito mais simples indo do que outros como Contiv, etc.

Nenhum privilégio de contêiner extra era necessário, e os contêineres (até mesmo como root) não podiam manipular a ponte nem suas próprias interfaces com os privilégios padrão do contêiner do Docker.

Primeiramente, instale pipework & brctl

sudo curl https://raw.githubusercontent.com/jpetazzo/pipework/master/pipework \
          > /usr/local/bin/pipework
sudo chmod u+x /usr/local/bin/pipework
sudo apt-get install bridge-utils

Antes de começar, verifique as interfaces e pontes atualmente definidas:

brctl show | tail -n+2 | awk '{ print $1 }' | xargs echo 
# docker0

ifconfig | grep encap | awk '{ print $1 }' | xargs echo
# eth0 docker0 lo

Para esta demonstração, criarei uma imagem chamada net-tester com a funcionalidade básica de teste de rede incluída:

docker run -itd --name=jtest debian:jessie
docker exec -it jtest apt-get update
docker exec -it jtest apt-get install -y traceroute curl dnsutils \
                                         netcat-openbsd jq nmap \
                                         net-tools isc-dhcp-client telnet
docker exec -it jtest apt-get clean
docker commit -p jtest net-tester
docker stop jtest && docker rm jtest

Inicie os dois contêineres na janela de encaixe padrão none network:

docker run -itd --net=none --name=node-a net-tester
docker run -itd --net=none --name=node-b net-tester

docker exec -it node-a ifconfig | grep encap | awk '{ print $1 }' | xargs echo
# lo

docker exec -it node-b ifconfig | grep encap | awk '{ print $1 }' | xargs echo
# lo

Crie br0 bridge e adicione interfaces em br0 a node-a e node-b .

sudo pipework br0 node-a 192.168.10.1/24
sudo pipework br0 node-b 192.168.10.2/24

docker exec -it node-a ifconfig | grep encap | awk '{ print $1 }' | xargs echo
# eth1 lo

docker exec -it node-b ifconfig | grep encap | awk '{ print $1 }' | xargs echo
# eth1 lo

(Implicitamente nomeada) interface eth1 foi criada em ambos os contêineres.

Vamos ver agora as pontes e interfaces do host:

brctl show | tail -n+2 | awk '{ print $1 }' | xargs echo
# br0 veth1pl31667 docker0

ifconfig | grep encap | awk '{ print $1 }' | xargs echo
# br0 docker0 eth0 lo veth1pl31645 veth1pl31667

Vamos ver o roteamento nesses contêineres:

docker exec -it node-a route -n
# Kernel IP routing table
# Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
# 192.168.10.0    0.0.0.0         255.255.255.0   U     0      0        0 eth1

docker exec -it node-b route -n
# Kernel IP routing table
# Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
# 192.168.10.0    0.0.0.0         255.255.255.0   U     0      0        0 eth1

Por fim, adicionamos a interface eth0 a node-b na sub-rede do host.

No meu caso: eth0 10.0.0.0/24 gw 10.0.0.1.

sudo pipework eth0 -i eth0 node-b 10.0.0.99/[email protected]

Aparentemente, isso também pode ser feito com o dhcp.

Agora, a verificação de interfaces em node-b fornece:

docker exec -it node-b ifconfig | grep encap | awk '{ print $1 }' | xargs echo
# eth0 eth1 lo

O roteamento se parece com isso:

docker exec -it node-b route -n
# Kernel IP routing table
# Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
# 0.0.0.0         10.0.0.1        0.0.0.0         UG    0      0        0 eth0
# 10.0.0.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0
# 192.168.10.0    0.0.0.0         255.255.255.0   U     0      0        0 eth1

Teste a conectividade de node-a :

docker exec -it node-a ping -c 3 192.168.10.2
# PING 192.168.10.2 (192.168.10.2): 56 data bytes
# 64 bytes from 192.168.10.2: icmp_seq=0 ttl=64 time=0.124 ms
# 64 bytes from 192.168.10.2: icmp_seq=1 ttl=64 time=0.101 ms
# 64 bytes from 192.168.10.2: icmp_seq=2 ttl=64 time=0.092 ms
# --- 192.168.10.2 ping statistics ---
# 3 packets transmitted, 3 packets received, 0% packet loss
# round-trip min/avg/max/stddev = 0.092/0.106/0.124/0.000 ms

docker exec -it node-a ping -c 3 10.0.0.1
# PING 10.0.0.1 (10.0.0.1): 56 data bytes
# ping: sending packet: Network is unreachable

docker exec -it node-a ping -c 3 8.8.8.8
# PING 8.8.8.8 (8.8.8.8): 56 data bytes
# ping: sending packet: Network is unreachable

Teste a conectividade de node-b :

docker exec -it node-b ping -c 3 192.168.10.1
# PING 192.168.10.1 (192.168.10.1): 56 data bytes
# 64 bytes from 192.168.10.1: icmp_seq=0 ttl=64 time=0.102 ms
# 64 bytes from 192.168.10.1: icmp_seq=1 ttl=64 time=0.086 ms
# 64 bytes from 192.168.10.1: icmp_seq=2 ttl=64 time=0.087 ms
# --- 192.168.10.1 ping statistics ---
# 3 packets transmitted, 3 packets received, 0% packet loss
# round-trip min/avg/max/stddev = 0.086/0.092/0.102/0.000 ms

docker exec -it node-b ping -c 3 10.0.0.1
# PING 10.0.0.1 (10.0.0.1): 56 data bytes
# 64 bytes from 10.0.0.1: icmp_seq=0 ttl=64 time=0.312 ms
# 64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=0.314 ms
# 64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=0.289 ms
# --- 10.0.0.1 ping statistics ---
# 3 packets transmitted, 3 packets received, 0% packet loss
# round-trip min/avg/max/stddev = 0.289/0.305/0.314/0.000 ms

docker exec -it node-b ping -c 3 8.8.8.8
# PING 8.8.8.8 (8.8.8.8): 56 data bytes
# 64 bytes from 8.8.8.8: icmp_seq=0 ttl=56 time=19.309 ms
# 64 bytes from 8.8.8.8: icmp_seq=1 ttl=56 time=18.279 ms
# 64 bytes from 8.8.8.8: icmp_seq=2 ttl=56 time=19.827 ms
# --- 8.8.8.8 ping statistics ---
# 3 packets transmitted, 3 packets received, 0% packet loss
# round-trip min/avg/max/stddev = 18.279/19.138/19.827/0.643 ms

Curiosamente, enquanto node-b pode fazer ping de outros IPs na sub-rede ethernet do host, ele não pode fazer o ping do próprio eth0 IP dos hosts. Isso é realmente o que eu queria, então eu não estou incomodado.

Quando os dois contêineres estiverem parados, podemos limpar a ponte:

sudo ifconfig br0 down
sudo brctl delbr br0

Para meu cenário específico, se eu vincular um daemon a 0.0.0.0 on node-b , que ficará visível para todos no host ethernet (via 10.0.0.99 ), então tive que tomar cuidado para vincular especificamente a 192.168.10.2 .

    
por 27.04.2016 / 15:20