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.
NoteDocker crée des règles
iptables
pour les réseaux bridge.Aucune règle
iptables
n'est créée pour le réseauipvlan
,macvlan
ouhost
.
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
etDOCKER
.
- 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
- 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
etDOCKER-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
ImportantL'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
etnat-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.
WarningDans 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
WarningDans 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.