Lerm-IT

Blog traitant de technologies informatiques. Logiciel libre, AdminSys, DevOps et GNU/Linux !

17 Aug 2021

[Puppet] Renouveler le certificat racine de puppet

Votre journée commençait tranquillement quand vous avez lancé une première commande puppet ce matin et là …

Warning: Certificate 'Puppet CA: XXX.company.tld' will expire on 2021-08-22T08:45:12GMT

Le certificat racine de votre infrastructure puppet va expirer dans les prochains jours !

Nous allons donc voir ici comment renouveler ce certificat et le distribuer sur votre infrastructure.

Présentation du fonctionnement des certificats avec Puppet

Quand vous utiliser puppet chaque serveur qui est provisionné a un certificat qui lui permet de s’authentifier sur l’infrastructure. Aussi sur chaque nœud vous trouverez plusieurs certificats dans le dossier /var/lib/puppet/ssl

root@myserver:/var/lib/puppet/ssl# tree
.
├── certificate_requests
│   └── myserver.pem
├── certs
│   ├── ca.pem
│   └── myserver.pem
├── crl.pem
├── private
├── private_keys
│   └── myserver.pem
└── public_keys
    └── myserver.pem

5 directories, 6 files

Comme on peut le voir ci-dessus, nous avons une clé privée et un certificat (private_keys/myserver.pem et certs/myserver.pem) qui permet à l’hôte (ici nommé myserver) de s’authentifier sur le serveur puppetmaster. Nous remarquons aussi que nous avons un fichier certs/ca.pem. Ce fichier permet à l’hôte de valider que le serveur puppetmaster est bien toujours le même et que nous ne faisons pas fasse à une attaque de type man in the middle.

L’avertissement que nous avons plus haut nous informe donc que ce certificat n’est bientôt plus valide. Nous allons voir ici comment le renouveler et le mettre à jour sur notre nos nœuds.

Une fois connecté sur le serveur puppet (puppetmaster) nous pouvons constater que le dossier /var/lib/rancher/ssl est un peu plus fournit que sur un simple nœud puppet client.

root@puppetmaster-server:/var/lib/puppet/ssl# tree
.
├── ca
│   ├── ca_crl.pem
│   ├── ca_crt.pem
│   ├── ca_key.pem
│   ├── inventory.txt
│   ├── private
│   │   └── ca.pass
│   ├── requests
│   │   ├── future-server.pem
│   ├── serial
│   └── signed
│       ├── myserver.pem
│       ├── myserver2.pem
│       ├── myserver3.pem
...
├── certificate_requests
│   └── puppetmaster-server.pem
├── certs
│   ├── puppetmaster-server.pem
│   └── ca.pem
├── crl.pem
├── private
├── private_keys
│   └── puppetmaster-server.pem
└── public_keys
    └── puppetmaster-server.pem

9 directories, 199 files

En effet sur le puppetmaster nous avons à la fois le certificat et la clé de la CA mais aussi tous les certificats qui ont été signés pour nos agents. Notre objectif est donc ici de régénérer le fichier ca/ca_crt.pem` sans casser la validation des certificats déjà signés.

Il est assez facile de constater si une CA est valide pour un certificat avec un simple commande openssl.

root@puppetmaster-server:/var/lib/puppet/ssl# openssl verify  -CAfile ca/ca_crt.pem ca/signed/myserver2.pem 
ca/signed/myserver2.pem: OK

Nous utiliserons cette commande pour valider la bonne tenue de nos actions.

Renouvellement du certificat racine

Nous allons donc renouveler le certificat racine de puppet.

Pour cela nous devons utiliser un fichier CSR. Les fichiers de CSR (Certificat Signing Request) sont des fichiers qui contiennent une partie des informations d’une clé privée ainsi que toutes les informations que nous souhaitons voir dans notre certificat cible (CN, Alt Name, …). Il se peut que ce fichier csr soit déjà présent dans le fichier /var/lib/puppet/ssl/ca/ca_csr.pem mais si comme moi ce n’est pas le cas vous pouvez régénérer ce CSR à partir du certificat et de la clé privée de la CA.

root@puppetmaster-server:/var/lib/puppet/ssl# openssl x509 -x509toreq -in ca/ca_crt.pem -signkey ca/ca_key.pem -out ca/ca_csr.pem
Getting request Private Key
Generating certificate request

Maintenant que nous avons un CSR valide il nous faut créer une configuration OpenSSL valide pour pouvoir régénérer notre CA. Nous allons pour cela créer le fichier /var/lib/puppet/ssl/ca/extension.cnf avec le contenu suivant

[CA_extensions]
basicConstraints = critical,CA:TRUE
nsComment = "Puppet Ruby/OpenSSL Internal Certificate"
keyUsage = critical,keyCertSign,cRLSign
subjectKeyIdentifier = hash

Nous pouvons maintenant générer un nouveau certificat pour notre CA.

root@puppetmaster-server:/var/lib/puppet/ssl# openssl x509 -req -days 3650 -in ca/ca_csr.pem -signkey ca/ca_key.pem -out ca/new_ca_crt.pem -extfile ca/extension.cnf -extensions CA_extensions
Signature ok
subject=/CN=Puppet CA: puppetmaster-server
Getting Private key

Avant de mettre en ligne ce certificat nous pouvons valider que ce certificat est valide.

root@puppetmaster-server:/var/lib/puppet/ssl# openssl verify  -CAfile ca/new_ca_crt.pem ca/signed/myserver3.pem
ca/signed/myserver3.pem: OK

Si vous avez un résultat valide nous pouvons remplacer l’ancien certificat par le précédent et redémarrer le serveur puppet. Dans notre cas le serveur puppet est démarré par un passenger nous devons donc rebooter apache. (Vous pouvez identifier le service à redémarrer via netstat -patune |grep LIST | grep 8140).

root@puppetmaster-server:/var/lib/puppet/ssl# mv ca/ca_crt.pem{,.old}
root@puppetmaster-server:/var/lib/puppet/ssl# mv ca/{new_,}ca_crt.pem
root@puppetmaster-server:/var/lib/puppet/ssl# service apache2 restart
Restarting web server: apache2

Déploiement du certificat sur les agents puppet

Il nous faut maintenant déployer ce fichier ca_crt.pem sur l’ensemble de nos agents puppet. Nous allons pour cela … Utiliser puppet :).

Dans notre dépôt puppet nous pouvons créer un fichier contenant le nouveau certificat de la CA.

$ vim files/puppet/ca.crt
...

Il ne nous reste qu'à l’envoyer dans le bon fichier. Le certificat doit être placé sur les nœuds dans /var/lib/puppet/ssl/certs/ca.pem. Voici un exemple de manifest.

file { '/var/lib/puppet/ssl/certs/ca.pem': 
  source => 'puppet:///modules/mymodule/puppet/ca.pem', 
  owner => 'puppet', 
  group => 'puppet', 
}

Et les agents dans tout ca?

Il est possible que vous rencontriez en même temps que l’expiration du certificat de la CA, l’expiration des certificats de vos agents. Ceux-ci aussi doivent être renouvelés. Il existe des modules puppet qui permettent de faire ceci automatiquement mais si vous voulez le faire manuellement voici la marche à suivre.

Sur le serveur puppetmaster

root@puppetmaster-server:~# puppet cert clean myserver2
Notice: Revoked certificate with serial 72
Notice: Removing file Puppet::SSL::Certificate myserver2 at '/var/lib/puppet/ssl/ca/signed/myserver2.pem'
Notice: Removing file Puppet::SSL::Certificate myserver2 at '/var/lib/puppet/ssl/certs/myserver2.pem'

Sur le serveur myserver2

root@myserver2:~# find /var/lib/puppet/ssl -name myserver2.pem -delete
root@myserver2:~# puppet agent -t
Info: Creating a new SSL key for myserver2
Info: csr_attributes file loading from /etc/puppet/csr_attributes.yaml
Info: Creating a new SSL certificate request for
myserver2
Info: Certificate Request fingerprint (SHA256):
...

Si vous n’avez pas de signature automatique il vous faut retourner sur le serveur puppetmaster pour signer ce nouveau certificat.

root@admin-monitoring:~# puppet cert sign myserver2
Notice: Signed certificate request for myserver2
Notice: Removing file Puppet::SSL::CertificateRequest myserver2 at '/var/lib/puppet/ssl/ca/requests/myserver2.pem'

Note: Le script suivant permet d’identifier les certificats qui vont expirer d’ici le mois prochain.

for i in $( ls /var/lib/puppet/ssl/ca/signed/ )
do
  end_date=$( openssl x509 -noout -in /var/lib/puppet/ssl/ca/signed/${i} -dates | grep notAf | cut -d"=" -f2 )
  end_date_s=$( date -d "$end_date" +%s )
  test $end_date_s -lt $( date --date='+1 month' +"%s" ) && echo "Should renew $i (will expire at $end_date)"
done

Conclusion

Après un passage global de puppet sur nos agents notre certificat racine a bien été renouvelé et nous pouvons reprendre notre journée de travail normalement :).