Ensemble de règles Iptables pour qu'un conteneur de docker puisse accéder à un service sur une adresse IP hôte

18

J'ai des problèmes pour accéder à une interface privée hôte (IP) à partir d'un conteneur Docker. Je suis assez certain que c'est lié à mes règles Iptables (ou peut-être au routage). Lorsque j'ajoute le --net=hostdrapeau à docker run, tout fonctionne comme prévu. De même, lorsque je précise que la politique INPUT suit un libéral -P INPUT ACCEPT, les choses fonctionnent aussi comme je m'y attendais. Cependant, ce sont des options indésirables et dangereuses que j'aimerais éviter.

Puisqu'il n'est pas spécifique à mes services (DNS), j'ai exclu cela du problème, car la recherche de cela en combinaison avec docker donne un autre problème (populaire), ajoutant du bruit aux résultats de la recherche.

La liaison de conteneurs Docker n'est pas non plus une option viable, car certains conteneurs doivent être exécutés avec l'option --net = host, empêchant la liaison et je souhaite créer une situation cohérente dans la mesure du possible.

J'ai les règles Iptables suivantes. Une combinaison de CoreOS, Digital Ocean et Docker, je suppose.

-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

Mes interfaces hôtes (pertinentes):

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

Et je lance un conteneur 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.

À ce stade, je veux pouvoir utiliser un service local, lié le 10.129.112.210:53. Pour que ce qui suit donne une réponse:

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

Lorsque j'exécute la même commande depuis mon hôte:

$ 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

Mon 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

Le point ici n'est pas d'accéder aux hôtes publics, mais plutôt aux hôtes internes, en utilisant le service DNS local disponible sur l'hôte (via une autre instance de docker).

Pour l'illustrer encore plus (mes compétences en conception d'art ascii dépassent mes fu iptables, ce qui devrait en dire assez à ce stade):

 ______________________________________________
|  __________________________           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)

J'ai cherché, lu et appliqué différents exemples de configurations Iptables, mais je connais trop peu de règles Iptables plus "avancées" pour comprendre ce qui se passe et ainsi obtenir le résultat souhaité.

Sortie 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

Sortie de cat /proc/sys/net/ipv4/ip_forward:

1
Dynom
la source
Pouvez-vous publier la sortie de iptables -t nat -nL? Avez-vous fait une analyse de paquet, disons faire un ping depuis le conteneur source et utiliser tcpdump pour capturer les paquets sur l'hôte.
Daniel t.
Certainement, merci pour votre aide jusqu'à présent: pastebin.com/TAaT73nk (ne correspondait pas au commentaire ..) - modifier -> Mise à jour du lien vers un pastebin qui n'expire pas.
Dynom
Peut-être que je n'ai pas bien compris votre problème, mais je ne vois aucune règle pour autoriser les requêtes DNS sur l'hôte. De plus, ip_forward est-il activé?
Laurentiu Roescu
Salut @LaurentiuRoescu. $ cat /proc/sys/net/ipv4/ip_forward -> 1et -A INPUT -i eth1 -j ACCEPTaccepte toutes les connexions sur l' interface privée . Quelles règles vous manquent?
Dynom
2
Je pense que les paquets du conteneur proviennent de l'interface docker0, pas eth1. essai-A INPUT -i docker0 -j ACCEPT
Laurentiu Roescu

Réponses:

14

Le conteneur communique avec l'hôte à l'aide de l' docker0interface. Pour autoriser le trafic provenant du conteneur, ajoutez:

-A INPUT -i docker0 -j ACCEPT
Laurentiu Roescu
la source
2
Dynom, une leçon que vous voudrez peut-être en tirer est que l'enregistrement de tous vos refus est utile, par exemple iptables -A INPUT -j LOG. Le timbre IN=docker0aurait été très utile pour déterminer quelle modification de règle était nécessaire. À ne pas retirer du travail de Laurentiu, qui était excellent - +1 de ma part!
MadHatter prend en charge Monica le
5
Pour les personnes qui utilisent UFW, voici ce que j'ai fait pour autoriser toutes les communications des conteneurs Docker à héberger: ufw allow in on docker0
Ali Ok
0

J'ai rencontré une situation très similaire, mais l'ajout -A INPUT -i docker0 -j ACCEPTouvrira tous les accès sur mon interface eth0 de l'hôte docker aux conteneurs, ce qui n'est absolument pas ce que je voulais.

Et comme j'ai remarqué que mon conteneur avait juste un accès limité (disons seulement le port 22) à l'interface hôte au lieu d'être totalement fermé du réseau hôte, j'ai passé en revue mes règles iptables et trouvé une règle dans la chaîne IN_public_allow qui devrait être responsable de cela. La règle est -A IN_public_allow -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT. J'ai donc ajouté des règles similaires pour permettre à mon conteneur d'accéder aux autres ports hôtes souhaités, ce qui, je pense, pourrait être un moyen un peu plus précis d'ouvrir l'accès au réseau hôte aux conteneurs.

L3w1s
la source
-i docker0devrait garantir que cela n'affectera pas le trafic qui n'arrive pas via le réseau docker0. Votre grammaire n'est cependant pas claire. Peut-être que vous disiez que l'accès sortant des hôtes Docker via eth0 était activé, ce qui pourrait être vrai. Je suis d'accord que des règles plus ciblées ne sont possibles que si vous en avez besoin.
mc0e