Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

Chapitre 619 minIntermédiaire

HTTPS sur Nginx : Let's Encrypt, Certbot et certificats internes

Par Josselyn C., prof de BTS SIO. Mis à jour le .

À la fin de ce chapitre, tu sauras

  • choisir entre autorité publique, autorité interne et certificat autosigné ;
  • obtenir et renouveler automatiquement un certificat Let's Encrypt avec Certbot ;
  • demander un certificat à l'autorité interne et faire confiance à sa racine ;
  • configurer TLS, la redirection et HSTS dans Nginx ;
  • vérifier un certificat et corriger les erreurs courantes.

3 exercices corrigés1 schéma

Passer un site en HTTPS, c'est installer sur le serveur web un certificat TLS : un fichier signé par une autorité de certification, qui associe le nom du site à une clé publique et permet au navigateur de vérifier qu'il parle au bon serveur avant de chiffrer les échanges. Ce chapitre montre comment obtenir et renouveler automatiquement un certificat gratuit de Let's Encrypt avec Certbot pour un site public, comment obtenir un certificat de l'autorité interne pour un service du réseau local, comment configurer TLS dans Nginx et comment vérifier un certificat. Le fonctionnement de la cryptographie elle-même (clés, signature, chaîne de confiance, poignée de main TLS 1.3) est expliqué dans le cours Cryptographie.

Quel certificat pour quel service

ServiceQui doit lui faire confianceCertificatChez Sorbier
Site publicN'importe quel navigateur sur InternetAutorité publique : Let's Encrypt (gratuit, automatisé) ou une autorité commercialesuivi.sorbier-logistique.fr sur srv-web
Service interneLes postes et serveurs de l'organisationAutorité interne de l'entrepriseglpi.ad.sorbier.example sur srv-glpi, signé par l'autorité AD CS Sorbier-AC
Maquette, testToi seulAutosignéUne VM de TP, jamais en production

Une autorité publique ne délivre de certificat que pour un nom public, dont elle peut vérifier que tu le contrôles : jamais pour srv-glpi, glpi.ad.sorbier.example (non publié sur Internet) ou une adresse IP privée. Les noms internes passent donc par l'autorité interne, que les postes du domaine reconnaissent parce que son certificat racine leur est distribué par stratégie de groupe (cours Windows Server, chapitre sur l'autorité de certification AD CS).

Let's Encrypt et le protocole ACME

Let's Encrypt est une autorité de certification publique, gratuite et automatisée. Un logiciel client, ici Certbot, dialogue avec elle selon le protocole ACME (RFC 8555) pour prouver que tu contrôles le nom demandé, puis obtient et renouvelle le certificat sans intervention humaine.

La preuve la plus courante est le défi HTTP-01 :

srv-webCertbot et NginxPare-feu203.0.113.17 → .225Let's Encryptautorité ACME1. demande un certificat pour suivi.sorbier-logistique.fr2. jeton à publier sous /.well-known/acme-challenge/3. lecture du jeton en HTTPredirection du port 804. jeton correct : certificat signé (90 jours)Certbot installe le certificat dans Nginx et programme son renouvellement.
Le défi HTTP-01 : Let's Encrypt vérifie que le nom pointe vers un serveur que tu contrôles
DéfiPreuveQuand l'utiliser
HTTP-01Un fichier publié sur le port 80 du serveur, à l'adresse /.well-known/acme-challenge/Un serveur web joignable depuis Internet sur le port 80 : le cas le plus simple
DNS-01Un enregistrement TXT _acme-challenge.<nom> publié dans la zone DNSUn certificat générique (*.sorbier-logistique.fr), ou un serveur non joignable depuis Internet ; demande une API chez l'hébergeur DNS
TLS-ALPN-01Une réponse spéciale pendant la poignée de main TLS, sur le port 443Quand seul le port 443 est ouvert ; peu de clients le gèrent

La durée de validité. Les certificats de Let's Encrypt durent aujourd'hui 90 jours et Certbot les renouvelle vers les deux tiers de cette durée. Les règles du CA/Browser Forum, qui s'imposent à toutes les autorités publiques, réduisent pas à pas la durée maximale : 200 jours depuis le 15 mars 2026, 100 jours à partir du 15 mars 2027, 47 jours à partir du 15 mars 2029. Let's Encrypt passera à 64 jours le 10 février 2027, puis à 45 jours le 16 février 2028. Conséquence pour toi : un certificat public ne se renouvelle plus à la main, et un renouvellement programmé « tous les 60 jours » ne suffira bientôt plus. Laisse Certbot décider de la date.

Deux services disparus. Depuis 2025, Let's Encrypt n'envoie plus de courriel avant l'expiration d'un certificat (c'est à ta supervision de surveiller les dates) et ne publie plus de répondeur OCSP : la directive ssl_stapling de Nginx est inutile avec ses certificats, et produit l'avertissement "ssl_stapling" ignored, no OCSP responder URL in the certificate.

Obtenir un certificat avec Certbot

Les prérequis. Avant de lancer Certbot sur srv-web :

  • le nom suivi.sorbier-logistique.fr a un enregistrement A, dans la zone DNS publique, vers 203.0.113.17 ;
  • le pare-feu redirige les ports 80 et 443 de cette adresse vers 192.168.10.225 (règles de la DMZ dans Cybersécurité des infrastructures) ;
  • srv-web peut joindre l'API de Let's Encrypt en HTTPS (acme-v02.api.letsencrypt.org) : si le filtrage sortant de la DMZ n'autorise que les dépôts de mises à jour, ajoute ce nom à l'alias ;
  • le server block du site existe, avec le bon server_name (chapitre 2).
bash
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d suivi.sorbier-logistique.fr

Certbot demande une adresse électronique (facultative, pour les annonces importantes) et l'acceptation des conditions d'utilisation, réalise le défi HTTP-01 en ajoutant temporairement un bloc location à Nginx, puis modifie le server block : il ajoute l'écoute en HTTPS, les chemins du certificat, et un second bloc qui redirige le port 80 vers HTTPS.

text
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/suivi.sorbier-logistique.fr/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/suivi.sorbier-logistique.fr/privkey.pem
This certificate expires on 2027-01-19.
Certbot has set up a scheduled task to automatically renew this certificate in the background.
Deploying certificate
Successfully deployed certificate for suivi.sorbier-logistique.fr to /etc/nginx/sites-enabled/suivi
Congratulations! You have successfully enabled HTTPS on https://suivi.sorbier-logistique.fr
FichierContenu
/etc/letsencrypt/live/<nom>/fullchain.pemLe certificat du site suivi du certificat intermédiaire : c'est lui que Nginx présente
/etc/letsencrypt/live/<nom>/privkey.pemLa clé privée, lisible par root seulement : elle ne quitte jamais le serveur
/etc/letsencrypt/renewal/<nom>.confComment renouveler ce certificat (méthode, options)
/etc/letsencrypt/options-ssl-nginx.confLes réglages TLS que Certbot inclut dans le server block

Les fichiers de live/ sont des liens vers la dernière version, dans archive/ : après un renouvellement, Nginx trouve le nouveau certificat au même chemin.

Le renouvellement automatique. Le paquet installe un minuteur systemd qui lance certbot renew deux fois par jour ; Certbot ne renouvelle que les certificats proches de l'expiration, puis recharge Nginx.

bash
systemctl list-timers certbot.timer
sudo certbot certificates           # liste des certificats, dates d'expiration
sudo certbot renew --dry-run        # simule un renouvellement contre l'environnement de test

Configurer TLS dans Nginx

Certbot écrit une configuration qui fonctionne. Il est utile de savoir l'écrire toi-même, pour un certificat interne ou pour la relire en recette. Le server block complet du site de suivi en HTTPS :

nginx
# Redirection de HTTP vers HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name suivi.sorbier-logistique.fr;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name suivi.sorbier-logistique.fr;

    ssl_certificate     /etc/letsencrypt/live/suivi.sorbier-logistique.fr/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/suivi.sorbier-logistique.fr/privkey.pem;

    # Le navigateur n'utilisera plus que HTTPS pour ce site pendant un an
    add_header Strict-Transport-Security "max-age=31536000" always;
    add_header X-Content-Type-Options "nosniff" always;

    root /var/www/suivi/public;
    index index.php index.html;
    access_log /var/log/nginx/suivi.access.log;
    error_log  /var/log/nginx/suivi.error.log;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.4-fpm-suivi.sock;
    }
    location ~ /\. {
        deny all;
    }
}
DirectiveRôle
return 301 https://$host$request_uriRedirection permanente vers la même adresse en HTTPS
listen 443 sslÉcoute en TLS sur le port 443
http2 onActive HTTP/2 (depuis Nginx 1.25, cette directive remplace le paramètre http2 de listen)
ssl_certificateLa chaîne : le certificat du site suivi des intermédiaires, jamais le certificat seul
ssl_certificate_keyLa clé privée
Strict-Transport-SecurityHSTS : même si l'utilisateur tape http://, le navigateur passe directement en HTTPS, sans requête en clair interceptable
X-Content-Type-Options: nosniffLe navigateur respecte le type annoncé d'un fichier au lieu de le deviner

Les protocoles sont déjà réglés dans nginx.conf de Debian 13 : ssl_protocols TLSv1.2 TLSv1.3. Ne réactive jamais TLS 1.0 ou 1.1 « pour un vieux client » : ils sont obsolètes et les navigateurs les refusent.

Comme au port 80, un server block par défaut sur le port 443 évite de présenter le certificat de ton site à qui balaie les adresses IP. ssl_reject_handshake refuse la poignée de main sans avoir besoin de certificat :

nginx
server {
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;
    ssl_reject_handshake on;
}

Un certificat de l'autorité interne

GLPI sera publié sur le réseau local sous le nom glpi.ad.sorbier.example, un alias (CNAME) de srv-glpi.ad.sorbier.example. Son certificat vient de l'autorité d'entreprise Sorbier-AC, avec le modèle SorbierServeurWeb, réservé au groupe GG_Serveurs_Web (cours Windows Server, modèles de certificats d'AD CS).

1. Sur srv-glpi, générer la clé et la demande de certificat (CSR). La clé privée est créée sur le serveur et n'en sort jamais ; seule la demande, qui contient la clé publique et les noms, voyage.

bash
sudo mkdir -p /etc/ssl/glpi && cd /etc/ssl/glpi
sudo openssl req -new -newkey rsa:3072 -noenc \
  -keyout glpi.key -out glpi.csr \
  -subj "/CN=glpi.ad.sorbier.example" \
  -addext "subjectAltName=DNS:glpi.ad.sorbier.example,DNS:srv-glpi.ad.sorbier.example"
sudo chmod 600 glpi.key
openssl req -in glpi.csr -noout -text | grep -A1 "Subject Alternative Name"
#             X509v3 Subject Alternative Name:
#                 DNS:glpi.ad.sorbier.example, DNS:srv-glpi.ad.sorbier.example

Les navigateurs ne lisent que l'extension SAN (Subject Alternative Name) et ignorent le nom commun CN : un certificat sans SAN est refusé, même si son CN est juste. -noenc (l'ancien -nodes) laisse la clé sans phrase de passe, pour que Nginx puisse la lire au démarrage sans intervention ; elle est protégée par ses droits (600, propriétaire root).

2. Depuis le poste d'administration, soumettre la demande à l'autorité. Copie glpi.csr sur le poste d'administration (avec scp), puis, dans un terminal PowerShell ouvert avec un compte autorisé sur le modèle :

powershell
certreq -submit -attrib "CertificateTemplate:SorbierServeurWeb" glpi.csr glpi.cer

certreq propose de choisir l'autorité, puis enregistre le certificat délivré dans glpi.cer. Exporte aussi le certificat racine de l'autorité, dont les serveurs Linux auront besoin : dans certsrv.msc, Propriétés de l'autorité, onglet Général, Afficher le certificat, onglet Détails, Copier dans un fichier, format X.509 encodé en Base 64 (.cer), fichier sorbier-ac.cer.

3. Sur srv-glpi, installer le certificat et faire confiance à l'autorité. Copie glpi.cer et sorbier-ac.cer sur le serveur. Les fichiers au format Base 64 commencent par -----BEGIN CERTIFICATE----- : c'est le format PEM qu'attend Nginx. Un fichier binaire (DER) se convertit avec openssl x509 -inform DER -in glpi.cer -out glpi.crt.

bash
sudo cp glpi.cer /etc/ssl/glpi/glpi.crt
# Le certificat racine, ajouté au magasin de confiance du système
sudo cp sorbier-ac.cer /usr/local/share/ca-certificates/sorbier-ac.crt
sudo update-ca-certificates
# 1 added, 0 removed; done.
openssl verify /etc/ssl/glpi/glpi.crt
# /etc/ssl/glpi/glpi.crt: OK

update-ca-certificates ne prend que les fichiers d'extension .crt du dossier /usr/local/share/ca-certificates/. Une fois la racine ajoutée, curl, PHP et les autres programmes du serveur font confiance à tout certificat signé par Sorbier-AC : c'est indispensable pour que GLPI se connecte à l'annuaire en LDAPS (chapitre 7).

Dans le server block de GLPI, les deux lignes deviennent :

nginx
    ssl_certificate     /etc/ssl/glpi/glpi.crt;
    ssl_certificate_key /etc/ssl/glpi/glpi.key;

Avec une autorité à un seul niveau, le certificat du serveur suffit : les clients ont déjà la racine. Avec une autorité racine hors ligne et une autorité secondaire, concatène le certificat du serveur puis celui de l'autorité secondaire dans le fichier de ssl_certificate.

Le renouvellement. AD CS ne parle pas le protocole ACME : le certificat interne se renouvelle à la main, ou par un script, avant sa date d'expiration (la durée dépend du modèle, souvent un ou deux ans). Note la date dans la documentation et surveille-la dans ta supervision. Des autorités internes compatibles ACME existent (step-ca, par exemple) et permettent d'utiliser Certbot en interne ; elles sont plus rares dans un environnement Windows.

Un certificat autosigné, pour une maquette seulement

Pour un TP où tu n'as pas d'autorité, un certificat autosigné se crée en une commande :

bash
sudo openssl req -x509 -newkey rsa:3072 -noenc -days 30 \
  -keyout /etc/ssl/private/maquette.key -out /etc/ssl/certs/maquette.crt \
  -subj "/CN=maquette.lab" -addext "subjectAltName=DNS:maquette.lab"

Le chiffrement est le même qu'avec un vrai certificat, mais rien ne prouve l'identité du serveur : le navigateur affiche un avertissement, et un utilisateur habitué à cliquer sur « Continuer » cliquera aussi devant le faux certificat d'un attaquant. En production, il n'a pas sa place, et à l'oral d'E6, « j'ai mis un certificat autosigné » appelle toujours la question « et en production ? ».

Vérifier un certificat et dépanner

bash
# Le certificat présenté par un serveur : sujet, émetteur, dates, noms
openssl s_client -connect suivi.sorbier-logistique.fr:443 -servername suivi.sorbier-logistique.fr </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -ext subjectAltName

# La négociation complète, vue par un client
curl -vI https://glpi.ad.sorbier.example 2>&1 | grep -E 'SSL connection|subject|issuer|expire|HTTP/'

L'option -servername envoie le nom demandé dans la poignée de main (extension SNI) : sans elle, un serveur qui héberge plusieurs sites présente son certificat par défaut, et tu crois à tort qu'il est faux.

Erreur côté clientCauseCorrection
certificate has expired / NET::ERR_CERT_DATE_INVALIDCertificat expiré, ou horloge du client fausseRenouveler ; vérifier certbot renew et l'heure des machines
no alternative certificate subject name matches target host name / ERR_CERT_COMMON_NAME_INVALIDLe nom tapé n'est pas dans le SANRefaire la demande avec tous les noms utilisés (alias compris)
unable to get local issuer certificateChaîne incomplète (intermédiaire absent), ou racine inconnue du clientfullchain.pem plutôt que cert.pem ; racine interne distribuée aux clients
self-signed certificateCertificat autosignéCertificat d'une autorité reconnue
nginx: [emerg] cannot load certificate key … Permission deniedNginx ne lit pas la clé (droits)Clé propriété de root, droits 600 : le processus maître de Nginx, lancé en root, la lit au démarrage

La date d'expiration de chaque certificat est un indicateur à superviser : Zabbix propose un modèle qui la lit et alerte quelques semaines avant l'échéance (cours Supervision).

Exercices

Exercice 1 sur 3 Le bon certificat Réussi À revoir

Pour chaque service, dis quel type de certificat tu installes (autorité publique, autorité interne, autosigné) et justifie en une phrase : (a) l'interface web de pfSense, consultée depuis le réseau d'administration ; (b) recette.sorbier-logistique.fr, publié sur Internet pour un client qui teste la nouvelle version ; (c) une VM de TP montée pour une démonstration en classe ; (d) le serveur MariaDB de srv-glpi, joint par Grafana ; (e) l'intranet intranet.ad.sorbier.example.

Voir la réponseMasquer la réponse de l'exercice 1
ServiceCertificatJustification
(a) pfSenseAutorité interneNom interne, consulté seulement par des postes du domaine qui connaissent Sorbier-AC
(b) recette publiqueAutorité publique (Let's Encrypt)Le client externe ne connaît pas l'autorité interne ; le nom est public, le défi HTTP-01 ou DNS-01 est possible
(c) VM de TPAutosignéAucune autorité disponible, aucun utilisateur réel ; durée courte
(d) MariaDBAutorité interneLe client (Grafana sur srv-journaux) est un serveur interne qui fait confiance à Sorbier-AC et peut vérifier le nom
(e) intranetAutorité interneNom interne ad.sorbier.example, non publié : aucune autorité publique ne le signerait

Tu avais trouvé ?

Exercice 2 sur 3 La demande de certificat Réussi À revoir

Le nouvel intranet sera joint sous trois noms : intranet.ad.sorbier.example, intranet (nom court, utilisé par les postes grâce au suffixe DNS) et srv-intranet.ad.sorbier.example. Écris la commande openssl qui crée la clé et la demande, puis explique pourquoi le nom court pose problème.

Voir la réponseMasquer la réponse de l'exercice 2
bash
sudo openssl req -new -newkey rsa:3072 -noenc \
  -keyout intranet.key -out intranet.csr \
  -subj "/CN=intranet.ad.sorbier.example" \
  -addext "subjectAltName=DNS:intranet.ad.sorbier.example,DNS:srv-intranet.ad.sorbier.example,DNS:intranet"

Le nom court intranet n'est pas un nom complet : il n'a de sens que dans le réseau qui ajoute le suffixe ad.sorbier.example. Aucune autorité publique ne l'accepterait ; une autorité interne peut le signer, mais c'est une mauvaise habitude. La bonne pratique est de rediriger le nom court vers le nom complet (un server block server_name intranet; qui renvoie un return 301 https://intranet.ad.sorbier.example$request_uri;) et de n'utiliser que le nom complet dans les liens.

Tu avais trouvé ?

Exercice 3 sur 3 Le renouvellement qui a échoué Réussi À revoir

Le 3 mars, le site de suivi affiche une erreur de certificat expiré. sudo certbot renew répond : Certbot failed to authenticate some domains (authenticator: nginx). The Certificate Authority reported these problems: Domain: suivi.sorbier-logistique.fr Type: connection Detail: 203.0.113.17: Fetching http://suivi.sorbier-logistique.fr/.well-known/acme-challenge/…: Timeout during connect (likely firewall problem). Explique ce qui s'est passé, propose la correction, puis deux mesures pour que cela ne se reproduise pas.

Voir la réponseMasquer la réponse de l'exercice 3

Ce qui s'est passé. Les serveurs de Let's Encrypt n'arrivent pas à joindre le port 80 de 203.0.113.17 : le défi HTTP-01 échoue, donc le certificat n'est plus renouvelé depuis des semaines, jusqu'à son expiration. Cause probable : la redirection du port 80 vers srv-web a été supprimée sur le pare-feu (par exemple « puisque le site est en HTTPS »), ou une règle bloque désormais ce port.

Correction. Rétablir la redirection du port 80 de l'adresse publique vers 192.168.10.225 (Nginx y répond par une redirection vers HTTPS, sauf pour les fichiers du défi), puis sudo certbot renew et vérifier avec openssl s_client.

Prévention. Superviser la date d'expiration du certificat avec une alerte un mois avant (Zabbix, ou un contrôle externe) : Let's Encrypt n'envoie plus de courriel. Et envoyer le journal de Certbot (/var/log/letsencrypt/letsencrypt.log) ou l'état de l'unité certbot.service à la supervision, pour qu'un renouvellement en échec déclenche une alerte dès la première tentative ratée. On peut aussi passer au défi DNS-01, qui ne dépend pas du port 80.

Tu avais trouvé ?

Chapitre terminé !

Fais les 3 exercices avant de passer à la suite : c'est en t'entraînant que tout se fixe.

chapitres
–
de série
–
du cours
–
Chapitre suivant : Déployer GLPI avec Nginx, PHP-FPM et MariaDBRevoir le sommaire du cours