Table des matières

Docker: activer IPv6 sur un réseau interne docker

Sur l'un de mes hosts tourne docker, et pour accéder aux applications j'utilise un reverse proxy (NPM), dans sa version jc21.

Je m'assure, dans les paramètres du NPM que l'application reçoive bien l'adresse IP du client (et pas celle du proxy); je valide l'IP du client (au niveau de l'application) notamment pour limiter l'accès depuis certains pays au niveau de l'application (même si j'aurais pu le faire au niveau du NPM, ça sera pour un futur article).

Si le passage de l'IPv4 du client vers l'application ne pose aucun problème avec les bon paramètres, l'IPv6 ne fonctionne pas bien, je capte l'IP du gateway à la place de l'IP du client, mais pourquoi…

La faute à Docker… ou plutôt au NAT IPv6 → application.

un petit schéma

articles:npm-architecture.png

dans ce schémas, j'ai un seul réseau défini mybridged_net, ce réseau est souvent créer avec une commande simple, sans précisions:

frater@host:~$ sudo docker network create --driver bridge mybridged_net

Docker attribuera automatiquement le premier subnet 172.x.0.0/16 libre (en général 172.18.0.0/16 et avec le gateway 172.18.0.1)

articles:npm-architecture-complexe.png

mais on peut imaginer un design plus complexe, avec 3 réseaux “définis”; public_net, frontend_net et backend_net:

frater@host:~$ sudo docker network create --driver bridge public_net
frater@host:~$ sudo docker network create --driver bridge frontend_net
frater@host:~$ sudo docker network create --driver host backend_net

Ces commandes créeront 3 réseaux avec probablement des ip : 172.18.0.0/16, 172.19.0.0/16 et 172.20.0.0/16 et le routage sera natif entre les différents réseaux… en IPv4, par défaut Docker utilisera le NAT pour convertir les packets IPv6 en IPv4.

Un peu de théorie

Docker et NAT

Docker utilise un système de réseau virtuel pour isoler les conteneurs, ce qui permet à chaque conteneur d'avoir une adresse IP unique. Il existe quelques drivers (bridge, ipvlan, macvlan, overlay, null) mais soyons réaliste, 99.99% des gens vont utiliser un réseau bridge ou host (dans certains cas précis).

Du coup un réseau bridge va réaliser du NATing (Network Address Translation) entre les différents interfaces (physiques ou virtuels) pour communiquer en dehors du réseau.

NAT ????

Le NAT est utilisé par Docker pour permettre aux conteneurs de communiquer sur Internet.

Le NAT est utilisé si le client est en IPv6 et que le conteneur n'est pas “publié” sur l'interface internet, il sera probablement uniquement en IPv4 (les reseaux interne de Docker n'ont pas d'adresse IPv6 activée par défaut), Docker utilise le NAT pour convertir de l'IPv6 vers l'IPv4 (utiliser dans les conteneurs non publiés).

Comme je l'ai dit, nous allons principalement (et quasi uniquement utiliser) un réseau de type bridge (oui, il est fortement conseillé de créer son propre réseau).

Lors de son installation, Docker crée un réseau de pont bridge, qui connecte les conteneurs entre eux et avec l'interface réseau principale de l'hôte.

Les conteneurs qui sont sur le même réseau peuvent communiquer entre eux directement via leur adresse IP locale (dans le sous-réseau bridge ou host), dans la mesure du possible, Docker routera les paquets entre les réseaux privés, sans NAT sauf si il y a changement de type d'adressage (par exemple IPv4 ↔ IPv6).

Docker applique une table NAT pour chaque paquet sortant du réseau bridge vers l'internet ou pour un changement de type d'adressage, Docker utilise le NAT pour modifier la source de l'adresse IP au niveau IP (source) pour qu'elle corresponde à l'adresse IP de l'interface réseau de l'hôte (eth0 ou ens33, par exemple).

Le destinataire verra donc que les paquets proviennent de cette adresse IP plutôt que de celle du conteneur, et ce même dans le cas d'un échange conteneur à conteneur si l'on change de type d'adressage.

Lorsqu'un conteneur doit envoyer un paquet vers Internet, le une table NAT est utilisée pour modifier la source de l'adresse IP du paquet. Le paquet est ensuite envoyé via l'interface réseau principale de l'hôte.

Les réponses arrivent à l'hôte via son interface réseau et sont ensuite redirigées vers le conteneur approprié grâce au NAT inversé (Reverse NAT).

La solution

le conteneur cible (en IPv4 “voit” l'IP client du NAT (en général le gateway) quand il y a changement de réseau ou de type d'adressage, cela peut poser problème.

Pour résoudre ce problème il faut que les réseaux privés de Docker soient capable de router, sans NAT autant l'IPv4 que l'IPv6; hors IPv6 reste difficile à comprendre et à implémenter.

Après de longue recherche sur le net, j'ai finalement trouver une solution:

frater@docker network create --driver bridge server_ipv4v6 --subnet=172.21.0.0/16 --gateway=172.21.0.1 --ipv6 --subnet=fc00:db8:0021::/112 --gateway=fc00:db8:0021::1

Cette commande va créer un réseau, avec comme IP 172.21.0.0/16 - GW 172.21.0.1 et comme IPv6 fc00:db8:0021::/112 et comme gateway fc00:db8:0021::1, il est facile avec cette IPv6 'locale' de créer de multiple sous réseaux en changeant le 0021 en autre chose…