Étapes pour limiter les connexions externes au conteneur Docker avec iptables?

20

Mon objectif est de limiter l'accès aux conteneurs Docker à seulement quelques adresses IP publiques. Existe-t-il un processus simple et reproductible pour atteindre mon objectif? Comprendre uniquement les bases d'iptables lors de l'utilisation des options par défaut de Docker, je trouve cela très difficile.

Je voudrais exécuter un conteneur, le rendre visible sur Internet public, mais autoriser uniquement les connexions à partir d'hôtes sélectionnés. Je m'attendrais à définir une politique d'entrée par défaut de REJET et à n'autoriser que les connexions de mes hôtes. Mais les règles et chaînes NAT de Docker se mettent en travers et mes règles INPUT sont ignorées.

Quelqu'un peut-il fournir un exemple de la façon d'atteindre mon objectif compte tenu des hypothèses suivantes?

  • Hôte IP publique 80.80.80.80 sur eth0
  • Hôte IP privé 192.168.1.10 sur eth1
  • docker run -d -p 3306:3306 mysql
  • Bloquer toute connexion à l'hôte / conteneur 3306 sauf à partir des hôtes 4.4.4.4 et 8.8.8.8

Je suis heureux de ne lier le conteneur qu'à l'adresse IP locale, mais j'aurais besoin d'instructions sur la façon de configurer correctement les règles de transfert iptables qui survivent au processus de docker et aux redémarrages de l'hôte.

Merci!

GGGforce
la source

Réponses:

15

Deux choses à garder à l'esprit lorsque vous travaillez avec les règles de pare-feu de Docker:

  1. Pour éviter que vos règles soient encombrées par le docker, utilisez la DOCKER-USERchaîne
  2. Docker effectue le mappage de port dans la PREROUTINGchaîne de la nattable. Cela se produit avant les filterrègles, donc --destet --dportverra l'IP interne et le port du conteneur. Pour accéder à la destination d'origine, vous pouvez utiliser -m conntrack --ctorigdstport.

Par exemple:

iptables -A DOCKER-USER -i eth0 -s 8.8.8.8 -p tcp -m conntrack --ctorigdstport 3306 --ctdir ORIGINAL -j ACCEPT
iptables -A DOCKER-USER -i eth0 -s 4.4.4.4 -p tcp -m conntrack --ctorigdstport 3306 --ctdir ORIGINAL -j ACCEPT
iptables -A DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 3306 --ctdir ORIGINAL -j DROP

REMARQUE: sans --ctdir ORIGINAL, cela correspondrait également aux paquets de réponse qui reviennent pour une connexion du conteneur au port 3306 sur un autre serveur, ce qui n'est certainement pas ce que vous voulez! Vous n'avez pas strictement besoin de cela si, comme moi, votre première règle est -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT, car elle traitera de tous les paquets de réponse, mais il serait plus sûr de l'utiliser --ctdir ORIGINALquand même.

SystemParadox
la source
Doit-il être modifié pour inclure le --ctdir ? J'utilise-m conntrack --ctstate NEW --ctorigdstport 3306 --ctdir ORIGINAL
lonix
@Ionix, oui, même si je viens juste de comprendre pourquoi cela m'a dérouté. J'ai ajouté un peu d'explication.
SystemParadox
1
Notez que le DOCKER-USERtableau par défaut contient l'entrée: -A DOCKER-USER -j RETURNqui s'exécutera avant ce qui précède si vous utilisez -A. Une solution consiste à insérer les règles en tête dans l'ordre inverse avec -I.
BMitch
@BMitch Ou encore mieux , ajoutez toutes les règles dans une nouvelle FILTERSchaîne, et -Iinsérez de nouvelles règles (comme vous l'avez dit), pour y -I INPUT -j FILTERS-I DOCKER-USER -i eth0 -j FILTERS
accéder
@BMitch Cependant, je viens de vérifier mon serveur, et la règle de retour n'est pas là, peut-être que la dernière version de docker ne l'insère plus? Bonne idée à utiliser -Icependant, juste pour être sûr.
lonix
8

Avec Docker v.17.06, il existe une nouvelle chaîne iptables appelée DOCKER-USER. Celui-ci est pour vos règles personnalisées: https://docs.docker.com/network/iptables/

Contrairement à la chaîne DOCKER, elle n'est pas réinitialisée lors de la construction / démarrage des conteneurs. Vous pouvez donc ajouter ces lignes à votre configuration / script iptables pour provisionner le serveur avant même d'installer Docker et de démarrer les conteneurs:

-N DOCKER
-N DOCKER-ISOLATION
-N DOCKER-USER
-A DOCKER-ISOLATION -j RETURN
-A DOCKER-USER -i eth0 -p tcp -m tcp --dport 3306 -j DROP
-A DOCKER-USER -j RETURN

Maintenant, le port pour MySQL est bloqué de l'accès externe (eth0), même si docker ouvre le port pour le monde. (Ces règles supposent que votre interface externe est eth0.)

Finalement, vous devrez d'abord nettoyer iptables, redémarrer le service docker, si vous l'avez trop gâché en essayant de verrouiller le port comme je l'ai fait.

ck1
la source
Je ne comprends pas pourquoi cette table DOCKER-USER est différente de toute autre table ajoutée par l'utilisateur. Elle n'a pas de filtre pré-appliqué, vous devez donc spécifier vous-même les noms d'interface. Si vous créez une "MA-CHAÎNE" et l'insérez dans la chaîne FORWARD, elle aura le même résultat, non?
ColinM
Oui, cela fait une différence, car Docker insère la chaîne DOCKER-USER dans la chaîne FORWARD: -A FORWARD -j DOCKER-USER -A FORWARD -j DOCKER-ISOLATION c'est pourquoi, les instructions personnalisées sont exécutées avant la chaîne DOCKER.
ck1
Notez que si vous utilisez --dportDOCKER-USER, cela doit correspondre à l' IP interne du service de conteneur, pas au port exposé. Celles-ci correspondent souvent mais pas toujours et cela pourrait facilement entrer en conflit avec d'autres services, donc je soutiens toujours que cette solution DOCKER-USER est à moitié cuite.
ColinM
4

MISE À JOUR : Bien que valide en 2015, cette solution n'est plus la bonne façon de le faire.

La réponse semble être dans la documentation de Docker à https://docs.docker.com/articles/networking/#the-world

Les règles de transfert de Docker autorisent toutes les adresses IP externes par défaut. Pour autoriser uniquement une IP ou un réseau spécifique à accéder aux conteneurs, insérez une règle négative en haut de la chaîne de filtrage DOCKER. Par exemple, pour restreindre l'accès externe de telle sorte que seule l'IP source 8.8.8.8 puisse accéder aux conteneurs, la règle suivante pourrait être ajoutée:iptables -I DOCKER -i ext_if ! -s 8.8.8.8 -j DROP

J'ai fini par faire:

iptables -I DOCKER -i eth0 -s 8.8.8.8 -p tcp --dport 3306 -j ACCEPT
iptables -I DOCKER -i eth0 -s 4.4.4.4 -p tcp --dport 3306 -j ACCEPT
iptables -I DOCKER 3 -i eth0 -p tcp --dport 3306 -j DROP

Je n'ai pas touché aux options --iptablesou --icc.

GGGforce
la source
1
Si vous le faites iptables -vnL DOCKER, les ports de destination sont tous les ports du conteneur. Si je comprends bien, cela signifie que les règles ci-dessus n'affecteraient que le port 3306dans le conteneur - c'est-à-dire que si vous étiez dans -p 12345:3306votre conteneur, votre règle serait toujours celle requise pour verrouiller l'accès (c'est --dport 12345-à- dire ne fonctionnerait pas) , car les règles ACCEPT de la chaîne DOCKER sont post-NAT.
sunside
C'est vrai, les règles doivent se rapporter aux ports dans les conteneurs.
GGGforce
1
Hum, c'est un peu moche si vous exécutez plusieurs conteneurs qui utilisent, par exemple, un NGINX interne pour effectuer un proxy inverse (par exemple Zabbix, un équilibreur de charge personnalisé, etc.), car cela vous obligerait à connaître l'IP du conteneur à l'avance. Je suis toujours à la recherche d'une solution à ce problème qui ne nécessite pas --iptables=false, car cela semble être le pire choix de tous.
sunside
Je vous remercie! Vous avez résolu mon problème après plusieurs heures de recherche. Maintenant, je peux enfin emprisonner MySQL juste à mon adresse IP personnelle sans exposer le ventre mou au monde entier.
Matt Cavanagh
1
La chaîne DOCKER n'est pas censée être directement manipulée par l'utilisateur! Utilisez la chaîne DOCKER-USER pour cela. Vérifiez la réponse acceptée.
Paul-Sebastian Manole
3

MISE À JOUR: Bien que cette réponse soit toujours valide, la réponse de @SystemParadox DOCKER-USERen combinaison avec --ctorigdstportest meilleure.

Voici une solution qui persiste bien entre les redémarrages et vous permet d'affecter le port exposé plutôt que le port interne .

iptables -t mangle -N DOCKER-mysql iptables -t mangle -A DOCKER-mysql -s 22.33.44.144/32 -j RETURN iptables -t mangle -A DOCKER-mysql -s 22.33.44.233/32 -j RETURN iptables -t mangle -A DOCKER-mysql -j DROP iptables -t mangle -A PREROUTING -i eth0 -p tcp -m tcp --dport 3306 -j DOCKER-mysql

J'ai construit une image Docker qui utilise cette méthode pour gérer automatiquement les iptables pour vous, en utilisant soit des variables d'environnement, soit dynamiquement avec etcd (ou les deux):

https://hub.docker.com/r/colinmollenhour/confd-firewall/

ColinM
la source