Post

home.arpa : Renouvellement automatique des certificats

home.arpa : Renouvellement automatique des certificats

Le plus simple est de tout centraliser sur la machine qui détient la clé de la CA (votre poste Arch, par exemple). Un timer systemd y lance un script qui réémet le certificat si nécessaire, puis le pousse en SSH vers chaque serveur et recharge le service concerné. Les serveurs n’ont donc besoin ni de timer ni de la clé de la CA, ce qui règle aussi le cas d’Alpine, qui n’a pas systemd.

  • Cibles : le script lit votre CSV et retient les lignes dont Opt contient cert.
  • Utilisateur : le script tourne en timer utilisateur (systemctl --user), car vos clés SSH sont dans votre ~/.ssh. Un timer système en root ne les verrait pas. La clé de la CA doit donc être lisible par votre utilisateur.
  • Droits distants : les serveurs ont un petit script root, appelable via sudo sans mot de passe, qui installe le certificat puis lance des hooks propres à chaque machine (nginx, Proxmox, etc.).

Script sur chaque serveur cible

1 - Script /usr/local/sbin/homearpa-cert-install

Créer le script /usr/local/sbin/homearpa-cert-install (root:root, chmod 755) sur chaque serveur cible

1
2
3
4
5
6
7
8
9
10
#!/bin/sh
set -eu
DIR=/etc/ssl/homearpa
mkdir -p "$DIR"
case "${1:-}" in
  key)    umask 077; cat > "$DIR/homearpa.key.new" && mv "$DIR/homearpa.key.new" "$DIR/homearpa.key" ;;
  cert)   umask 022; cat > "$DIR/homearpa.crt.new" && mv "$DIR/homearpa.crt.new" "$DIR/homearpa.crt" ;;
  reload) for h in /etc/homearpa-cert/reload.d/*; do [ -x "$h" ] && "$h"; done; exit 0 ;;
  *) echo "usage: $0 key|cert|reload" >&2; exit 1 ;;
esac

2 - Autoriser exécution script dans sudoers

Il utilise sh pur, donc il fonctionne aussi sur Alpine. Autorisez-le dans sudoers sans mot de passe avec le nom d’utilisateur de la machine :

1
sudo visudo -f /etc/sudoers.d/homearpa-cert

cwwk

1
yick ALL=(root) NOPASSWD: /usr/local/sbin/homearpa-cert-install

3 - Hook exécutable par le script

Ensuite, déposez dans /etc/homearpa-cert/reload.d/

1
2
# si dossier inexistant
sudo mkdir -p /etc/homearpa-cert/reload.d/

un hook exécutable par service :

cwwk /etc/homearpa-cert/reload.d/10-nginx

1
2
3
# 10-nginx  (Debian/Arch)
#!/bin/sh
systemctl reload nginx

proxmox /etc/homearpa-cert/reload.d/10-pve

1
2
3
# 10-pve  (Proxmox : méthode officielle)
#!/bin/sh
pvenode cert set /etc/ssl/homearpa/homearpa.crt /etc/ssl/homearpa/homearpa.key --force 1 --restart 1

Alpine /etc/homearpa-cert/reload.d/10-alpine

1
2
3
# 10-nginx  (Alpine)
#!/bin/sh
rc-service nginx reload

Si le service qui lit la clé ne tourne pas en root, ajustez le groupe ou les droits dans le hook.

Poste PC1 EndeavourOS

Script renouvellement certificats home.arpa

Les informations homearpaCA.crt + homearpaCA.key sont dans un coffre

Solution recommandée : un fichier dans le coffre Cryptomator La passphrase est stockée à côté de la clé de la CA, donc chiffrée au repos par le coffre, qui est inaccessible tant qu’il est verrouillé.

1
2
3
4
5
6
7
8
CA_DIR="$HOME/.local/share/Cryptomator/mnt/yann-crypto/home.arpa"

# Création du fichier sans laisser la passphrase dans l'historique
read -rsp "Passphrase de la CA : " p; echo
( umask 077; printf '%s\n' "$p" > "$CA_DIR/ca.pass" ); unset p

# Test : la clé se lit sans interaction ?
openssl pkey -in "$CA_DIR/homearpaCA.key" -passin file:"$CA_DIR/ca.pass" -noout && echo "OK"

Créer le script ~/.local/bin/homearpa-cert-renew (chmod 700) :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#!/usr/bin/env bash
set -euo pipefail

CSV="${CSV:-$HOME/.ssh/.ssh_servers}"   # À ADAPTER : votre fichier CSV
KEYDIR="$HOME/.ssh"                               # dossier des clés (champ SSH_Key)
# Les informations homearpaCA.crt + homearpaCA.key sont dans un coffre verrouillé en fermeture de session
CA_DIR="$HOME/.local/share/Cryptomator/mnt/yann-crypto/home.arpa/"           # homearpaCA.crt + homearpaCA.key (chmod 600)
OUT="$HOME/.local/share/homearpa-certs"
NAME=homelab
DOMAIN=home.arpa
DAYS=90
RENEW_BEFORE=$((30*86400))
HELPER=/usr/local/sbin/homearpa-cert-install
# Lecture passphrase 
CA_PASS="$CA_DIR/ca.pass"

for f in homearpaCA.crt homearpaCA.key ca.pass; do
  [[ -r "$CA_DIR/$f" ]] || { echo "Introuvable : $CA_DIR/$f (coffre Cryptomator verrouillé ?)" >&2; exit 1; }
done

# SAN_EXTRA="${SAN_EXTRA:-}"                        # ex. "DNS:alias.home.arpa,DNS:autre.home.arpa"
SAN_EXTRA="DNS:*.home.arpa, DNS:home.arpa, DNS:cwwk.home.arpa, DNS:dns.home.arpa, DNS:ged.home.arpa, DNS:link.home.arpa, DNS:lldap.home.arpa, DNS:portainer.home.arpa, DNS:pve.home.arpa, DNS:site.home.arpa, DNS:terminal.home.arpa"
mkdir -p "$OUT"; cd "$OUT"; umask 077

# Cibles : Opt (champ 9) contient "cert" → alias|user|host|port|key|ipver
mapfile -t TARGETS < <(awk -F';' 'NR>1 && $1!="" {
  n=split($9,o,","); for(i=1;i<=n;i++) if(o[i]=="cert") print $1"|"$2"|"$3"|"$4"|"$5"|"$10 }' "$CSV")
[[ ${#TARGETS[@]} -gt 0 ]] || { echo "Aucune ligne avec l'option cert" >&2; exit 1; }

# SAN : alias.home.arpa (ou l'hôte s'il finit déjà par .home.arpa) + IP si le champ est une IPv4
sans=()
for t in "${TARGETS[@]}"; do
  IFS='|' read -r alias user host port key ver <<<"$t"
  if [[ "$host" == *.$DOMAIN ]]; then sans+=("DNS:$host"); else sans+=("DNS:$alias.$DOMAIN"); fi
  if [[ "$host" =~ ^[0-9]+(\.[0-9]+){3}$ ]]; then sans+=("IP:$host"); fi
done
SAN=$(IFS=,; echo "${sans[*]}")
[[ -n "$SAN_EXTRA" ]] && SAN="$SAN,$SAN_EXTRA"

norm() { sed 's/IP Address:/IP:/g' | tr -d ' ' | tr ',' '\n' | sort | tr '\n' ','; }
WANT=$(echo "$SAN" | norm)
CUR=$(openssl x509 -in $NAME.crt -noout -ext subjectAltName 2>/dev/null | tail -n1 | norm || true)

# 1. Réémission si les noms ont changé ou s'il reste moins de 30 jours
if [[ "$CUR" != "$WANT" ]] || ! openssl x509 -in $NAME.crt -noout -checkend $RENEW_BEFORE >/dev/null 2>&1; then
  echo "Réémission ($DAYS jours) : $SAN"
  cat > $NAME.ext <<EOF
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = $SAN
EOF
  openssl req -new -newkey rsa:2048 -nodes -keyout $NAME.key -out $NAME.csr \
    -subj "/C=FR/ST=Pays-de-la-Loire/L=Beaupreau/O=HomeLab/CN=$DOMAIN"
  openssl x509 -req -in $NAME.csr -CA "$CA_DIR/homearpaCA.crt" -CAkey "$CA_DIR/homearpaCA.key" \
    -passin file:"$CA_PASS" -CAcreateserial -days $DAYS -sha256 -extfile $NAME.ext -out $NAME.crt
  openssl verify -CAfile "$CA_DIR/homearpaCA.crt" $NAME.crt
fi

# 2. Déploiement idempotent (n'agit que sur les serveurs pas à jour)
rc=0
for t in "${TARGETS[@]}"; do
  IFS='|' read -r alias user host port key ver <<<"$t"
  opts=(-i "$KEYDIR/$key" -p "$port" -o IdentitiesOnly=yes -o BatchMode=yes -o ConnectTimeout=10)
  if [[ "$ver" == 4 || "$ver" == 6 ]]; then opts+=("-$ver"); fi
  S=(ssh "${opts[@]}" "$user@$host")

  if "${S[@]}" "cat /etc/ssl/homearpa/homearpa.crt" 2>/dev/null | cmp -s - $NAME.crt; then continue; fi
  echo "Déploiement sur $alias"
  if "${S[@]}" "sudo -n $HELPER key"    < $NAME.key \
  && "${S[@]}" "sudo -n $HELPER cert"   < $NAME.crt \
  && "${S[@]}" "sudo -n $HELPER reload"; then echo "  OK"; else echo "  ÉCHEC sur $alias" >&2; rc=1; fi
done
exit $rc

Ce script suppose deux choses à vérifier :

  • Nom DNS : l’alias correspond au nom Technitium (cwwk donne cwwk.home.arpa, ce que vous avez validé). Si proxtin ou proxmox ont un autre nom en DNS, utilisez SAN_EXTRA ou renommez l’enregistrement.
  • Clés SSH : le champ SSH_Key désigne un fichier dans ~/.ssh. Si votre script sshm les cherche ailleurs, changez KEYDIR.

La ligne proxy (rnmkcy.eu, sans option cert) est ignorée, ce qui est normal : un nom public ne relève pas de votre CA locale.

Automatiser le renouvellement (systemd timer utilisateur)

Créer le service utilisateur~/.config/systemd/user/homearpa-cert.service :

1
2
3
4
5
6
[Unit]
Description=Renouvellement et déploiement du certificat home.arpa

[Service]
Type=oneshot
ExecStart=%h/.local/bin/homearpa-cert-renew

Créer le timer utilisateur ~/.config/systemd/user/homearpa-cert.timer :

1
2
3
4
5
6
7
8
9
10
[Unit]
Description=Vérification hebdomadaire du certificat home.arpa

[Timer]
OnCalendar=weekly
RandomizedDelaySec=1h
Persistent=true

[Install]
WantedBy=timers.target

Rechargement, activation, exécution et vérification

1
2
3
4
systemctl --user daemon-reload
systemctl --user enable --now homearpa-cert.timer
systemctl --user start homearpa-cert.service      # premier passage manuel
journalctl --user -u homearpa-cert.service -e

Le journal

1
2
oct. 22 15:08:35 pc1 systemd[819]: Starting Renouvellement et déploiement du certificat home.arpa...
oct. 22 15:08:36 pc1 systemd[819]: Finished Renouvellement et déploiement du certificat home.arpa.

Persistent=true rattrape un passage manqué si le poste était éteint.

Ordre de mise en route

  1. Installer le script root, la règle sudoers et les hooks sur les serveurs marqués cert (e6230l n’en a pas besoin pour l’instant).
  2. Copier la CA dans $CA_DIR (chmod 600 sur la clé).
  3. Lancer le service à la main et lire le journal.
  4. Recharger https://cwwk.home.arpa dans Brave.

La clé de la CA est stockée sans passphrase pour permettre l’automatisation. Gardez donc des droits stricts sur ce dossier et ne le synchronisez pas.

Conclusions

En résumé :

  • homearpaCA.crt : la racine, à installer dans les magasins de confiance. C’est déjà fait, vous n’y touchez plus.
  • homearpaCA.key : la clé de la CA, qui reste dans le coffre Cryptomator.
  • homelab.crt et homelab.key : le certificat serveur renouvelé tous les 90 jours et déployé par le script sur les machines marquées cert.

Parfait, Brave accepte maintenant le domaine, ce qui confirme que le certificat avec noms explicites est bien déployé.

Le journal ne contient que « Starting » et « Finished », sans aucune ligne « Réémission » ni « Déploiement ». Ce passage n’a donc rien eu à faire : le certificat était déjà à jour (mêmes noms, plus de 30 jours de validité) et chaque serveur avait déjà la bonne version. C’est le comportement attendu d’un script idempotent, probablement parce que vous aviez déjà fait un premier passage à la main. Une confirmation rapide :

1
2
3
4
5
# Validité et noms du certificat courant
openssl x509 -in ~/.local/share/homearpa-certs/homelab.crt -noout -dates -ext subjectAltName

# Prochain passage du timer
systemctl --user list-timers homearpa-cert.timer

Vous devriez voir environ 90 jours de validité et un DNS: pour chaque machine marquée cert. Pour contrôler un serveur précis, openssl s_client -connect cwwk.home.arpa:443 -servername cwwk.home.arpa </dev/null 2>/dev/null | openssl x509 -noout -dates doit afficher les mêmes dates.

Le renouvellement se déclenchera tout seul quand il restera moins de 30 jours, soit autour de fin décembre. Un test utile avant cette date : ajouter l’option cert à une nouvelle ligne du CSV, relancer le service avec systemctl --user start homearpa-cert.service, et vérifier que le journal affiche « Réémission » puis « Déploiement sur … ».

Cet article est sous licence CC BY 4.0 par l'auteur.