Outils pour utilisateurs

Outils du site


sysadmin:linux:gestion_certificats:generer_certificat_san_localhost

Générer un certificat SAN pour les services web locaux

Lors de mes différentes tentatives je n'ai pas pu générer de certificat valide utilisant un wilcard sous un domaine de premier niveau (TLD) comme *.localhost, *.local ou *.lan. Par contre les certificats SAN génériques utilisant un wilcard à partir d'un nom de domaine de deuxième niveau étaient valides et acceptés par les navigateurs.

Le principe est de gérer un seul certificat SSL pouvant être présenté par le reverse proxy pour l'ensemble des web services tournant localement. C'est ce que permet de faire le certificat SAN contenant un champ Subject Alternate Name pouvant contenir une liste de FQDN utilisée à la place du champ Common Name voir le wiki

Après plusieurs essais il ressort que:

  • Les certificats SAN listant des TLD sont valides s'ils n'utilisent pas le wilcard;
  • Les certificats SAN génériques sur un domaine de deuxieme niveau sont refusés: *.localhost, *.local, *.any sont invalides;
  • Les certificats SAN utilisant le wilcard sur un nom de domaine de troisième niveau sont valides: *.any.domain est valide

De ce que j'ai pu déduire le caractère générique (étoile ou wilcard) ne peut s'appliquer que sur un FQDN pour désigner les serveurs ou les sous-domaines mais pas les domaines et les TLDs.

Pour rappel le FQDN est formaté ainsi: server.subdomain.domain.tld.

Notons également que c'est en général une mauvaise idée de travailler directement avec .localhost ou avec un TLD:

  • L'enregistrement ou la configuration de cookies peut poser des problèmes;
  • Certaines API incluses dans l'application rejettent des URL de la forme http://localhost les services peuvent alors produire des erreurs ou se comporter différemment en local;

Ci-dessous pour rappel les manipulations faites avec un certificat de test

# Affiche la valeur du champ SAN (Subject Alternate Name)
openssl x509 -noout -text -in service.localhost.crt | grep -i -A 1 "Subject Alternative Name"
            X509v3 Subject Alternative Name: 
                IP Address:127.0.0.1, DNS:localhost, DNS:service.localhost, DNS:*.service.localhost, DNS:any, DNS:*.any

Comme on peut le voir ci-dessus, le champ SAN contient une adresse IP 127.0.0.1, si on contacte le serveur qui a cette même IP tout se passe bien:

curl -v --cacert ca-chain.cert.pem https://127.0.0.1
...
*  subjectAltName: host "127.0.0.1" matched cert's IP address!

Le champ SAN contient deux noms localhost et any, ici encore tout se passe bien:

curl -v --cacert ca-chain.cert.pem https://localhost
...
*  subjectAltName: host "localhost" matched cert's "localhost"

C'est pour les valeurs *.any et *.localhost du certificat que le résultat obtenu est différent de celui attendu. Le caractère générique devrait nous permettre d'utiliser un nom de la forme traefik.any ou traefik.localhost. Cependant on obtient une erreur:

curl -v --cacert ca-chain.cert.pem https://traefik.any
...
*  subjectAltName does not match traefik.any
* SSL: no alternative certificate subject name matches target host name 'traefik.any'
* Closing connection 0
* TLSv1.3 (OUT), TLS alert, close notify (256):
curl: (60) SSL: no alternative certificate subject name matches target host name 'traefik.any'
More details here: https://curl.haxx.se/docs/sslcerts.html

Cette erreur ne se manifeste plus au niveau inférieur avec *.service.localhost

curl -v --cacert ca-chain.cert.pem https://traefik.service.localhost
...
*  subjectAltName: host "traefik.service.localhost" matched cert's "*.service.localhost"

Pour ces raisons, il est préférable de générer un certificat SAN avec des FQDN ( c'est à dire des noms de domaine de troisième niveau comme *.something.tld.

En utilisant .localhost en TLD on évite tout risque collision (La RFC 2606 le définit comme l'un des quatre domaines de premier niveau réservé). Aucun sous-domaines réels dans le système de nom de domaine (Domain Name System) d'Internet ne peut être construit en dessous. Un nom d'un domaine de premier niveau réservé n'est pas inclus dans les serveurs racines du DNS.

Pour que le nom de domaine évoque bien que l'hébergement de services locaux j'ai fait le choix d'utiliser *.services.localhost

# Depuis le répertoire de l'autorité de certification intermédiaire
cd intermediate_ca/
export dn=localhost
 
# Création de la clé privée
openssl genrsa -out private/$dn.key 2048
chmod 400 private/$dn.key
 
# Création de la CSR
openssl req -new -config CSR.cnf -key private/$dn.key -out csr/$dn.csr

Préparer le fichier des extensions X509 en complétant la section [alt_names] avec les FQDN

# Création du certificat
openssl x509 -req -extfile build_cert_with_SAN.ext -days 30 -in csr/$dn.csr -CA certs/intermediate_ca.cert.pem -CAkey private/intermediate_ca.key -CAcreateserial -out certs_x509/$dn.crt
 
# effacer la variable
unset $dn
# via cURL
curl --verbose --cacert ca-chain.cert.pem https://server.domain
 
# via openssl
true | openssl s_client -showcerts -CAfile ca-chain.cert.pem traefik.labinfo.mairie.local:443 2>/dev/null | openssl x509 -noout -text

Dans l' exemple ci-dessus le certificat de l'autorité de confiance n'est pas installé sur le système, les options --cacert pour curl et -CAfile pour openssl permettent de fournir les certificats des autorités de confiance afin de valider la chaîne de certification dans sa globalité.

Voir le wiki installer le certificat racine d'une autorité de confiance.

Renouveler le certificat

Références

sysadmin/linux/gestion_certificats/generer_certificat_san_localhost.txt · Dernière modification : 2022/08/02 15:55 de yoann