web-dev-qa-db-fra.com

Quelle est la meilleure pratique de docker + ufw sous Ubuntu

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:

  • une Ubuntu 14.04 nouvellement installée (noyau: 3.13.0-53)
  • Docker 1.6.2 
  • le transfert ufw est activé. ( Activer le transfert UFW )
  • --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.

21
Yi-Chien Chang

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/

12
mkubaczyk

Problème

Ce problème existe depuis longtemps.

Désactiver iptables dans Docker prendra d’autres problèmes.

Annuler les changements en premier

Si vous avez modifié votre serveur en fonction de la solution actuelle trouvée sur Internet, annulez d'abord ces modifications, notamment:

  • Activer la fonctionnalité iptables de Docker. Supprimez toutes les modifications telles que --iptables=false, y compris le fichier de configuration /etc/docker/daemon.json.
  • La règle FORWARD par défaut d'UFW rétablit la valeur par défaut DROP au lieu de ACCEPT.
  • Supprimez les règles relatives au réseau Docker dans le fichier de configuration UFW /etc/ufw/after.rules.
  • Si vous avez modifié les fichiers de configuration de Docker, redémarrez Docker en premier. Nous modifierons la configuration UFW ultérieurement et nous pourrons la redémarrer ensuite.

Résoudre les problèmes UFW et Docker

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

Comment ça marche?

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

Plus

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

Usage

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

Mise à jour: 2018-09-10

La raison de choisir ufw-user-forward, pas ufw-user-input

en utilisant 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

en utilisant 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 .

Conclusion

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.


Mise à jour: 6 octobre 2018

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.

  • Modification de tous les fichiers after.rules sur tous les nœuds, y compris les gestionnaires et les travailleurs
  • Déploiement de ce script sur les nœuds de gestionnaire

En 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.

26
Feng

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.

0
funky-future

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

Modifier

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 

0
Dylan Scott