Il y avait deux dépôts d'infrastructure et aucune frontière entre eux. La seule distinction défendable — « documentation » d'un côté, « fichiers de configuration » de l'autre — ne tenait plus : infra avait fini par contenir plus de documentation qu'owni, pendant qu'owni décrivait du concret (IP, bases, comptes). Tout vient donc ici : conf/ (vhosts de référence), dns/ (export de zone), services/ (units systemd), scripts/ (sauvegarde) et docs/ (courrier sortant, certificats, sauvegardes, et le déploiement par service). Dans ce sens plutôt que l'inverse parce qu'« owni » nomme la machine, là où « infra » ne dit pas de quoi il s'agit — et c'est le nom que Cédric emploie spontanément, y compris pour le dossier de secrets. Les renvois croisés entre les deux dépôts deviennent des liens internes : un lien vers un dépôt qu'on s'apprête à archiver aurait pourri en silence. Reste une incohérence, signalée plutôt que corrigée à la hâte : docs/admin.md, static.md, wiki.md, dynamic.md et proxy-calendar.md décrivent le déploiement de sites, pas la machine. Ils ont la même place ici que celle que la messagerie n'avait pas — ils devraient rejoindre leurs dépôts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SFbwnJurBwTs7x7t93ecku
7.4 KiB
Certificats TLS sur owni
État relevé le 27/09/2026. Deux mécanismes coexistent, et ce n'est pas un désordre : c'est une histoire. Ce document dit lequel gère quoi, où chercher quand un site passe en « connexion non sécurisée », et ce qui a déjà cassé.
Qui gère quoi
certbot — un certificat multi-domaines pour huit sites
/etc/letsencrypt/live/alpid.alpinux.org/ porte un seul certificat couvrant
dix noms :
alpid.alpinux.org dolibarr.alpinux.org owni.alpinux.org
alpinux.org gitea.alpinux.org wiki.alpinux.org
autoconfig.alpinux.org installparty.alpinux.org www.alpinux.org
cloud.alpinux.org
Huit vhosts le servent directement. Renouvellement : certbot renew chaque
nuit à 3 h 00, par cronwrap (donc visible sur admin.alpinux.org).
Dix noms sur les cent autorisés : le nombre n'est pas un problème, le couplage l'est. Un seul domaine dont le DNS casse ou dont la validation échoue empêche le renouvellement des neuf autres. Et un nom couvert par un SAN devient un cas particulier que les outils traitent mal — voir l'incident
cloudplus bas.
ISPConfig et acme.sh — un certificat par site
ISPConfig émet par acme.sh (/root/.acme.sh/<domaine>_ecc/) et dépose une
copie dans /var/www/clients/<client>/<web>/ssl/<domaine>-le.crt. C'est cette
copie que le vhost lit, jamais l'original.
Concernés : admin, messagerie, portail, static.
Le piège, et il a déjà mordu deux fois
ISPConfig ne rafraîchit sa copie que lorsqu'il crée le certificat. Un renouvellement par certbot passe inaperçu : le vhost continue de servir l'ancien fichier jusqu'à son expiration, puis le site tombe. Re-sauvegarder le site dans l'interface ne suffit pas à déclencher la copie.
| Quand | Quoi | Vu le |
|---|---|---|
| 1er août 2026 | static et portail perdent leur HTTPS |
5 septembre |
| 4 août 2026 | cloud perd son HTTPS |
27 septembre |
Cinq semaines, puis sept. C'est le délai qu'il faut pour que quelqu'un s'aperçoive qu'un site ne répond plus.
Le dispositif qui l'évite aujourd'hui
- Le hook
/etc/letsencrypt/renewal-hooks/deploy/ispconfig-cert.shs'exécute après chaque renouvellement certbot. Il itère sur$RENEWED_DOMAINS— tous les noms du certificat, SAN compris —, cherche*/ssl/${domain}-le.crtsous/var/www/clientset le met à jour. C'est le cas nominal. - Le filet
/usr/local/sbin/owni-certs-sync.sh, chaque nuit à 4 h 20, rattrape ce que le hook n'a pas traité : hook absent lors d'un renouvellement passé, copie écrasée par ISPConfig, certificat posé à la main. Mode audit par défaut,--fixpour corriger, alerte à 21 jours. - La surveillance : depuis le 27/09/2026 ce script passe par
cronwrap, donc son état remonte suradmin.alpinux.org. Auparavant il alertait parMAILTO=root, c'est-à-dire nulle part — l'expiration declouda été signalée chaque nuit pendant 54 jours sans que personne la voie.
Retenir de cette histoire : une alerte qui n'atteint personne n'est pas une alerte. Le script fonctionnait parfaitement ; c'est sa sortie qui se perdait.
Diagnostiquer
La seule méthode qui ne dépend pas du mécanisme
Ce que voit un visiteur, quel que soit l'outil qui a émis le certificat :
for d in messagerie.alpinux.org alpinux.org wiki.alpinux.org \
gitea.alpinux.org cloud.alpinux.org; do
fin=$(echo | openssl s_client -servername $d -connect $d:443 2>/dev/null \
| openssl x509 -noout -enddate | cut -d= -f2)
echo "$d : $(( ($(date -d "$fin" +%s) - $(date +%s)) / 86400 )) jours"
done
Un echo | openssl s_client qui ne rend rien = TLS cassé, donc site
inaccessible en HTTPS.
L'audit complet, sur le serveur
sudo /usr/local/sbin/owni-certs-sync.sh # audit, n'écrit rien
sudo /usr/local/sbin/owni-certs-sync.sh --fix # corrige ce qu'il peut
Il rend 0 si tout est cohérent, 1 s'il subsiste une anomalie. Sa sortie
distingue :
ok— à jour ;ORPHELIN(majuscules) — expiré, c'est une panne ;orphelin(minuscules) — inconnu de certbot mais valide, simple avertissement ;inutilisé— un certificat existe et aucun service ne le lit.
Quel certificat un vhost lit-il vraiment
for f in /etc/apache2/sites-enabled/*.vhost; do
cert=$(sudo awk '/SSLCertificateFile/ {print $2; exit}' "$f")
[ -n "$cert" ] && printf "%-34s %s\n" "$(basename "$f")" "$cert"
done
Ce que couvre un certificat multi-domaines
sudo openssl x509 -noout -text -in /etc/letsencrypt/live/alpid.alpinux.org/fullchain.pem \
| grep -A1 "Subject Alternative Name"
Réparer un site dont le certificat a expiré
Cas rencontré le 27/09/2026 avec cloud.alpinux.org.
1. Établir ce qui manque vraiment. Le nom est-il couvert par un certificat valide existant ? S'il est dans le SAN, il n'y a rien à émettre — seulement à recopier.
2. Recopier le certificat valide à l'emplacement que le vhost attend, en gardant l'ancien plutôt qu'en l'écrasant :
D=/var/www/clients/client1/web19/ssl
L=/etc/letsencrypt/live/alpid.alpinux.org
sudo cp -a $D/cloud.alpinux.org-le.crt $D/cloud.alpinux.org-le.crt.expire-AAAAMMJJ
sudo cp -a $D/cloud.alpinux.org-le.key $D/cloud.alpinux.org-le.key.expire-AAAAMMJJ
sudo cp $L/fullchain.pem $D/cloud.alpinux.org-le.crt
sudo cp $L/privkey.pem $D/cloud.alpinux.org-le.key
sudo chmod 644 $D/cloud.alpinux.org-le.crt
sudo chmod 600 $D/cloud.alpinux.org-le.key
sudo apache2ctl configtest && sudo systemctl reload apache2
3. Vérifier de l'extérieur, pas seulement que la commande a rendu la main.
Une fois le fichier en place, le hook le maintiendra : il cherche
*/ssl/${domain}-le.crt et ne peut mettre à jour que ce qui existe. C'est
pourquoi cette réparation n'est pas un pansement.
Alternative propre, si le domaine mérite son propre certificat : le réémettre depuis ISPConfig (Sites → le site → SSL → Let's Encrypt, décocher, enregistrer, recocher, enregistrer).
Ce qui reste en suspens
Neuf certificats ISPConfig ne servent à rien. Chaque site couvert par le SAN possède en plus un certificat à son nom, généré et jamais lu :
inutilisé wiki.alpinux.org, gitea.alpinux.org, alpid.alpinux.org,
dolibarr.alpinux.org, installparty.alpinux.org,
autoconfig.alpinux.org, alpinux.org, owni.alpinux.org, cloud…
Rien n'est cassé de ce fait, et les supprimer demanderait de vérifier un par un qu'aucun service — Postfix, Dovecot, pure-ftpd — ne les lit. Le script les signale précisément pour éviter qu'on cesse de mettre à jour un fichier qui sert ailleurs.
owni-certs-sync.sh raisonne par nom de dossier, pas par SAN. Il cherche
/etc/letsencrypt/live/<domaine>/ et classe « orphelin » tout nom qui n'en a
pas — même couvert par le SAN d'un autre. C'est pourquoi il a su signaler
l'expiration de cloud sans pouvoir la réparer. Lui apprendre à lire les
SAN le rendrait autonome sur ce cas.
Le script n'est pas versionné
/usr/local/sbin/owni-certs-sync.sh et le hook n'existent que sur la
machine. Choix assumé : ce sont des rustines autour d'un comportement
d'ISPConfig, et les versionner reviendrait à les pérenniser. Mais cela veut
dire qu'un serveur reconstruit les perdra, avec la connaissance qu'ils
portent — d'où ce document, qui doit suffire à les réécrire.