Exposer le socket Docker via TLS
Préambule
Pour certaines opérations, il peut être utile de pouvoir lancer des commandes Docker à distance. Par exemple, les graphes des services sont construits de manière centralisée en récupérant des informations sur les conteneurs qui tournent, à distance. Une chaîne d’intégration aurait besoin de lancer des commandes à distance.
Pour ce faire, l’idée est d’exposer le socket Docker (qui permet d’effectuer toutes les opérations possibles avec Docker) à l’extérieur.
Attention:
Exposer le socket Docker à l’extérieur revient à donner des accès root
à quiconque pourra discuter avec lui. Il est donc essentiel d’utiliser TLS pour le sécuriser proprement.
Pour pouvoir communiquer avec le socket Docker depuis l’extérieur, les éléments suivants sont nécessaires :
- Le certificat d’une autorité de certification
- Un certificat client
- Une clé privée client permettant de signer les messages
Note:
La clé privée est l’équivalent d’une clé privée SSH, qui est plus sûre qu’un mot de passe. Le certificat client est une sorte de carte d’identité du client, et ne peut être émis que par l’instance de Docker accessible à l’extérieur.
Pour ce faire, il faut créer une autorité de certification (CA) côté serveur et générer l’ensemble de ces éléments.
Important:
On crée ces éléments dans /DATA/docker/remote
, ils appartiendront à root
avec le groupe docker
, et seront en u=rw,g=r
.
Note:
On a fixé arbitrairement la durée de vie des certificats à un an. Il conviendra donc de les renouveler et de les mettre à jour sur les serveurs et les clients concernés tous les ans.
Lien:
Un tutoriel complet est disponible dans la documentation officielle, nous en reprenons les éléments principaux.
Une bonne pratique est de ne pas ré-utiliser les certificats et clés clientes pour des clients multiples : générez autant de certificats que de clients.
Créer une clé privée pour la CA
Attention:
Cette opération n’est qu’à faire une fois, pas à chaque renouvellement !
C’est la clé la plus importante, puisqu’elle permet de créer les certificats. Si elle est compromise, n’importe qui pourrait créer un certificat client illégitime. C’est pourquoi on la protegera avec un mot de passe.
On génère la clé :
- snippet.bash
openssl genrsa -aes256 -out ca-key.pem 4096
Générer et stocker la passphrase dans Vaultwarden, avec la nomenclature Tech/Keys/<serveur>/ca-key.pem
.
Créer ou renouveller la CA
On crée la CA à partir de la clé privée existante, ca-key.pem
, à l’aide de la commande suivante :
- snippet.bash
$ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
Les informations à remplir sont :
Country Name
:FR
State or Province Name
:Picardie
Locality Name
:Compiegne
Organization Name
:Picasoft
Organization Unit Name
:Picasoft
Common Name
:<serveur>.picasoft.net
e.g.pica01-test.picasoft.net
Email Address
:picasoft@assos.utc.fr
Note:
L’autorité de certification ainsi obtenue permettra de créer des certificats (serveur ou client). Elle n’est jamais utilisée pour chiffrer les communications.
Création du certificat serveur
Ce certificat est spécifié lors du lancement du démon Docker, et a plusieurs rôles :
- Spécifier les hôtes concernés par l’autorité de certification. Ainsi, si ce certificat serveur est utilisé sur un autre hôte, les connexions seront refusées.
- La clé privée qui lui est associée permet de chiffrer les communications.
On commence par créer une nouvelle clé privée pour le certificat serveur :
- snippet.bash
$ openssl genrsa -out server-key.pem 4096
On crée une demande de signature du certificat. Attention, on remplace bien le CN
par le champ Common Name
de la CA, c’est-à-dire l’URL du serveur.
- snippet.bash
$ openssl req -subj "/CN=<serveur>.picasoft.net" -sha256 -new -key server-key.pem -out server.csr
Notez que le fichier extfile.cnf
contient déjà toutes les informations nécessaires pour autoriser l’accès au socket Docker. S’il n’existe pas, créez le avec les informations suivantes :
extendedKeyUsage = serverAuth subjectAltName = DNS:<serveur>.picasoft.net,IP:<IP du serveur>,IP:127.0.0.1
Ainsi, ce certificat pourra être utilisé pour valider les authentifications côté serveur. On génère le certificat signé par la CA elle-même.
- snippet.bash
openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf
On fait du nettoyage :
- snippet.bash
$ rm server.csr
Renouvellement des certificats client
On commence par effacer puis créer à nouveau les dossiers des clients (e.g graphbot
, ci
) :
Pour chaque client, on crée une nouvelle clé privée:
- snippet.bash
$ cd <client> $ openssl genrsa -out <client>-key.pem 4096
On crée ensuite la demande de signature de certificat :
- snippet.bash
$ openssl req -subj '/CN=<client>' -new -key <client>-key.pem -out <client>.csr
Le fichier extfile-client.cnf
contient déjà les informations nécessaires pour autoriser l’accès au socket Docker. S’il n’existe pas, créez le avec les informations suivantes :
extendedKeyUsage = clientAuth
Ainsi, le client sera autorisé à s’authentifier. On crée ensuite le certificat client signé.
- snippet.bash
$ openssl x509 -req -days 365 -sha256 -in <client>.csr -CA ../ca.pem -CAkey ../ca-key.pem -CAcreateserial -out <client>-cert.pem -extfile ../extfile-client.cnf
On fait du nettoyage :
- snippet.bash
$ rm <client>.csr
Renouvelez l’opération pour l’ensemble des clients.
Configurer le démon Docker
Vérifier que le démon Docker est bien configuré pour démarrer avec TLS et utiliser le certificat généré.
Relancer le démon Docker
Avant toute chose, il est indispensable de donner des permissions propres à tout ce qui a été généré :
- snippet.bash
cd /DATA/docker/remote sudo chown -R root:docker . sudo chmod -R u=rwX,g=rwX .
Il faut relancer le démon Docker avec les nouveaux certificats.
- snippet.bash
sudo systemctl restart docker systemctl status docker
Utilisation des certificats clients
Depuis le serveur concerné, récupérez les fichiers suivants :
ca.pem
: le certificat de la CA elle-même.<client>/<client>-cert.pem
: le certificat du client, qui doit avoir été signé par la CA.<client>/<client>-key.pem
: la clé privée du client pour chiffrer les communications.
Pour faciliter et sécuriser la tâche, on peut mettre tout ça dans un tar puis le chiffrer avec un mot de passe temporaire.
- snippet.bash
tar -cvf certs.tar ca.pem <client> gpg -c certs.tar rm certs.tar
Depuis notre PC, on récupère le fichier chiffré, puis on l’envoie vers le bon client:
- snippet.bash
rsync <user>@<serveur>.picasoft.net:/DATA/docker/remote/certs.tar.gpg /tmp rsync /tmp/certs.tar.gpg <user>@<client>.picasoft.net:/tmp
On se connecte au client et on déchiffre et extrait le fichier:
- snippet.bash
cd /tmp gpg certs.tar.gpg tar -xvf certs.tar
On peut vérifier que tout est ok avec la commande suivante:
- snippet.bash
docker --tlsverify --tlscacert=/tmp/ca.pem --tlscert=/tmp/client/<client>-cert.pem --tlskey=/tmp/client/<client>-key.pem -H=<serveur>.picasoft.net:2376 version
Si tout se passe bien, vous obtenez la version de Docker. Vérifiez qu’il n’y a aucune erreur.
Note:
Il est nécessaire de mettre à jour les fichiers des services qui l’utilisent : on se référera à leur documentation.