Conjunto de regras de iptables para que um contêiner de docker possa acessar um serviço em um IP de host

18

Tenho problemas para acessar uma interface privada de host (ip) de um contêiner de docker. Estou bastante certo de que está relacionado às minhas regras do Iptables (ou talvez roteamento). Quando adiciono o --net=hostsinalizador a docker run, tudo funciona como esperado. Da mesma forma, quando especifico que a política INPUT está seguindo um liberal -P INPUT ACCEPT, as coisas também funcionam como eu esperaria. No entanto, essas são opções indesejáveis ​​e inseguras que eu gostaria de evitar.

Como não é específico para meus serviços (DNS), excluí-o do problema, pois a pesquisa em combinação com o docker gera uma área diferente (popular) do problema, adicionando ruído aos resultados da pesquisa.

Também a vinculação de contêineres do Docker não é uma opção viável, porque certos contêineres precisam ser executados com a opção --net = host, impedindo a vinculação e desejo criar uma situação consistente sempre que possível.

Eu tenho as seguintes regras do Iptables. Uma combinação de CoreOS, Digital Ocean e Docker, suponho.

-P INPUT DROP
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-N DOCKER
-A INPUT -i lo -j ACCEPT
-A INPUT -i eth1 -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 0 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 3 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 11 -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT

Minhas interfaces de host (relevantes):

3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    inet 10.129.112.210/16 brd 10.129.255.255 scope global eth1
       valid_lft forever preferred_lft forever
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    inet 172.17.42.1/16 scope global docker0
       valid_lft forever preferred_lft forever

E eu corro um contêiner de docker:

$ docker run --rm -it --dns=10.129.112.210 debian:jessie # Specifying the DNS is so that the public DNS servers aren't used.

Neste ponto, desejo poder usar um serviço local, vinculado a 10.129.112.210:53. Para que o seguinte deva dar uma resposta:

$ ping google.com
^C
$ ping user.skydns.local
^C

Quando executo o mesmo comando do meu host:

$ ping photo.skydns.localPING photo.skydns.local (10.129.112.206) 56(84) bytes of data.
64 bytes from 10.129.112.206: icmp_seq=1 ttl=64 time=0.790 ms
^C

Meu resolv.conf

$ cat /etc/resolv.conf
nameserver 10.129.112.210
nameserver 127.0.0.1
nameserver 8.8.8.8
nameserver 8.8.4.4

O objetivo aqui não é acessar hosts públicos, mas internos, usando o serviço DNS local disponível no host (por meio de outra instância do docker).

Para ilustrá-lo ainda mais (minhas habilidades de design de arte ascii superam meu iptables fu, de modo que deve ser suficiente neste momento):

 ______________________________________________
|  __________________________           Host   |
| |   Docker DNS container   |                 |
|  ``````````````````````|```                  |
|                        |                     |
|     ,----------,---( private n. interface )  |
|     |          |                             |
|     |          |   ( public  n. interface )---
|     |          |                             |
|     |          |   ( loopbck n. interface )  |
|     |          |                             |
|     |          |                             |
|     |        __|_______________________      |
|     |       | Docker service container |     |
|     |        ``````````````````````````      |
|     |                                        |
|     |                                        |
| [ Local host service using DNS. ]            |
|                                              |
|______________________________________________|

  private (host) network interface: eth1 (10.129.0.0/16)
  Docker network interface: docker0 (172.17.0.0/16)

Pesquisei, li e apliquei diferentes exemplos de configurações do Iptables, mas conheço muito pouco das regras mais avançadas do Iptables para entender o que está acontecendo e, assim, obter o resultado desejado.

Saída de iptables -t nat -nL:

Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  0.0.0.0/0           !127.0.0.0/8          ADDRTYPE match dst-type LOCAL

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  172.17.0.0/16        0.0.0.0/0

Chain DOCKER (2 references)
target     prot opt source               destination

Saída de cat /proc/sys/net/ipv4/ip_forward:

1
Dynom
fonte
Você pode postar a saída de iptables -t nat -nL? Você fez alguma análise de pacote, por exemplo, execute um ping no contêiner de origem e use tcpdump para capturar os pacotes no host.
Daniel t.
Certamente, obrigado por ajudar até agora: pastebin.com/TAaT73nk (Não se encaixou no comentário ..) - editar -> Atualizou o link para um pastebin que não expira.
Dynom 27/07/2015
Talvez eu não tenha entendido corretamente o seu problema, mas não vejo nenhuma regra para permitir consultas DNS no host. Além disso, o ip_forward está ativado?
Laurentiu Roescu
Oi @LaurentiuRoescu. $ cat /proc/sys/net/ipv4/ip_forward -> 1e -A INPUT -i eth1 -j ACCEPTaceita todas as conexões na interface privada . Que regras você está perdendo?
Dynom 27/07/2015
2
Eu acho que os pacotes do contêiner vêm da interface docker0, não da eth1. tente-A INPUT -i docker0 -j ACCEPT
Laurentiu Roescu

Respostas:

14

O contêiner se comunica com o host usando a docker0interface. Para permitir o tráfego do contêiner, adicione:

-A INPUT -i docker0 -j ACCEPT
Laurentiu Roescu
fonte
2
Dynom, uma lição que você pode tirar disso é que registrar todas as suas recusas é útil, por exemplo iptables -A INPUT -j LOG. O selo IN=docker0teria sido muito útil para descobrir qual ajuste de regra era necessário. Para não tirar o trabalho de Laurentiu, que foi excelente - +1 de mim!
MadHatter apoia Monica
5
Para as pessoas que usam UFW, aqui está o que eu fiz para permitir que toda a comunicação de contentores Docker para host: ufw permitir que dentro em docker0
Ali Ok
0

Eu encontrei uma situação muito semelhante, mas a adição -A INPUT -i docker0 -j ACCEPTabrirá todos os acessos na minha interface eth0 do host do docker para contêineres, o que não é absolutamente o que eu pretendia.

E como notei que meu contêiner tinha apenas acesso limitado (digamos, apenas a porta 22) à interface do host em vez de desligar totalmente da rede do host, revisei minhas regras do iptables e encontrei uma regra na cadeia IN_public_allow que deveria ser responsável por isso. A regra é -A IN_public_allow -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT. Por isso, adicionei regras semelhantes para permitir que meu contêiner acesse outras portas de host desejadas, o que acho que poderia ser uma maneira um pouco mais precisa de abrir o acesso à rede de contêineres.

L3w1s
fonte
-i docker0deve garantir que isso não afete o tráfego que não chega pela rede docker0. Sua gramática não é clara. Talvez você estivesse dizendo que o acesso de saída dos hosts do docker via eth0 estava ativado, o que pode ser verdade. Concordo que regras mais direcionadas são possíveis, abrindo apenas o necessário.
mc0e 11/04