Certificat Self-Signed et Registry Docker

Photo by Aleksandar Pasaric from Pexels

TL;DR: Cet article présente quelques commandes très utiles qui s'appuient sur OpenSSL pour créer une autorité de certification, créer un certificat et l'utiliser avec une registry docker privée.

Vous pouvez trouver de nombreuses autorités de certifications gratuites, les plus populaires étant Let's Encrypt et CACert. Si pour quelque raison que ce soit, vous préférez utiliser votre propre autorité de certification, une CA, vous utiliserez certainement OpenSSL qui est une jolie boite à outils pour ce type d'opérations. Et pour en savoir plus sur OpenSSL, reportez-vous à sa documentation.

Une CA ou autorité de certification peut être extrêmement utile pour vos infrastructures cloud native. Par exemple, vous pourrez l'utiliser avec Vault pour partager des secrets. Vous pouvez l'utiliser dans le cadre d'une infrastructure à clés publiques pour authentifier des clients et des serveurs via mTLS. Vous pourrez également l'utiliser pour chiffrer vos communications réseau en HTTP ou en gRPC. Dans les sections ci-dessous, vous verrez comment créer une CA et comment l'utiliser pour créer un certificat et l'utiliser sur une registry docker privée.

Note 1: L'exemple qui suit s'appuie sur une distribution Enterprise Linux ou Fedora. Il devra être adapté pour fonctionner sur d'autres distributions Linux sur Windows ou OSX

Note 2: Gérer les clés et les mots de passe d'une autorité de certification est sans doute le premier challenge. Comment faire pour que ceux-ci ne soient jamais compromis par le départ d'une personne par exemple et s'assurer qu'ils ne sont pas perdus ? Cet article est un exemple et, assurez-vous que les mots de passe sont sécurisés, notamment en changeant les challenges, c'est à dire xxxxxx.

Note 3: Pour des raisons de sécurité, on préfèrera ne jamais utiliser l'autorité de certification racine et utiliser à la place une autorité intermédiaire. Pour une documentation complète sur le sujet, lisez OpenSSL Certificate Authority par Jaimie Nguyen.

Créer votre autorité de certification ou CA

OpenSSL fournit un script Perl nommé CA.pl qui vous aide à créer et gérer une autorité de certification. Vous pouvez évidemment réaliser toutes ces opérations directement à l'aide des commandes openssl ca et autres. Il faudra alors maintenir les références par vous-mêmes. CA.pl offre un modèle de gestion complet. Pour l'utiliser, installez le package associé à l'aide de dnf ou yum comme ci-dessous:

sudo dnf -y install openssl-perl

Pour créer une autorité de Certification, il suffit alors de lancer la commande ci-dessous:

sudo /usr/bin/CA.pl -newca

Un exemple d'interaction associé à la commande est disponible ci-dessous:

CA certificate filename (or enter to create)

Making CA certificate ...
====
openssl req  -new -keyout /etc/pki/CA/private/cakey.pem -out /etc/pki/CA/careq.pem 
Generating a RSA private key
...................................................................+++++
.........+++++
writing new private key to '/etc/pki/CA/private/cakey.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:FR
State or Province Name (full name) []:Ile-de-France
Locality Name (eg, city) [Default City]:La Garenne Colombes
Organization Name (eg, company) [Default Company Ltd]:Easyteam
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:rootca
Email Address []:sre@easyteam.fr            

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:xxxxxx
An optional company name []:Easyteam
==> 0
====
====
openssl ca  -create_serial -out /etc/pki/CA/cacert.pem -days 1095 -batch
  -keyfile /etc/pki/CA/private/cakey.pem -selfsign -extensions v3_ca  
  -infiles /etc/pki/CA/careq.pem
Using configuration from /etc/pki/tls/openssl.cnf
Enter pass phrase for /etc/pki/CA/private/cakey.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number:
            20:bf:d1:76:3c:b1:3d:2b:21:c3:9c:5b:98:9f:99:43:b7:48:aa:4d
        Validity
            Not Before: Dec 28 10:37:11 2019 GMT
            Not After : Dec 27 10:37:11 2022 GMT
        Subject:
            countryName               = FR
            stateOrProvinceName       = Ile-de-France
            organizationName          = Easyteam
            commonName                = rootca
            emailAddress              = sre@easyteam.fr
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                48:14:DF:EE:56:D1:D8:A6:E9:EA:99:A0:5C:E7:89:98:F8:10:5E:A8
            X509v3 Authority Key Identifier: 
                keyid:48:14:DF:EE:56:D1:D8:A6:E9:EA:99:A0:5C:E7:89:98:F8:10:5E:A8

            X509v3 Basic Constraints: critical
                CA:TRUE
Certificate is to be certified until Dec 27 10:37:11 2022 GMT (1095 days)

Write out database with 1 new entries
Data Base Updated
==> 0
====
CA certificate is in /etc/pki/CA/cacert.pem

Ce script réalise 2 choses importantes:

  • Il crée la clé privée root et la stocke dans le répertoire /etc/pki/CA/private. Vous pouvez :

    • Vérifier que la clé est utilisable à l'aide de la commande sudo openssl pkey -in /etc/pki/CA/private/cakey.pem
    • La sauvegarder car c'est cette clé qui est utilisée pour signer les demandes à venir
    • La sécuriser afin d'assurer que personne ne peut y accéder
  • Il crée un certificat self-signed dans le répertoire /etc/pki/CA. Ce certificat pourra être distribué et permettre à des tiers de vérifier que l'autorité de certification utilisée pour créer un certificat est bien celle-ci. Pour vérifier ce certificat, lancez la commande : openssl x509 -noout -text -in /etc/pki/CA/cacert.pem

Faire une demande de Certificat

Les demandes de certificats doivent être réalisées sur le guest à l'origine de la demande de sorte que la clé privée n'ait pas à être déplacée. Pour créer la demande de certificat, on créera un fichier req.conf comme ci-dessous en prenant soit de mettre:

  • Dans CN, li'dentifiant du guest pour lequel le certificat sera délivré. cela permet de retrouver facilement ce certificat
  • Dans subjectAltName la liste des DNS et IP auxquelles le certificat s'applique
[req]
default_bits           = 4096
distinguished_name     = req_distinguished_name
prompt                 = no
req_extensions         = req_ext

[req_distinguished_name]
C                      = FR
ST                     = Ile-de-France
L                      = La Garenne
O                      = Easyteam
CN                     = registry1.easyteam.fr
emailAddress           = sre@easyteam.fr

[req_ext]
subjectAltName         = DNS:registry,IP:127.0.0.1

Vous pourrez alors générer une clé ainsi qu'une demande de certificat à l'aide de la commande ci-dessous:

openssl req -newkey rsa:4096 -days 365 -config req.conf -out newreq.pem \
   -keyout newkey.pem

La commande vous demande un mot de passe pour protéger votre clé privée. Vous pouvez éviter le prompt en ajoutant la propriété output_password = xxxxxx dans la section [req] du fichier. Si vous voulez que la clé ne soit pas protégée, lancez la commande :

openssl req -newkey rsa:4096 -days 365 -config req.conf -out newreq.pem \
   -keyout newkey.pem -nodes

Pour vérifier la demande de certificat, lancez la commande ci-dessous:

openssl req -in newreq.pem -noout -text

Si vous avez généré une clé protégée par un mot de passe, la commande ci-dessous génère un fichier sans mot de passe:

openssl rsa -in newkey.pem -out newkey-unprotected.pem

Signer la demande de certificat

Dans la requête qui précède, la propriété subjectAltName désigne les alias DNS et IP que le certificat doit référencer. C'est désormais la manière standard de référencer le serveur cible. Pour signer le certificat, le nom du serveur doit être confirmé.

Par défault, le script CA.pl ne gère pas cette extension. Pour corriger cela, il faudra modifier le fichier /etc/pki/tls/openssl.cnf comme ceci:

  • Sous la ligne commençant par HOME, ajoutez SAN = "email:sre@easyteam.fr" en remplaçant le mail par celui de votre équipe
  • à la fin de la section [ usr_cert ], ajoutez la ligne subjectAltName = $ENV::SAN pour indiquer que le Subject Alternative Name sera extrait de la variable d'environnement SAN lors de l'exécution du script CA.pl

Une fois le fichier modifié vous devez :

  • Pousser le fichier de demande de certificat newreq.pem sur l'autorité de certification
  • La signer à l'aide de la commande ci-dessous en prenant soin de faire correspondre la variable SAN à votre demande:
SAN="DNS:registry,IP:127.0.0.1" /usr/bin/CA.pl -sign

Le résultat de la commande est celui ci-dessous:

openssl ca  -policy policy_anything -out newcert.pem  -infiles newreq.pem
Using configuration from /etc/pki/tls/openssl.cnf
Enter pass phrase for /etc/pki/CA/private/cakey.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number:
            20:bf:d1:76:3c:b1:3d:2b:21:c3:9c:5b:98:9f:99:43:b7:48:aa:4e
        Validity
            Not Before: Dec 28 11:39:47 2019 GMT
            Not After : Dec 27 11:39:47 2020 GMT
        Subject:
            countryName               = FR
            stateOrProvinceName       = Ile-de-France
            localityName              = La Garenne
            organizationName          = Easyteam
            commonName                = registry1.easyteam.fr
            emailAddress              = sre@easyteam.fr
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:FALSE
            Netscape Comment: 
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier: 
                82:1E:E8:AC:85:DC:EE:E9:8B:9D:5A:4B:89:EB:96:E7:D6:1E:A1:F3
            X509v3 Authority Key Identifier: 
                keyid:48:14:DF:EE:56:D1:D8:A6:E9:EA:99:A0:5C:E7:89:98:F8:10:5E:A8

            X509v3 Subject Alternative Name: 
                DNS:registry, IP Address:127.0.0.1
Certificate is to be certified until Dec 27 11:39:47 2020 GMT (365 days)
Sign the certificate? [y/n]:yes


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
==> 0
====
Signed certificate is in newcert.pem

Vous pouvez vérifier le contenu du certificat ainsi que le SAN associé en lançant la commande ci-dessous:

openssl x509 -noout -text -in newcert.pem

Transferez le certificat au serveur sur lequel vous voudrez l'utiliser.

Docker HTTPS-Enabled Registry

Pour utiliser la clé et le certificat, mettez les 2 fichiers associés, c'est à dire newkey-unprotected.pem et newcert.pem dans le répertoire $(pwd)/certs puis lancez simplement la commande ci-dessous:

docker run -d -p 5000:5000 --restart=always --name kind-registry \
  --security-opt label=disable -v $(pwd)/certs:/certs \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/newcert.pem \
  -e REGISTRY_HTTP_TLS_KEY=/certs/newkey-unprotected.pem \
  registry:2

Pour tester l'accès à la registry via curl, ajoutez la ligne 127.0.0.1 registry à votre fichier /etc/hosts. Vous devez alors constater ce qui suit:

curl https://registry:5000/
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

Cela indique que l'autorité de certification n'est pas connue de curl. Vous pouvez également vérifier le certificat à l'aide de la commande ci-dessous:

openssl s_client -showcerts -servername registry -connect registry:5000

Pour recommaitre l'autorité de certification via curl, copiez le certificat associé sur la CA et situé dans /etc/pki/CA/cacert.pem sur votre serveur puis lancez la commande ci-dessous. Le code retour est HTTP-200:

curl -v --cacert ./cacert.pem https://registry:5000

Enregistrez le certificat dans la liste des CA que votre système reconnait. Sur Fedora ou Entreprise Linux, procédez comme suit:

sudo cp ./cacert.pem /etc/pki/ca-trust/source/anchors/.
sudo update-ca-trust 
curl https://registry:5000/ -v

Enregistrer le certificat pour Docker

Pour enregister l'autorité de certification avec docker, procédez comme suit:

sudo mkdir -p /etc/docker/certs.d/registry:5000
sudo cp ./cacert.pem /etc/docker/certs.d/registry:5000/ca.crt
sudo systemctl restart docker

Enregistrer la registry en SSL avec un cluster Kind

Pour faire fonctionner la registry sécurisée avec le certificat, reportez- vous à Local Registry et modifier la procédure d'installation comme suit:

  • Dans le script examples/kind-with-registry.sh, remplacez le protocole du endpoint de http à https avant de l'exécuter
  • Ajouter le certicat de l'autorité de certification dans le système du système comme ci-dessous:
docker exec kind-control-plane mkdir /usr/local/share/ca-certificates/registry
docker cp ./certs/cacert.pem kind-control-plane:/usr/local/share/ca-certificates/registry/ca.crt
docker exec kind-control-plane chmod 755 /usr/local/share/ca-certificates/registry
docker exec kind-control-plane chmod 644 /usr/local/share/ca-certificates/registry/ca.crt
docker exec kind-control-plane curl https://registry:5000/

Pour que l'autorité de certification soit prise en compte, vous devrez relancer containerd et kubelet dans le node:

docker exec kind-control-plane systemctl restart containerd
docker exec kind-control-plane systemctl restart kubelet

Si vous utilisez la configuration de l'article précédent concernant Ko et Kind, vous pourrez désormais réaliser le build et le déploiement de votre projet en une seule ligne:

export KO_DOCKER_REPO=registry:5000
ko publish github.com/easyteam-fr/side-effects/blog/ko-helloworld

Pour aller plus loin...

Dans cet article vous avez vu comment intégrer le certificat après avoir démarré votre cluster. Il est possible de l'intégrer à l'image du node Kind au moment du build ou juste après et, ainsi, vous simplifier l'installation.

Easyteam DevOps

Easyteam DevOps