Table des matières
- Une panne de certificat, c'est grave à quel point ?
- Pourquoi le renouvellement automatique ne suffit-il pas ?
- Comment vérifier l'expiration d'un certificat en ligne de commande ?
- Attraper le cas « renouvelé mais pas rechargé »
- Quels seuils une alerte de certificat doit-elle utiliser ?
- Comment transformer ça en alerte qui m'atteint vraiment ?
- Étape 1 — Créez deux canaux, pas un
- Étape 2 — Lancez la vérification
- Étape 3 — Planifiez-la, et alertez quand la planification elle-même échoue
- Étape 4 — Séparez l'échelle avec des conditions
- Étape 5 — Testez avant de faire confiance
- Et si ma supervision vérifie déjà les certificats ?
- Ce qu'Echobell ne fait pas
- FAQ
- Let's Encrypt a-t-il vraiment arrêté les e-mails d'expiration ?
- Combien de temps un certificat TLS est-il valable en 2026 ?
- Faut-il alerter sur l'expiration si j'utilise ARI ?
- Et les certificats qui ne sont pas sur un serveur web ?
- Une vérification quotidienne ne va-t-elle pas devenir du bruit ?
- Toute l'équipe peut-elle recevoir l'alerte de certificat ?
- Est-ce que ça marche sur macOS ?
- Est-il prudent d'envoyer des détails du certificat dans une notification ?
- À lire aussi
Deux choses ont changé sous l'infrastructure de certificats de tout le monde, et la plupart des équipes ne se sont adaptées ni à l'une ni à l'autre.
D'abord, le filet de sécurité a disparu. Let's Encrypt a fermé son service de notification d'expiration le 4 juin 2025 — ces e-mails qui ont discrètement sauvé des milliers de sites quand l'automatisation cassait. Le raisonnement se tenait (la plupart des abonnés automatisent le renouvellement, conserver des millions d'adresses e-mail est un risque pour la vie privée, et le service coûtait « des dizaines de milliers de dollars par an »), et l'annonce se termine en vous invitant à trouver une supervision tierce (Let's Encrypt). Beaucoup ont lu ça, approuvé, puis n'ont jamais fait la seconde moitié.
Ensuite, la marge d'erreur s'est effondrée. Depuis le 15 mars 2026, un certificat TLS public est valable au maximum 200 jours, puis 100 jours à partir du 15 mars 2027 et 47 jours à partir du 15 mars 2029, selon le scrutin SC-081v3 du CA/Browser Forum. Let's Encrypt va plus vite que le plafond : les certificats de 6 jours sont disponibles pour tous depuis le 15 janvier 2026, et son plan amène le profil par défaut à 64 jours en février 2027 puis 45 jours en février 2028 (Let's Encrypt).
Les deux changements poussent dans le même sens. Le renouvellement a lieu plus souvent, donc il a plus d'occasions de casser, et quand il casse plus personne ne vous écrit. Ce guide est la vérification qui comble ce trou : un script shell testé, des seuils adaptés aux certificats à durée courte, et un moyen de faire sonner votre téléphone dans le dernier cas, avec Echobell.
Une panne de certificat, c'est grave à quel point ?
Assez grave pour que plus d'un tiers des organisations en ait subi une l'an dernier. Dans le 2026 Global Certificate Management Outlook de DigiCert — une enquête Propeller Insights auprès de 1 001 décideurs IT et cybersécurité aux États-Unis, au Royaume-Uni et en Australie, menée en mai 2026 — plus d'un tiers des organisations déclarent une interruption de service causée par un certificat expiré au cours de l'année écoulée. Près des trois quarts ont cumulé au moins cinq heures d'indisponibilité liée aux certificats, une sur cinq a atteint 25 heures ou plus, et près d'une sur quatre indique que son incident le plus grave a coûté plus de 250 000 dollars (DigiCert).
Ce qui est intéressant dans ces chiffres, c'est la durée. Cinq heures, ce n'est pas le temps que prend un renouvellement — un renouvellement prend quelques secondes. Cinq heures, c'est le temps qu'il a fallu à quelqu'un pour s'en apercevoir.
Pourquoi le renouvellement automatique ne suffit-il pas ?
Parce que l'automatisation du renouvellement échoue en silence, et qu'un cron qui a cessé de tourner ne produit aucune sortie. Chacun de ces cas est réel et fréquent, et aucun ne fait de bruit :
- Le timer ne tourne plus. Une montée de version, une reconstruction de conteneur, ou un
systemctl disabled'il y a trois mois dont personne ne se souvient. Uncertbot.timerqui ne se déclenche pas ressemble exactement à uncertbot.timerqui se déclenche bien. - Le renouvellement a réussi mais le service n'a jamais rechargé. Le nouveau certificat est sur le disque ; nginx, HAProxy ou Postfix garde l'ancien en mémoire. C'est la façon la plus courante dont une installation « entièrement automatisée » expire quand même.
- Le chemin du challenge est cassé. Quelqu'un a ajouté une redirection, une règle WAF ou un
Denydevant/.well-known/acme-challenge/, et HTTP-01 échoue. Ou le jeton d'API du fournisseur DNS utilisé pour DNS-01 a expiré. - Le renouvellement n'a eu lieu que sur un nœud. Deux répartiteurs de charge, un seul cron. Le second continue de servir l'ancien certificat jusqu'à ce qu'il ne le puisse plus.
- Le certificat n'est pas du tout sur un serveur web. Clients mTLS internes, broker Kafka, serveur LDAP, concentrateur VPN, certificat push de gestion de flotte. Rien sur l'internet public ne le voit et aucun client ACME ne le gère.
- Le renouvellement est figé sur le mauvais intervalle. Let's Encrypt est explicite : « renouveler à un intervalle codé en dur de 60 jours ne suffira plus » une fois le profil par défaut passé à 64 puis 45 jours (Let's Encrypt).
Le dernier mérite d'être souligné, parce que c'est la panne vers laquelle le calendrier pousse tout le monde. Si votre cadence de renouvellement est un nombre saisi par quelqu'un en 2022, la durée de vie des certificats se dirige maintenant droit dessus.
Comment vérifier l'expiration d'un certificat en ligne de commande ?
Un seul pipeline openssl, sans dépendances. Pour un hôte en ligne :
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -startdate -enddate
Pour un fichier sur le disque :
openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -noout -enddate
L'option -servername n'est pas facultative sur une IP partagée : sans SNI vous obtenez le certificat que le serveur considère par défaut, qui n'est peut-être pas celui qui vous inquiète.
Pour une réponse par oui ou non, évitez complètement l'analyse de dates. openssl x509 -checkend <secondes> sort avec 0 si le certificat survit à cette fenêtre et 1 s'il expire dedans (y compris s'il a déjà expiré) :
openssl x509 -in fullchain.pem -noout -checkend $((14 * 86400)) \
|| echo "expire dans moins de 14 jours"
Ce contrat de code de sortie, c'est toute la primitive de supervision. Tout le reste n'est que tuyauterie autour.
Attraper le cas « renouvelé mais pas rechargé »
Comparez ce qui est sur le disque avec ce qui est réellement servi. C'est la vérification que presque personne ne fait, et elle attrape la panne que l'automatisation du renouvellement ne peut pas voir :
served=$(openssl s_client -connect 127.0.0.1:443 -servername example.com </dev/null 2>/dev/null \
| openssl x509 -noout -fingerprint -sha256)
ondisk=$(openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -noout -fingerprint -sha256)
[ "$served" = "$ondisk" ] || echo "le service sert un certificat périmé — rechargement nécessaire"
Les deux commandes affichent exactement le même format sha256 Fingerprint=AB:CD:..., une simple comparaison de chaînes suffit donc. Lancez-la quelques minutes après la fenêtre de votre timer de renouvellement.
Quels seuils une alerte de certificat doit-elle utiliser ?
Des fractions de la durée de vie du certificat, pas un nombre de jours fixe. Une règle « alerter à 30 jours » était raisonnable pour des certificats de 90 jours. Appliquée à un certificat de 47 jours, elle se déclenche sur un certificat parfaitement sain ; appliquée à un certificat de 6 jours, elle se déclenche en permanence.
Ancrez l'échelle au point de renouvellement. Let's Encrypt recommande de renouveler « environ aux deux tiers de la durée de vie du certificat courant » — donc un tiers de durée de vie restante correspond au moment où le renouvellement aurait déjà dû avoir lieu. Tout ce qui vient après est la preuve qu'il n'a pas eu lieu :
| Durée de vie restante | Ce que ça signifie | Type de notification |
|---|---|---|
| 1/3 | La fenêtre de renouvellement s'est ouverte | Rien — c'est normal |
| 1/6 | La fenêtre a été manquée une fois | Notification normale |
| 1/12 | Le renouvellement échoue, il n'est pas en retard | Urgente |
| < 1/24, expiré ou injoignable | Vous êtes à quelques heures d'une panne | Appel |
En chiffres concrets, pour un certificat de 47 jours : silence jusqu'à 7,8 jours, notification à 3,9 jours, urgente à 2 jours, appel sous 1 jour. Pour un certificat de 90 jours : 15 jours, 7,5 jours, 3,75 jours. Pour un certificat de 6 jours, cela se compte en heures, et une échelle humaine cesse d'être le bon outil — appuyez-vous sur ACME Renewal Information (ARI, publié en RFC 9773), qui laisse l'autorité de certification dire à votre client quand renouveler, et n'alertez que sur des échecs répétés du client.
Calculer la fraction tient en quatre lignes, et rend le même script correct pour tous vos certificats quel que soit l'émetteur :
to_epoch() {
date -u -d "$1" +%s 2>/dev/null || date -u -j -f '%b %d %T %Y %Z' "$1" +%s 2>/dev/null
}
pct_left() { # lit un PEM sur stdin, affiche le pourcentage de durée de vie restante
local pem nb na
pem=$(cat)
nb=$(to_epoch "$(printf '%s' "$pem" | openssl x509 -noout -startdate | cut -d= -f2)")
na=$(to_epoch "$(printf '%s' "$pem" | openssl x509 -noout -enddate | cut -d= -f2)")
echo $(( (na - $(date -u +%s)) * 100 / (na - nb) ))
}
La première forme de date est GNU, la seconde BSD/macOS ; le || choisit celle dont vous disposez.
Comment transformer ça en alerte qui m'atteint vraiment ?
Echobell transforme un webhook ou un e-mail en notification normale, en alerte urgente, ou en véritable appel qui traverse le mode Concentration et Ne pas déranger (voir contourner le mode Concentration d'iOS). Pour les certificats, cela compte parce que le dernier échelon du tableau ci-dessus est celui que vous rencontrerez à 3 h du matin un dimanche.
Étape 1 — Créez deux canaux, pas un
Créez un canal dans l'application, réglez son type de notification sur Urgente et nommez-le « Certificats à expirer ». Créez-en un second en Appel, nommé « Certificat sur le point d'expirer ». Copiez l'URL de webhook de chacun depuis les détails du canal ; elle ressemble à https://hook.echobell.one/t/<channel-token>. Traitez-les comme des secrets : quiconque détient celle de l'appel peut faire sonner votre téléphone (guide des webhooks).
Rédigez les modèles pour que la notification soit exploitable depuis l'écran verrouillé, sans rien déverrouiller :
Titre : TLS {{state}} : {{host}}
Corps : {{daysLeft}} jours restants, expire le {{notAfter}} — émis par {{issuer}}
Toute clé JSON envoyée devient une variable (modèles).
Étape 2 — Lancez la vérification
Voici le script des sections précédentes, assemblé et testé de bout en bout. Enregistrez-le sous /usr/local/bin/cert-watch :
#!/usr/bin/env bash
# cert-watch — envoie un POST à Echobell quand un certificat TLS approche de l'expiration.
set -uo pipefail
HOOK="${ECHOBELL_CERT_HOOK:?définissez ECHOBELL_CERT_HOOK avec l'URL de webhook de votre canal}"
WARN_DAYS="${WARN_DAYS:-14}"
post() {
curl -sS -m 10 -X POST "$HOOK" \
-H 'content-type: application/json' \
-d "{\"host\":\"$1\",\"daysLeft\":$2,\"notAfter\":\"$3\",\"state\":\"$4\"}" \
>/dev/null
}
days_left() {
local end
end=$(date -u -d "$1" +%s 2>/dev/null) ||
end=$(date -u -j -f '%b %d %T %Y %Z' "$1" +%s 2>/dev/null) || return 1
echo $(( (end - $(date -u +%s)) / 86400 ))
}
check() {
local host="$1" port="$2" pem state not_after days
pem=$(openssl s_client -connect "$host:$port" -servername "$host" \
</dev/null 2>/dev/null | openssl x509 2>/dev/null)
if [ -z "$pem" ]; then
post "$host" 0 "" "unreachable"
return
fi
if printf '%s' "$pem" | openssl x509 -noout -checkend 0 >/dev/null 2>&1; then
printf '%s' "$pem" | openssl x509 -noout -checkend $((WARN_DAYS * 86400)) >/dev/null 2>&1 && return 0
state="expiring"
else
state="expired"
fi
not_after=$(printf '%s' "$pem" | openssl x509 -noout -enddate | cut -d= -f2)
days=$(days_left "$not_after") || days=-999
post "$host" "$days" "$not_after" "$state"
}
for target in "$@"; do
case "$target" in
*:*) check "${target%:*}" "${target##*:}" ;;
*) check "$target" 443 ;;
esac
done
Il rapporte trois états — expiring, expired et unreachable — et se tait quand tout va bien. Les cibles s'écrivent host ou host:port, donc les certificats non web sont couverts aussi :
ECHOBELL_CERT_HOOK="https://hook.echobell.one/t/<token>" \
cert-watch example.com api.example.com mail.example.com:993 ldap.internal:636
unreachable est délibérément une alerte plutôt qu'un saut silencieux. Une vérification qui traite « je n'ai pas pu regarder » comme « tout va bien » est précisément la raison pour laquelle les certificats expirent.
Étape 3 — Planifiez-la, et alertez quand la planification elle-même échoue
Une fois par jour suffit pour des certificats de 45 jours et plus ; deux fois par jour si vous utilisez des certificats de 6 jours. Un timer systemd :
# /etc/systemd/system/cert-watch.service
[Unit]
Description=TLS certificate expiry check
[Service]
Type=oneshot
EnvironmentFile=/etc/echobell.env
ExecStart=/usr/local/bin/cert-watch example.com api.example.com mail.example.com:993
# /etc/systemd/system/cert-watch.timer
[Unit]
Description=Daily TLS certificate expiry check
[Timer]
OnCalendar=daily
RandomizedDelaySec=1h
Persistent=true
[Install]
WantedBy=timers.target
Persistent=true compte : sans lui, une machine éteinte à l'heure prévue saute simplement cette exécution.
Puis bouclez la boucle sur la vérification elle-même. Une unité Type=oneshot qui sort avec un code non nul déclenche OnFailure=, donc un seul drop-in fait qu'un renouvellement cassé s'annonce tout seul :
# /etc/systemd/system/certbot.service.d/echobell.conf
[Unit]
OnFailure=echobell-alert@%n.service
# /etc/systemd/system/echobell-alert@.service
[Unit]
Description=Echobell alert for %i
[Service]
Type=oneshot
EnvironmentFile=/etc/echobell.env
ExecStart=/usr/local/bin/echobell-notify %i
Où /usr/local/bin/echobell-notify fait trois lignes :
#!/usr/bin/env bash
curl -sS -m 10 -X POST "$ECHOBELL_CERT_HOOK" \
-H 'content-type: application/json' \
-d "{\"host\":\"$(hostname -f)\",\"state\":\"renewal-failed\",\"unit\":\"$1\",\"daysLeft\":-1}"
certbot renew sort avec un code non nul dès qu'un renouvellement échoue, ce qui est exactement le signal souhaité et exactement celui qui, aujourd'hui, ne va nulle part. Attention : le --deploy-hook de certbot ne s'exécute qu'en cas de succès, il ne peut donc pas servir ici — le chemin d'échec doit venir de l'unité.
Étape 4 — Séparez l'échelle avec des conditions
Les deux canaux reçoivent la même charge utile ; les conditions décident lequel se déclenche. Sur le canal Urgente :
state == "expiring" && daysLeft > 3
Sur le canal Appel :
state == "expired" || state == "unreachable" || state == "renewal-failed" || daysLeft <= 3
Notez que <= convertit les deux côtés avec Number(), donc un daysLeft envoyé sous forme de chaîne se compare quand même numériquement. Les conditions n'ont pas d'opérateur « contient », c'est pourquoi le script envoie un champ state explicite plutôt qu'un texte libre qu'il faudrait reconnaître par motif.
Étape 5 — Testez avant de faire confiance
Pointez le script sur un hôte au certificat volontairement invalide et regardez l'alerte arriver :
ECHOBELL_CERT_HOOK="https://hook.echobell.one/t/<token>" cert-watch expired.badssl.com
Faites-le avec Ne pas déranger activé sur le téléphone qui recevra réellement l'alerte, et activez Rappeler en cas d'échec dans l'application pour qu'un appel bloqué par le mode Concentration soit retenté. Un chemin d'escalade jamais déclenché n'est qu'une hypothèse.
Et si ma supervision vérifie déjà les certificats ?
Alors branchez son webhook existant sur un canal et sautez le script. La plupart des outils de supervision connaissent déjà la date d'expiration ; ce qui leur manque, c'est un chemin qui survit à un humain endormi.
- Uptime Kuma dispose d'une notification d'expiration de certificat intégrée — pointez-la sur un canal (guide Uptime Kuma, configuration des appels).
- Prometheus + Alertmanager avec le blackbox exporter vous donne
probe_ssl_earliest_cert_expiry; alertez dessus et routez vers un canal (guide Prometheus, appels avec Alertmanager). - Les règles d'alerte Grafana postent directement vers un canal (guide Grafana).
- Upptime et UptimeRobot couvrent les endpoints publics (Upptime, UptimeRobot).
- Tout ce qui n'envoie que des e-mails — le portail d'une AC, les avis ACM d'un fournisseur cloud, une PKI interne — passe par une règle de transfert. Chaque canal a sa propre adresse, et
from,to,subject,textethtmlsont disponibles comme variables (déclencheurs e-mail).
La seule chose qu'aucune de ces solutions ne remplace, c'est une vérification exécutée depuis l'extérieur de la machine qui sert le certificat. Si votre supervision vit sur le même hôte, une panne qui fait tomber l'hôte emporte l'alerte avec elle.
Ce qu'Echobell ne fait pas
La précision compte ici, parce que la gestion de certificats est une catégorie pleine de produits qui font bien plus que ça.
Echobell fait : transformer un webhook ou un e-mail en notification normale, alerte urgente ou appel qui sonne ; filtrer avec des conditions ; mettre en forme avec des modèles ; livrer un déclenchement à chaque abonné d'un canal partagé, chacun choisissant son urgence.
Echobell ne fait pas :
- Découvrir ou inventorier vos certificats. Il ne scanne pas votre réseau, n'explore pas les journaux de transparence des certificats et ne vous parlera pas du certificat que personne ne se rappelle avoir émis. Le script ci-dessus ne vérifie que les hôtes que vous listez. La prolifération de certificats est un vrai problème, et ceci n'en est pas la solution.
- Renouveler quoi que ce soit. Pas de client ACME, pas d'accès à vos clés. Il vous dit que le renouvellement a cassé ; le réparer reste votre travail.
- Vérifier les certificats de son propre chef. Il n'y a pas de sonde hébergée. Quelque chose que vous exécutez — un timer, un job de CI, votre supervision existante — doit aller regarder.
- Fournir des astreintes, des politiques d'escalade ou des accusés de réception. Il n'y a pas de « si personne ne répond en cinq minutes, appeler le suivant ». Si vous avez besoin de ça, il vous faut une plateforme d'incidents — voyez le comparatif des alternatives à Opsgenie.
- Garantir la livraison. Un appel dépend de l'infrastructure de push, du réseau et d'un téléphone chargé. Il raccourcit l'écart entre la casse et la prise de conscience ; ce n'est pas un contrôle sur lequel s'appuyer absolument.
FAQ
Let's Encrypt a-t-il vraiment arrêté les e-mails d'expiration ?
Oui. Le service de notification a pris fin le 4 juin 2025, et Let's Encrypt a supprimé les adresses e-mail conservées avec les enregistrements d'émission. L'annonce recommande une supervision tierce et cite Red Sift Certificates Lite, gratuit jusqu'à 250 certificats, comme une option. Si vous n'avez pas reçu d'avertissement d'expiration de Let's Encrypt depuis plus d'un an, c'est la raison — pas parce que rien n'a jamais failli expirer.
Combien de temps un certificat TLS est-il valable en 2026 ?
200 jours maximum pour les certificats TLS publiquement approuvés, depuis le 15 mars 2026. Le plafond tombe à 100 jours le 15 mars 2027 et à 47 jours le 15 mars 2029, selon le scrutin SC-081v3. Chaque AC émet sous le plafond par prudence — DigiCert, par exemple, émet des certificats de 199 jours précisément « pour éviter de dépasser la validité maximale autorisée ». Let's Encrypt va plus loin et plus vite de son côté, avec des certificats de 6 jours disponibles depuis janvier 2026.
Faut-il alerter sur l'expiration si j'utilise ARI ?
Oui, mais sur d'autres événements. ARI (RFC 9773) indique à votre client ACME quand renouveler, ce qui supprime entièrement le problème de l'intervalle codé en dur — mais ne garantit ni que le renouvellement réussisse, ni que le service recharge, ni que le client tourne encore. Alertez sur des échecs répétés de renouvellement et sur une divergence entre le certificat servi et celui sur le disque, pas sur un compte à rebours que vous n'avez plus besoin de gérer.
Et les certificats qui ne sont pas sur un serveur web ?
Ce sont ceux qui expirent le plus, parce qu'aucun client ACME ne les surveille et qu'aucun navigateur ne proteste avant que quelque chose casse. Le script accepte host:port, donc IMAP sur 993, LDAPS sur 636, un broker Kafka sur 9093 ou une API interne sur 8443 fonctionnent pareil. Les certificats qui ne touchent jamais une socket — signature de code, certificats de notifications push, certificats clients d'une flotte d'appareils — nécessitent d'extraire la date là où ils vivent et de la poster sur le même canal.
Une vérification quotidienne ne va-t-elle pas devenir du bruit ?
Pas si elle se tait quand tout va bien, ce qui est exactement pourquoi le script n'envoie rien au-dessus du seuil. La conception bruyante, c'est celle qui annonce « certificat OK » chaque jour : au bout de deux semaines plus personne ne lit, et le jour où ça s'arrête personne ne le remarque. Si vous voulez un battement de cœur, mettez-le sur un canal Normal séparé et jamais sur celui qui sonne. Voir régler la fatigue d'alertes pour la version générale de cet argument.
Toute l'équipe peut-elle recevoir l'alerte de certificat ?
Oui. Partagez le canal et chaque abonné reçoit le déclenchement, en choisissant son propre type de notification. Une répartition qui marche : la personne d'astreinte s'abonne au canal Appel, les autres au canal Urgente, pour qu'une expiration à 3 h du matin réveille une personne et pas six.
Est-ce que ça marche sur macOS ?
Oui, avec une réserve : le date BSD n'accepte pas -d, c'est pourquoi days_left et to_epoch essaient d'abord la forme GNU puis retombent sur date -u -j -f. L'openssl de macOS est LibreSSL par défaut et gère -checkend et -fingerprint de manière identique. Si vous installez OpenSSL via Homebrew, rien ne change.
Est-il prudent d'envoyer des détails du certificat dans une notification ?
Les champs utilisés ici — nom d'hôte, date d'expiration, émetteur — sont publics ; n'importe qui peut les lire sur votre serveur avec la même commande openssl. N'étendez pas la charge utile avec des clés privées, des chemins internes ou quoi que ce soit d'un certificat sur un hôte non public au-delà du nom d'hôte. Echobell ne stocke le contenu et l'historique des notifications que sur votre appareil, ne gardant côté serveur que comptes, canaux et abonnements (modèle de confidentialité) — un bon réglage par défaut, mais pas une raison d'en envoyer plus que nécessaire.