⚠️ Traduction non officielle - Cette documentation est une traduction communautaire non officielle de Docker.

Filtrage de paquets et pare-feux

Sur Linux, Docker crée des règles iptables et ip6tables pour implémenter l'isolation réseau, la publication et le filtrage de ports.

Parce que ces règles sont nécessaires au bon fonctionnement des réseaux bridge Docker, vous ne devriez pas modifier les règles créées par Docker.

Mais, si vous exécutez Docker sur un hôte exposé à internet, vous voudrez probablement ajouter des politiques iptables qui empêchent l'accès non autorisé aux conteneurs ou autres services s'exécutant sur votre hôte. Cette page décrit comment y parvenir, et les mises en garde dont vous devez être conscient.

Note

Docker crée des règles iptables pour les réseaux bridge.

Aucune règle iptables n'est créée pour le réseau ipvlan, macvlan ou host.

Docker et les chaînes iptables

Dans la table filter, Docker définit la politique par défaut à DROP, et crée les chaînes iptables personnalisées suivantes :

  • DOCKER-USER
    • Un espace réservé pour les règles définies par l'utilisateur qui seront traitées avant les règles dans les chaînes DOCKER-FORWARD et DOCKER.
  • DOCKER-FORWARD
    • La première étape de traitement pour les réseaux de Docker. Règles qui passent les paquets qui ne sont pas liés aux connexions établies aux autres chaînes Docker, ainsi que les règles pour accepter les paquets qui font partie de connexions établies.
  • DOCKER
    • Règles qui déterminent si un paquet qui ne fait pas partie d'une connexion établie doit être accepté, basé sur la configuration de transfert de port des conteneurs en cours d'exécution.
  • DOCKER-ISOLATION-STAGE-1 et DOCKER-ISOLATION-STAGE-2
    • Règles pour isoler les réseaux Docker les uns des autres.
  • DOCKER-INGRESS
    • Règles liées au réseau Swarm.

Dans la chaîne FORWARD, Docker ajoute des règles qui sautent inconditionnellement vers les chaînes DOCKER-USER, DOCKER-FORWARD et DOCKER-INGRESS.

Dans la table nat, Docker crée la chaîne DOCKER et ajoute des règles pour implémenter le masquerading et le mappage de ports.

Ajouter des politiques iptables avant les règles de Docker

Les paquets qui sont acceptés ou rejetés par les règles dans ces chaînes personnalisées ne seront pas vus par les règles définies par l'utilisateur ajoutées à la chaîne FORWARD. Donc, pour ajouter des règles supplémentaires pour filtrer ces paquets, utilisez la chaîne DOCKER-USER.

Les règles ajoutées à la chaîne FORWARD seront traitées après les règles de Docker.

Faire correspondre l'IP et les ports originaux pour les requêtes

Quand les paquets arrivent à la chaîne DOCKER-USER, ils ont déjà passé à travers un filtre de Traduction d'Adresse Réseau de Destination (DNAT). Cela signifie que les drapeaux iptables que vous utilisez ne peuvent correspondre qu'aux adresses IP internes et ports des conteneurs.

Si vous voulez faire correspondre le trafic basé sur l'IP et le port originaux dans la requête réseau, vous devez utiliser l' extension iptables conntrack. Par exemple :

$ sudo iptables -I DOCKER-USER -p tcp -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
$ sudo iptables -I DOCKER-USER -p tcp -m conntrack --ctorigdst 198.51.100.2 --ctorigdstport 80 -j ACCEPT
Important

L'utilisation de l'extension conntrack peut entraîner une dégradation des performances.

Publication et mappage de ports

Par défaut, pour IPv4 et IPv6, le démon bloque l'accès aux ports qui n'ont pas été publiés. Les ports de conteneurs publiés sont mappés aux adresses IP de l'hôte. Pour ce faire, il utilise iptables pour effectuer la Traduction d'Adresse Réseau (NAT), la Traduction d'Adresse de Port (PAT), et le masquerading.

Par exemple, docker run -p 8080:80 [...] crée un mappage entre le port 8080 sur n'importe quelle adresse sur l'hôte Docker, et le port 80 du conteneur. Les connexions sortantes du conteneur masqueront, en utilisant l'adresse IP de l'hôte Docker.

Restreindre les connexions externes aux conteneurs

Par défaut, toutes les IP sources externes sont autorisées à se connecter aux ports qui ont été publiés aux adresses de l'hôte Docker.

Pour permettre seulement à une IP ou un réseau spécifique d'accéder aux conteneurs, insérez une règle niée en haut de la chaîne de filtres DOCKER-USER. Par exemple, la règle suivante supprime les paquets de toutes les adresses IP sauf 192.0.2.2 :

$ iptables -I DOCKER-USER -i ext_if ! -s 192.0.2.2 -j DROP

Vous devrez changer ext_if pour correspondre à l'interface externe réelle de votre hôte. Vous pourriez à la place permettre les connexions depuis un sous-réseau source. La règle suivante permet seulement l'accès depuis le sous-réseau 192.0.2.0/24 :

$ iptables -I DOCKER-USER -i ext_if ! -s 192.0.2.0/24 -j DROP

Finalement, vous pouvez spécifier une plage d'adresses IP à accepter en utilisant --src-range (N'oubliez pas d'ajouter aussi -m iprange quand vous utilisez --src-range ou --dst-range) :

$ iptables -I DOCKER-USER -m iprange -i ext_if ! --src-range 192.0.2.1-192.0.2.3 -j DROP

Vous pouvez combiner -s ou --src-range avec -d ou --dst-range pour contrôler à la fois la source et la destination. Par exemple, si l'hôte Docker a les adresses 2001:db8:1111::2 et 2001:db8:2222::2, vous pouvez faire des règles spécifiques à 2001:db8:1111::2 et laisser 2001:db8:2222::2 ouvert.

Vous pourriez avoir besoin de permettre les réponses des serveurs en dehors des plages d'adresses externes autorisées. Par exemple, les conteneurs peuvent envoyer des requêtes DNS ou HTTP à des hôtes qui ne sont pas autorisés à accéder aux services du conteneur. La règle suivante accepte tout paquet entrant ou sortant appartenant à un flux qui a déjà été accepté par d'autres règles. Elle doit être placée avant les règles DROP qui restreignent l'accès depuis les plages d'adresses externes.

$ iptables -I DOCKER-USER -m state --state RELATED,ESTABLISHED -j ACCEPT

iptables est compliqué. Il y a beaucoup plus d'informations sur Netfilter.org HOWTO.

Routage direct

Le mappage de ports assure que les ports publiés sont accessibles sur les adresses réseau de l'hôte, qui sont susceptibles d'être routables pour tous les clients externes. Aucune route n'est normalement configurée dans le réseau de l'hôte pour les adresses de conteneurs qui existent dans un hôte.

Mais, particulièrement avec IPv6, vous pourriez préférer éviter d'utiliser NAT et à la place organiser le routage externe vers les adresses de conteneurs ("routage direct").

Pour accéder aux conteneurs sur un réseau bridge depuis l'extérieur de l'hôte Docker, vous devez d'abord configurer le routage vers le réseau bridge via une adresse sur l' hôte Docker. Ceci peut être accompli en utilisant des routes statiques, le Protocole de Passerelle Frontière (BGP), ou tout autre moyen approprié pour votre réseau. Par exemple, dans un réseau de couche 2 local, les hôtes distants peuvent configurer des routes statiques vers un réseau de conteneurs via l'adresse de l'hôte du démon Docker sur le réseau local.

Routage direct vers les conteneurs dans les réseaux bridge

Par défaut, les hôtes distants ne sont pas autorisés à accéder directement aux adresses IP de conteneurs dans les réseaux bridge Linux de Docker. Ils ne peuvent accéder qu'aux ports publiés aux adresses IP de l'hôte.

Pour permettre l'accès direct à tout port publié, sur tout conteneur, dans tout réseau bridge Linux, utilisez l'option de démon "allow-direct-routing": true dans /etc/docker/daemon.json ou l'équivalent --allow-direct-routing.

Pour permettre le routage direct depuis n'importe où vers les conteneurs dans un réseau bridge spécifique, voir Modes de passerelle.

Ou, pour permettre le routage direct via des interfaces hôte spécifiques, vers un réseau bridge spécifique, utilisez l'option suivante lors de la création du réseau :

  • com.docker.network.bridge.trusted_host_interfaces

Exemple

Créez un réseau où les ports publiés sur les adresses IP de conteneurs peuvent être accessibles directement depuis les interfaces vxlan.1 et eth3 :

$ docker network create --subnet 192.0.2.0/24 --ip-range 192.0.2.0/29 -o com.docker.network.bridge.trusted_host_interfaces="vxlan.1:eth3" mynet

Exécutez un conteneur dans ce réseau, publiant son port 80 au port 8080 sur l'interface de bouclage de l'hôte :

$ docker run -d --ip 192.0.2.100 -p 127.0.0.1:8080:80 nginx

Le serveur web s'exécutant sur le port 80 du conteneur peut maintenant être accessible depuis l'hôte Docker à http://127.0.0.1:8080, ou directement à http://192.0.2.100:80. Si les hôtes distants sur les réseaux connectés aux interfaces vxlan.1 et eth3 ont une route vers le réseau 192.0.2.0/24 à l'intérieur de l'hôte Docker, ils peuvent aussi accéder au serveur web via http://192.0.2.100:80.

Modes de passerelle

Le pilote de réseau bridge a les options suivantes :

  • com.docker.network.bridge.gateway_mode_ipv6
  • com.docker.network.bridge.gateway_mode_ipv4

Chacune de celles-ci peut être définie à l'un des modes de passerelle :

  • nat
  • nat-unprotected
  • routed
  • isolated

Le défaut est nat, les règles NAT et de masquerading sont configurées pour chaque port de conteneur publié. Les paquets quittant l'hôte utiliseront une adresse d'hôte.

Avec le mode routed, aucune règle NAT ou de masquerading n'est configurée, mais des iptables sont toujours configurées pour que seuls les ports de conteneurs publiés soient accessibles. Les paquets sortants du conteneur utiliseront l'adresse du conteneur, pas une adresse d'hôte.

En mode nat, quand un port est publié à une adresse d'hôte spécifique, ce port n'est accessible que via l'interface hôte avec cette adresse. Donc, par exemple, publier un port à une adresse sur l'interface de bouclage signifie que les hôtes distants ne peuvent pas y accéder.

Cependant, en utilisant le routage direct, les ports de conteneurs publiés sont toujours accessibles depuis les hôtes distants, sauf si le pare-feu de l'hôte Docker a des restrictions supplémentaires. Les hôtes sur le réseau de couche 2 local peuvent configurer le routage direct sans avoir besoin de configuration réseau supplémentaire. Les hôtes en dehors du réseau local ne peuvent utiliser le routage direct vers le conteneur que si les routeurs du réseau sont configurés pour l'activer.

En mode nat-unprotected, les ports de conteneurs non publiés sont aussi accessibles en utilisant le routage direct, aucune règle de filtrage de port n'est configurée. Ce mode est inclus pour la compatibilité avec le comportement par défaut hérité.

Le mode de passerelle affecte aussi la communication entre les conteneurs qui sont connectés à différents réseaux Docker sur le même hôte.

  • En modes nat et nat-unprotected, les conteneurs dans d'autres réseaux bridge ne peuvent accéder aux ports publiés que via les adresses d'hôte auxquelles ils sont publiés. Le routage direct depuis d'autres réseaux n'est pas autorisé.
  • En mode routed, les conteneurs dans d'autres réseaux peuvent utiliser le routage direct pour accéder aux ports, sans passer via une adresse d'hôte.

En mode routed, un port d'hôte dans un mappage de port -p ou --publish n'est pas utilisé, et l'adresse d'hôte n'est utilisée que pour décider s'il faut appliquer le mappage à IPv4 ou IPv6. Donc, quand un mappage ne s'applique qu'au mode routed, seules les adresses 0.0.0.0 ou :: devraient être utilisées, et un port d'hôte ne devrait pas être donné. Si une adresse ou un port spécifique est donné, il n'aura aucun effet sur le port publié et un message d'avertissement sera enregistré.

Le mode isolated ne peut être utilisé que quand le réseau est aussi créé avec le drapeau CLI --internal, ou équivalent. Une adresse est normalement assignée au dispositif bridge dans un réseau internal. Donc, les processus sur l'hôte docker peuvent accéder au réseau, et les conteneurs dans le réseau peuvent accéder aux services hôte écoutant sur cette adresse bridge (incluant les services écoutant sur "toute" adresse hôte, 0.0.0.0 ou ::). Aucune adresse n'est assignée au bridge quand le réseau est créé avec le mode de passerelle isolated.

Exemple

Créez un réseau adapté au routage direct pour IPv6, avec NAT activé pour IPv4 :

$ docker network create --ipv6 --subnet 2001:db8::/64 -o com.docker.network.bridge.gateway_mode_ipv6=routed mynet

Créez un conteneur avec un port publié :

$ docker run --network=mynet -p 8080:80 myimage

Alors :

  • Seul le port 80 du conteneur sera ouvert, pour IPv4 et IPv6.
  • Pour IPv6, en utilisant le mode routed, le port 80 sera ouvert sur l'adresse IP du conteneur. Le port 8080 ne sera pas ouvert sur les adresses IP de l'hôte, et les paquets sortants utiliseront l'adresse IP du conteneur.
  • Pour IPv4, en utilisant le mode nat par défaut, le port 80 du conteneur sera accessible via le port 8080 sur les adresses IP de l'hôte, ainsi que directement depuis l'hôte Docker. Mais, le port 80 du conteneur ne peut pas être accessible directement depuis l'extérieur de l'hôte. Les connexions provenant du conteneur masqueront, en utilisant l' adresse IP de l'hôte.

Dans docker inspect, ce mappage de port sera montré comme suit. Notez qu'il n'y a pas de HostPort pour IPv6, parce qu'il utilise le mode routed :

$ docker container inspect <id> --format "{{json .NetworkSettings.Ports}}"
{"80/tcp":[{"HostIp":"0.0.0.0","HostPort":"8080"},{"HostIp":"::","HostPort":""}]}

Alternativement, pour rendre le mappage IPv6 seulement, désactivant l'accès IPv4 au port 80 du conteneur, utilisez l'adresse IPv6 non spécifiée [::] et n'incluez pas un numéro de port d'hôte :

$ docker run --network mynet -p '[::]::80'

Définir l'adresse de liaison par défaut pour les conteneurs

Par défaut, quand les ports d'un conteneur sont mappés sans aucune adresse d'hôte spécifique, le démon Docker lie les ports de conteneurs publiés à toutes les adresses d'hôte (0.0.0.0 et [::]).

Par exemple, la commande suivante publie le port 8080 à toutes les interfaces réseau sur l'hôte, sur les adresses IPv4 et IPv6, les rendant potentiellement disponibles au monde extérieur.

docker run -p 8080:80 nginx

Vous pouvez changer l'adresse de liaison par défaut pour les ports de conteneurs publiés pour qu'ils ne soient accessibles qu'à l'hôte Docker par défaut. Pour ce faire, vous pouvez configurer le démon pour utiliser l'adresse de bouclage (127.0.0.1) à la place.

Warning

Dans les versions antérieures à 28.0.0, les hôtes du même segment L2 (par exemple, les hôtes connectés au même commutateur réseau) peuvent atteindre les ports publiés sur localhost. Pour plus d'informations, voir moby/moby#45610

Pour configurer ce paramètre pour les réseaux bridge définis par l'utilisateur, utilisez l'option de pilote com.docker.network.bridge.host_binding_ipv4 quand vous créez le réseau.

$ docker network create mybridge \
  -o "com.docker.network.bridge.host_binding_ipv4=127.0.0.1"
Note
  • Définir l'adresse de liaison par défaut à :: signifie que les liaisons de port sans adresse d'hôte spécifiée fonctionneront pour toute adresse IPv6 sur l'hôte. Mais, 0.0.0.0 signifie toute adresse IPv4 ou IPv6.
  • Changer l'adresse de liaison par défaut n'a aucun effet sur les services Swarm. Les services Swarm sont toujours exposés sur l'interface réseau 0.0.0.0.

Bridge par défaut

Pour définir la liaison par défaut pour le réseau bridge par défaut, configurez la clé "ip" dans le fichier de configuration daemon.json :

{
  "ip": "127.0.0.1"
}

Ceci change l'adresse de liaison par défaut à 127.0.0.1 pour les ports de conteneurs publiés sur le réseau bridge par défaut. Redémarrez le démon pour que ce changement prenne effet. Alternativement, vous pouvez utiliser le drapeau dockerd --ip lors du démarrage du démon.

Docker sur un routeur

Sur Linux, Docker a besoin que "IP Forwarding" soit activé sur l'hôte. Donc, il active les paramètres sysctl net.ipv4.ip_forward et net.ipv6.conf.all.forwarding s'ils ne sont pas déjà activés quand il démarre. Quand il fait cela, il définit aussi la politique de la chaîne iptables FORWARD à DROP.

Si Docker définit la politique pour la chaîne FORWARD à DROP. Ceci empêchera votre hôte Docker d'agir comme un routeur, c'est le paramètre recommandé quand IP Forwarding est activé.

Pour empêcher Docker de définir la politique de la chaîne FORWARD à DROP, incluez "ip-forward-no-drop": true dans /etc/docker/daemon.json, ou ajoutez l'option --ip-forward-no-drop à la ligne de commande dockerd.

Alternativement, vous pouvez ajouter des règles ACCEPT à la chaîne DOCKER-USER pour les paquets que vous voulez transférer. Par exemple :

$ iptables -I DOCKER-USER -i src_if -o dst_if -j ACCEPT
Warning

Dans les versions antérieures à 28.0.0, Docker définissait toujours la politique par défaut de la chaîne IPv6 FORWARD à DROP. Dans la version 28.0.0 et plus récentes, il ne définira cette politique que s'il active lui-même le transfert IPv6. Ceci a toujours été le comportement pour le transfert IPv4.

Si le transfert IPv6 est activé sur votre hôte avant que Docker démarre, vérifiez la configuration de votre hôte pour vous assurer qu'elle est toujours sécurisée.

Empêcher Docker de manipuler iptables

Il est possible de définir les clés iptables ou ip6tables à false dans la configuration du démon, mais cette option n'est pas appropriée pour la plupart des utilisateurs. Elle est susceptible de casser le réseau de conteneurs pour le moteur Docker.

Tous les ports de tous les conteneurs seront accessibles depuis le réseau, et aucun ne sera mappé depuis les adresses IP de l'hôte Docker.

Il n'est pas possible d'empêcher complètement Docker de créer des règles iptables, et créer des règles après coup est extrêmement complexe et au-delà de la portée de ces instructions.

Intégration avec firewalld

Si vous exécutez Docker avec l'option iptables définie à true, et firewalld est activé sur votre système, Docker crée automatiquement une zone firewalld appelée docker, avec la cible ACCEPT.

Toutes les interfaces réseau créées par Docker (par exemple, docker0) sont insérées dans la zone docker.

Docker crée aussi une politique de transfert appelée docker-forwarding qui permet le transfert depuis la zone ANY vers la zone docker.

Docker et ufw

Uncomplicated Firewall (ufw) est un frontend qui est livré avec Debian et Ubuntu, et il vous permet de gérer les règles de pare-feu. Docker et ufw utilisent iptables de manières qui les rendent incompatibles l'un avec l'autre.

Quand vous publiez les ports d'un conteneur en utilisant Docker, le trafic vers et depuis ce conteneur est dévié avant qu'il passe par les paramètres de pare-feu ufw. Docker route le trafic de conteneurs dans la table nat, ce qui signifie que les paquets sont déviés avant qu'ils atteignent les chaînes INPUT et OUTPUT qu'ufw utilise. Les paquets sont routés avant que les règles de pare-feu puissent être appliquées, ignorant effectivement votre configuration de pare-feu.