Très complexe dans les premières années de la solution, l'utilisation d'un firewall avec Docker est possible depuis maintenant plus d'un an. Mais la solution technique reste encore méconnue.
Docker interagit avec iptables afin d'effectuer le mappage des ports que vous allez pouvoir déclarer dans vos configurations ou même dans vos commandes de "run". Cette gestion fine du réseau est un des grands atouts de Docker ( isolation des conteneurs ... ) et en même temps une problématique technique. Surtout si vous souhaitez protéger vos serveurs avec IPTABLES ou encore si vous avez l'habitude d'utiliser des outils qui vont manipuler ces tables ( shorewall,ufw, etc ).
Contexte
En réalité la gestion d'IPTABLES avec Docker est devenue très simple depuis la version 17.06, mais cette modification est passée plutôt inaperçue :
In Docker 17.06 and higher, you can add rules to a new table called DOCKER-USER, and these rules will be loaded before any rules Docker creates automatically. This can be useful if you need to pre-populate iptables rules that need to be in place before Docker runs.
En résumé, nous pouvons maintenant utiliser une chaîne DOCKER-USER qui est chargée avant toutes règles DOCKER.
Le redémarrage du daemon Docker n'entrainera donc aucun conflit avec ces règles. Et oui, fini les IPTABLES qui disparaissent au redémarrage du serveur.
Un exemple de configuration vaut mieux que bien des explications théoriques !
Installer mon firewall
Pour mettre en place un firewall simpliste, nous allons créer un fichier de configuration iptables :
*filter
:INPUT ACCEPT [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
:FILTERS - [0:0]
:DOCKER-USER - [0:0]
-F INPUT
-F DOCKER-USER
-F FILTERS
-A INPUT -i lo -j ACCEPT
-A INPUT -j FILTERS
-A DOCKER-USER -i eno1 -j FILTERS
-A FILTERS -m state --state ESTABLISHED,RELATED -j ACCEPT
#allow 80 and 443
-A FILTERS -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
-A FILTERS -m state --state NEW -m tcp -p tcp --dport 443 -j ACCEPT
#rules home
-A FILTERS -m state --state NEW -s X.X.X.X/32 -j ACCEPT
-A FILTERS -j REJECT --reject-with icmp-host-prohibited
COMMIT
Ok mais il fait quoi ce fichier ?
Ce fichier n'ajoute des règles qu'aux chaînes INPUT
, DOCKER-USER
ou FILTERS
. Ces chaînes ne sont pas modifiées par Docker, vous pourrez donc redémarrer autant que vous le souhaitez Docker, il n'interférera pas avec ces règles.
Dans un premier temps, on flush les trois chaînes que nous allons utiliser :
-F INPUT
-F DOCKER-USER
-F FILTERS
La table INPUT
étant la première en entrée, nous allons créer quelques règles pour celle-ci :
-A INPUT -i lo -j ACCEPT
-A INPUT -j FILTERS
On accepte tout le trafic sur l'interface de loopback et je transfère le reste à ma chaîne FILTERS
et je fais la même chose avec la chaîne DOCKER-USER
:
-A DOCKER-USER -i eno1 -j FILTERS
On autorise les connexions établies :
-A FILTERS -m state --state ESTABLISHED,RELATED -j ACCEPT
Enfin nous allons pouvoir mettre en place nos règles. Je rappelle que la table FILTERS
est là pour ça ! Il serait donc dommage de ne pas le faire dans cette table :
#allow 80 and 443
-A FILTERS -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
-A FILTERS -m state --state NEW -m tcp -p tcp --dport 443 -j ACCEPT
#rules home
-A FILTERS -m state --state NEW -s X.X.X.X/32 -j ACCEPT
2 exemples de règles : l'autorisation des ports 80 et 443 mais également l'autorisation d'une IP publique sans filtrage.
Enfin on termine par bloquer le reste du trafic :
-A FILTERS -j REJECT --reject-with icmp-host-prohibited
Charger le firewall
On peut alors le charger :
iptables-restore -n /etc/iptables.conf
L'argument -n
est essentiel afin de ne pas flush l'intégralité des tables ( et donc celle de DOCKER ... ). Et vous pouvez maintenant comprendre pourquoi il est nécessaire de flush "manuellement" nos chaînes au début de notre fichier de configuration.
Démarrer le pare-feu au démarrage
On va pouvoir utiliser systemd afin de démarrer notre firewall au démarrage de la machine.
Pour cela nous allons tout simplement créer un service à démarrer avec notre machine - /etc/systemd/system/iptables.service
:
[Unit]
Description=Restore iptables firewall rules
Before=network-pre.target
[Service]
Type=oneshot
ExecStart=/sbin/iptables-restore -n /etc/iptables.conf
[Install]
WantedBy=multi-user.target
Et activer le service :
sudo systemctl enable --now iptables
Votre firewall est maintenant fonctionnel, et laisse les chaines liées à Docker intactes. Vous pouvez redémarrer sans soucis votre machine et vérifiez le bon fonctionnement de vos conteneurs.
Mise à jour du firewall
Éditez votre fichier avec votre éditeur favori et ajoutez ou supprimez votre règle dans la section des chaines FILTERS
, ensuite redémarrer le firewall avec la commande suivante :
$ sudo systemctl restart iptables