Je viens d'essayer Docker. C'est génial mais semble ne pas bien fonctionner avec ufw. Par défaut, docker manipulera un peu les iptables. Le résultat n'est pas un bug mais pas ce à quoi je m'attendais. Pour plus de détails, vous pouvez lire Les dangers d'UFW + Docker
Mon but est de mettre en place un système comme
Host (running ufw) -> docker container 1 - nginx (as a reverse proxy)
-> docker container 2 - node web 1
-> docker container 3 - node web 2
-> .......
Je veux gérer le trafic entrant (par exemple, restreindre l'accès) via ufw, donc je ne veux pas que docker touche mes iptables. Voici mon test
Environnement:
--iptables=false
a été ajouté au démon Docker.Première tentative
docker run --name ghost -v /home/xxxx/ghost_content:/var/lib/ghost -d ghost
docker run --name nginx -p 80:80 -v /home/xxxx/nginx_site_enable:/etc/nginx/conf.d:ro --link ghost:ghost -d nginx
Pas de chance. La première commande est correcte mais la deuxième commande génère une erreur.
Error response from daemon: Cannot start container
Deuxième tentative
Ensuite, j'ai trouvé ceci: impossible de lier les conteneurs avec --iptables = false # 12701
Après avoir exécuté la commande suivante, tout semble aller pour le mieux.
Sudo iptables -N DOCKER
Cependant, j'ai remarqué que je ne pouvais pas établir de connexion sortante à l'intérieur de conteneurs. Par exemple:
xxxxg@ubuntu:~$ Sudo docker exec -t -i nginx /bin/bash
root@b0d33f22d3f4:/# ping 74.125.21.147
PING 74.125.21.147 (74.125.21.147): 56 data bytes
^C--- 74.125.21.147 ping statistics ---
35 packets transmitted, 0 packets received, 100% packet loss
root@b0d33f22d3f4:/#
Si je supprime --iptables=false
du démon Docker, la connexion Internet des conteneurs sera redevenue normale, mais l'ufw ne fonctionnera pas "correctement" (bien ... d'après ma définition).
Alors, quelle est la meilleure pratique de docker + ufw? Quelqu'un peut-il fournir de l'aide?
Merci.
Bart.
J'ai eu un tel problème il y a des mois et récemment décidé de décrire le problème avec la solution sur mon blog. Voici le raccourci.
Utiliser --iptables=false
ne vous aidera pas beaucoup avec le cas que vous avez décrit. Ce n'est tout simplement pas suffisant ici. Par défaut, aucun de vos conteneurs ne peut établir de connexion sortante.
Vous omettez un petit pas sur votre chemin pour avoir des conteneurs derrière UFW ici. Vous pouvez utiliser --iptables=false
ou créer un fichier /etc/docker/daemon.json
avec un contenu comme suit
{
"iptables": false
}
le résultat sera le même, mais cette dernière option nécessite de redémarrer tout le service Docker avec service docker restart
ou même de procéder à un redémarrage si docker avait la possibilité d'ajouter des règles iptables avant de désactiver cette fonction.
Une fois cela fait, il suffit de faire deux autres choses:
$ sed -i -e 's/DEFAULT_FORWARD_POLICY="DROP"/DEFAULT_FORWARD_POLICY="ACCEPT"/g' /etc/default/ufw
$ ufw reload
vous devez donc configurer la politique de transfert par défaut dans UFW pour accepter et utiliser:
$ iptables -t nat -A POSTROUTING ! -o docker0 -s 172.17.0.0/16 -j MASQUERADE
De cette manière, vous désactivez le comportement désordonné de Docker dans vos règles iptables et en même temps, Docker reçoit le routage nécessaire pour que les conteneurs puissent effectuer les connexions sortantes sans problème. Les règles UFW seront toujours restreintes à partir de maintenant.
J'espère que cela résoudra le problème pour vous et tous ceux qui arriveront ici à la recherche d'une réponse.
J'ai décrit le problème et sa solution de manière plus complète à l'adresse https://www.mkubaczyk.com/2017/09/05/force-docker-not-bypass-ufw-rules-ubuntu-16-04/
Ce problème existe depuis longtemps.
Désactiver iptables dans Docker prendra d’autres problèmes.
Si vous avez modifié votre serveur en fonction de la solution actuelle trouvée sur Internet, annulez d'abord ces modifications, notamment:
--iptables=false
, y compris le fichier de configuration /etc/docker/daemon.json
.DROP
au lieu de ACCEPT
./etc/ufw/after.rules
.Cette solution doit modifier un seul fichier de configuration UFW. Toutes les configurations et options de Docker restent les valeurs par défaut. N’a pas besoin de désactiver la fonction docker iptables.
Modifiez le fichier de configuration UFW /etc/ufw/after.rules
et ajoutez les règles suivantes à la fin du fichier:
# BEGIN UFW AND DOCKER
*filter
:ufw-user-forward - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16
-A DOCKER-USER -j ufw-user-forward
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12
-A DOCKER-USER -j RETURN
COMMIT
# END UFW AND DOCKER
Utilisation de la commande Sudo systemctl restart ufw
pour redémarrer UFW après la modification du fichier. À présent, le réseau public ne peut accéder aux ports de menu fixe publiés, le conteneur et le réseau privé peuvent se rendre visite régulièrement, et les conteneurs peuvent également accéder au réseau externe de l'intérieur.
Si vous souhaitez autoriser les réseaux publics à accéder aux services fournis par le conteneur Docker, par exemple, le port de service d'un conteneur est 80
. Exécutez la commande suivante pour autoriser les réseaux publics à accéder à ce service:
ufw route allow proto tcp from any to any port 80
Cette commande permet au réseau public d'accéder à tous les ports publiés dont le port de conteneur est 80.
Remarque: Si nous publions un port à l'aide de l'option -p 8080:80
, nous devrions utiliser le port conteneur 80
, et non le port hôte 8080
.
S'il existe plusieurs conteneurs avec un port de service de 80, nous souhaitons uniquement que le réseau externe accède à un conteneur particulier. Par exemple, si l'adresse privée du conteneur est 172.17.0.2, utilisez la commande suivante:
ufw route allow proto tcp from any to 172.17.0.2 port 80
Si le protocole de service réseau est UDP, par exemple un service DNS, vous pouvez utiliser la commande suivante pour autoriser le réseau externe à accéder à tous les services DNS publiés:
ufw route allow proto udp from any to any port 53
De même, s'il ne s'agit que d'un conteneur spécifique, tel que l'adresse IP 172.17.0.2:
ufw route allow proto udp from any to 172.17.0.2 port 53
Les règles suivantes permettent aux réseaux privés de se rendre visite. En règle générale, les réseaux privés sont plus fiables que les réseaux publics.
-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16
Les règles suivantes permettent à UFW de déterminer si les réseaux publics sont autorisés à visiter les services fournis par le conteneur Docker. Ainsi, nous pouvons gérer toutes les règles de pare-feu au même endroit.
-A DOCKER-USER -j ufw-user-forward
Les règles suivantes bloquent les demandes de connexion lancées par tous les réseaux publics, mais permettent aux réseaux internes d'accéder aux réseaux externes. Pour le protocole TCP, cela empêche d'établir activement une connexion TCP à partir de réseaux publics. Pour le protocole UDP, tous les accès aux ports inférieurs à 32767 sont bloqués. Pourquoi est ce port? Le protocole UDP étant sans état, il n'est pas possible de bloquer le signal d'établissement de liaison qui initie la demande de connexion comme le fait TCP. Pour GNU/Linux, nous pouvons trouver la plage de ports locaux dans le fichier /proc/sys/net/ipv4/ip_local_port_range
. La plage par défaut est 32768 60999
. Lors de l'accès à un service de protocole UDP à partir d'un conteneur en cours d'exécution, le port local est sélectionné de manière aléatoire dans la plage de ports et le serveur renvoie les données sur ce port aléatoire. Par conséquent, nous pouvons supposer que le port d'écoute du protocole UDP dans tous les conteneurs est inférieur à 32768. C'est la raison pour laquelle nous ne voulons pas que les réseaux publics accèdent aux ports UDP à moins de 32768.
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12
-A DOCKER-USER -j RETURN
https://github.com/chaifeng/ufw-docker
Sudo wget -O /usr/local/bin/ufw-docker https://github.com/chaifeng/ufw-docker/raw/master/ufw-docker
chmod +x /usr/local/bin/ufw-docker
ufw-docker help
ufw-docker install
ufw-docker status
ufw-docker allow webapp
ufw-docker allow webapp 80
ufw-docker allow webapp 53/udp
ufw-docker list webapp
ufw-docker delete allow webapp 80/tcp
ufw-docker delete allow webapp
ufw-user-forward
, pas ufw-user-input
ufw-user-input
Pro:
Facile à utiliser et à comprendre, supporte les anciennes versions d'Ubuntu.
Par exemple, pour autoriser le public à visiter un port publié dont le port de conteneur est 8080
, utilisez la commande suivante:
ufw allow 8080
Con:
Cela expose non seulement les ports des conteneurs, mais également les ports de l'hôte.
Par exemple, si un service est en cours d'exécution sur l'hôte et que le port est 8080
. La commande ufw allow 8080
permet au réseau public de visiter le service et tous les ports publiés dont le port du conteneur est 8080
. Mais nous souhaitons simplement exposer le service en cours d'exécution sur l'hôte ou uniquement le service s'exécutant à l'intérieur de conteneurs, pas les deux.
Pour éviter ce problème, nous devrons peut-être utiliser une commande semblable à celle-ci pour tous les conteneurs:
ufw allow proto tcp from any to 172.16.0.3 port 8080
ufw-user-forward
Pro:
Impossible d'exposer des services s'exécutant sur des hôtes et des conteneurs en même temps par la même commande.
Par exemple, si nous voulons publier le port 8080
des conteneurs, utilisez la commande suivante:
ufw route allow 8080
Le réseau public peut accéder à tous les ports publiés dont les ports de conteneur sont 8080
.
Mais le port 8080
de l'hôte n'est toujours pas accessible par le réseau public. Si nous voulons le faire, exécutez la commande suivante pour autoriser le public à accéder séparément au port sur l'hôte:
ufw allow 8080
Con:
Ne supporte pas les anciennes versions d'Ubuntu, et la commande est un peu plus compliquée. Mais vous pouvez utiliser mon script https://github.com/chaifeng/ufw-docker .
Si nous utilisons une version plus ancienne d’Ubuntu, nous pouvons utiliser la chaîne ufw-user-input
. Mais veillez à ne pas exposer des services qui ne devraient pas être exposés.
Si nous utilisons une version plus récente d'Ubuntu qui est la sous-commande support ufw route
, nous ferions mieux d'utiliser la chaîne ufw-user-forward
et d'utiliser la commande ufw route
pour gérer les règles de pare-feu des conteneurs.
Le script ufw-docker prend maintenant en charge Docker Swarm. S'il vous plaît voir le dernier code pour plus, https://github.com/chaifeng/ufw-docker
Nous ne pouvons utiliser ce script que sur les nœuds de gestionnaire pour gérer les règles de pare-feu en mode Swarm.
after.rules
sur tous les nœuds, y compris les gestionnaires et les travailleursEn cours d'exécution en mode Docker Swarm, ce script ajoute un service global ufw-docker-agent
. L'image chaifeng/ufw-docker-agent est également construite automatiquement à partir de ce projet.
Pour ce qui en vaut la peine, voici un addendum à la réponse de @ mkubaczyk pour le cas où plusieurs réseaux de passerelles sont impliqués dans l’ensemble de la configuration. Ceux-ci peuvent être fournis par des projets Docker-Compose et voici comment générer les règles appropriées, étant donné que ces projets sont contrôlés par systemd
.
/etc/systemd/system/[email protected]
[Unit]
Description=Docker-Compose project: %I
After=docker.service
BindsTo=docker.service
AssertPathIsDirectory=/<projects_path>/%I
AssertFileNotEmpty=/<projects_path>/%I/docker-compose.yml
[Service]
Type=simple
Restart=always
WorkingDirectory=/<projects_path>/%I
ExecStartPre=/usr/bin/docker-compose up --no-start --remove-orphans
ExecStartPre=+/usr/local/bin/update-iptables-for-docker-bridges
ExecStart=/usr/bin/docker-compose up
ExecStop=/usr/bin/docker-compose stop --timeout 30
TimeoutStopSec=30
User=<…>
StandardOutput=null
[Install]
WantedBy=multi-user.target
/usr/local/bin/update-iptables-for-docker-bridges
#!/bin/sh
for network in $(docker network ls --filter 'driver=bridge' --quiet); do
iface=$(docker network inspect --format '{{index .Options "com.docker.network.bridge.name"}}' ${network})
[ -z $iface ] && iface="br-${network}"
subnet=$(docker network inspect --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}' ${network})
rule="! --out-interface ${iface} --source ${subnet} --jump MASQUERADE"
iptables --table nat --check POSTROUTING ${rule} || iptables --table nat --append POSTROUTING ${rule}
done
De toute évidence, cela ne va pas si bien.
Il convient également de noter que l'ensemble du concept de base dissimulera la source de toute connexion pour les applications exécutées dans un conteneur.
Vous ne savez pas trop ce que vous demandez, mais d'après ce que je peux comprendre, vous voudriez avoir un meilleur contrôle sur les personnes pouvant accéder à vos applications s'exécutant dans Docker? J'ai répondu à une question similaire ici pour contrôler le trafic via un proxy frontal plutôt qu'avec des tables IP Bloquer l'accès externe aux conteneurs de menu fixe
J'espère que cela t'aides
Dylan
Avec l'approche ci-dessus, vous pouvez alors utiliser UFW pour n'autoriser que les connexions entrantes sur le port 80 (c'est-à-dire le proxy). Cela permet de minimiser l'exposition des ports avec le bonus supplémentaire que vous pouvez contrôler le trafic via une configuration de proxy et DNS