Compare commits

...
Author SHA1 Message Date
gandalf 032611e544 merge: reunir cert sync (#1019) et les delais de televersement (#1030)
DEUX CORRECTIONS SUR DEUX BRANCHES, ET L UNE A EFFACE L AUTRE. Un paquet
secubox-haproxy construit depuis master a ete deploye sur la board par-dessus
un paquet issu de la branche 1019 : le verbe `cert sync` a disparu, et avec
lui le hook de renouvellement de certbot. Les certificats servis restaient
valables — c est bien ce qui rend la panne invisible — mais aucun renouvellement
n aurait plus ete recopie vers HAProxy.

Les deux entrees de changelog sont conservees : elles decrivent deux
corrections distinctes, toutes les deux vraies.
2026-08-13 19:00:55 +02:00
gandalf 8630e808a3 fix(haproxy,waf-ng): delais adaptes aux televersements du depot (ref #1030)
Un depot de 22 Mio mettait 116 s a s ecrire. HAProxy abandonnait a 30 s, sbxwaf
a 10 s — et le serveur, lui, TERMINAIT le travail. Chaque essai deposait a
nouveau.

LE DELAI EST RELEVE PAR REQUETE, PAS DANS `defaults`. La section `defaults`
protege TOUS les vhosts d un amont qui trainerait ; la relever aurait retire
cette protection a la centaine de vhosts qui n en ont pas besoin — payer pour
tous ce dont un seul a besoin.

DEUX LECONS PAYEES A L ESSAI. `http-request set-timeout` ne vit QUE dans un
backend — HAProxy refuse « proxy has no backend capability » cote frontend — et
il ne connait que `server` et `tunnel` : `set-timeout client` N EXISTE PAS. Les
deux refus ont ete rattrapes par la validation qui precede l installation : la
configuration vivante n a jamais bouge. Ce garde-fou vaut sa ligne.

`timeout client` reste a 30 s, et c est correct : c est un delai d INACTIVITE,
qu un televersement regulier n atteint jamais.

Cote WAF, `--upstream-timeout` passe de 10 s a 120 s : dix secondes sont trop
courtes pour tout point d entree qui ecrit sur disque.
2026-08-13 17:21:02 +02:00
gandalf 7c6b3969ab fix(haproxy): HAProxy servait des certificats que le renouvellement ne mettait jamais a jour (ref #1019)
certbot renouvelle dans /etc/letsencrypt/live ; HAProxy sert un fichier
CONCATENE dans /data/haproxy/certs. Rien ne recopiait l'un vers l'autre :
aucun hook n'existait, ni par certificat ni dans les repertoires globaux, tous
deux vides. Le verbe cert renew existant est ecrit pour acme.sh, absent de
cette board — du code mort ici.

Le defaut etait SILENCIEUX : le renouvellement reussit, les journaux sont
propres, le site repond, jusqu'au jour ou la copie servie expire des semaines
plus tard. Trois certificats sur six divergeaient sur gk2.

cert sync reconstruit les .pem depuis /etc/letsencrypt/live et compare le
CONTENU, pas les dates de fichier : un mtime recent ne prouve pas qu'un contenu
a change, et une copie touchee a la main aurait un mtime recent tout en servant
un certificat perime. Ecriture par renommage, sans quoi un .pem tronque ferait
echouer le rechargement pour TOUS les vhosts. Rechargement seulement si quelque
chose a change.

UNE GARDE AJOUTEE APRES L'ESSAI A BLANC, qui a montre le piege : deux
certificats servis expiraient PLUS TARD que la copie de letsencrypt. Le premier
jet les aurait ecrases et aurait RACCOURCI leur duree de vie. Un .pem servi peut
venir d'ailleurs qu'ACME ; la source n'est pas autoritaire par nature, seule la
date d'expiration l'est. Avec la garde, 3 mises a jour deviennent 1.

Un hook de deploiement certbot appelle cert sync apres chaque renouvellement
reussi, livre par le paquet — sinon il disparaitrait a la reinstallation.
2026-08-13 07:12:25 +02:00
6 changed files with 234 additions and 0 deletions
+70
View File
@@ -1,3 +1,73 @@
secubox-haproxy (1.8.0-1~bookworm1) bookworm; urgency=high
* Cette version REUNIT deux corrections qui vivaient sur des branches
separees, et dont l'une avait efface l'autre sur la board : un paquet
construit depuis master a fait perdre le verbe `cert sync` de la 1.6.4,
jamais fusionne. Le renouvellement automatique des certificats etait donc
casse en silence — les certificats servis restaient valables, mais rien ne
recopiait plus les renouvellements vers HAProxy.
secubox-haproxy (1.7.1-1~bookworm1) bookworm; urgency=high
* HAProxy servait des certificats que le renouvellement ne mettait jamais a
jour (ref #1019). certbot renouvelle dans /etc/letsencrypt/live ; HAProxy
sert un fichier CONCATENE dans /data/haproxy/certs. Rien ne recopiait l'un
vers l'autre : aucun hook n'existait, ni par certificat ni dans les
repertoires globaux, tous deux vides.
* Le defaut etait SILENCIEUX. Le renouvellement reussit, les journaux de
certbot sont propres, le site repond — jusqu'au jour ou la copie servie
expire, des semaines plus tard, et le lien de cause a effet est alors
indechiffrable.
* Constate sur gk2 : trois certificats sur six divergeaient, dont un qui
serait tombe le 14 octobre alors qu'une version valide jusqu'au 2 novembre
dormait deja sur le disque, inutilisee.
* Nouveau `haproxyctl cert sync` : reconstruit les .pem depuis
/etc/letsencrypt/live, compare le CONTENU et non les dates — un mtime plus
recent ne prouve pas qu'un fichier a change, et une copie touchee a la main
aurait un mtime recent tout en servant un certificat perime. Ecriture par
renommage, sans quoi un .pem tronque ferait echouer le rechargement pour
TOUS les vhosts. Ne recharge que si quelque chose a change.
* Un hook de deploiement certbot l'appelle apres chaque renouvellement reussi.
Livre par le paquet, pas pose a la main — sans quoi il disparaitrait a la
prochaine reinstallation, comme le reste.
* `cert sync` NE RACCOURCIT JAMAIS la vie d'un certificat servi. Le premier
jet ecrasait des que le contenu differait ; l'essai a blanc sur gk2 a montre
le piege : deux certificats servis expiraient PLUS TARD que la copie de
/etc/letsencrypt (live.maegia.tv, 4 novembre contre 14 octobre). Les
synchroniser aurait raccourci leur duree de vie — l'inverse du but. Un .pem
servi peut venir d'ailleurs qu'ACME (joker, autre outil, depot manuel) : la
source n'est pas autoritaire par nature, seule la date d'expiration l'est.
* `cert renew` reste ecrit pour acme.sh, absent de cette board : c'est du
code mort ici, laisse tel quel plutot que retire a l'aveugle.
-- Gerald Kerma <devel@cybermind.fr> Thu, 13 Aug 2026 10:00:00 +0200
secubox-haproxy (1.7.2-1~bookworm1) bookworm; urgency=medium
* DELAI RELEVE PAR REQUETE POUR LES TELEVERSEMENTS DU DEPOT (ref #1030). La
section `defaults` porte `timeout client/server 30s`, et c'est bien : elle
protege TOUS les vhosts d'un amont qui trainerait. Mais un depot de 22 Mio
a mis 116 s a s'ecrire — HAProxy abandonnait a 30, le deposant voyait un
504, ET LE SERVEUR TERMINAIT LE TRAVAIL. Chaque essai deposait a nouveau.
* `http-request set-timeout` (HAProxy >= 2.4, la board est en 2.6) releve le
delai POUR CES REQUETES SEULEMENT. Relever `defaults` aurait couvert le cas
en retirant la protection a la centaine de vhosts qui n'en ont pas besoin —
payer pour tous ce dont un seul a besoin.
* DEUX LECONS PAYEES A L'ESSAI. `http-request set-timeout` ne vit QUE dans un
backend — HAProxy refuse « proxy has no backend capability » cote frontend —
et il ne connait que `server` et `tunnel` : `set-timeout client` N'EXISTE
PAS. Les deux refus ont ete rattrapes par la validation qui precede
l'installation : la configuration vivante n'a jamais bouge. Ce garde-fou
vaut sa ligne.
* `timeout client` reste a 30 s, et c'est correct : c'est un delai
d'INACTIVITE, qu'un televersement regulier n'atteint jamais.
* L'ACL porte sur le CHEMIN, pas sur l'hote : deux `acl` de meme nom sont un
OU chez HAProxy, pas un ET. Nommer l'hote ET le chemin aurait releve le
delai pour tout hote contenant « depot », ou pour ce chemin sur n'importe
quel hote — l'inverse de ce qu'on cherche.
-- Gerald Kerma <devel@cybermind.fr> Thu, 13 Aug 2026 17:45:00 +0200
secubox-haproxy (1.6.3-1~bookworm1) bookworm; urgency=medium
* `vhost add` est idempotent (ref #1015). Il appendait sans jamais regarder
+6
View File
@@ -10,6 +10,12 @@
override_dh_usrlocal:
override_dh_auto_install:
# Hook de deploiement certbot (#1019). Sans lui, HAProxy sert des copies que
# le renouvellement ne met jamais a jour : trois certificats sur six
# divergeaient sur gk2, dont un qui serait tombe alors qu'une version valide
# dormait deja sur le disque.
install -d debian/secubox-haproxy/etc/letsencrypt/renewal-hooks/deploy
install -m 0755 letsencrypt-hooks/secubox-haproxy debian/secubox-haproxy/etc/letsencrypt/renewal-hooks/deploy/secubox-haproxy
install -d debian/secubox-haproxy/usr/lib/secubox/haproxy/
cp -r api debian/secubox-haproxy/usr/lib/secubox/haproxy/
install -d debian/secubox-haproxy/usr/share/secubox/www
+19
View File
@@ -0,0 +1,19 @@
#!/bin/sh
# /etc/letsencrypt/renewal-hooks/deploy/secubox-haproxy — #1019
#
# certbot renouvelle dans /etc/letsencrypt/live ; HAProxy sert un fichier
# CONCATENE (chaine + cle) dans /data/haproxy/certs. Rien ne recopiait l'un vers
# l'autre : trois certificats sur six divergeaient sur gk2, dont un qui serait
# tombe le 14 octobre alors qu'une version valide jusqu'au 2 novembre dormait
# deja sur le disque.
#
# Le defaut etait SILENCIEUX : le renouvellement reussit, les journaux de
# certbot sont propres, le site repond — jusqu'au jour ou la copie servie
# expire, des semaines plus tard. Le lien de cause a effet est alors
# indechiffrable.
#
# Ce hook est un DEPLOY hook : certbot ne l'appelle qu'apres un renouvellement
# reussi, jamais sur un simple controle. `cert sync` ne recharge HAProxy que si
# un fichier a change, donc ce hook ne coupe aucune connexion pour rien.
set -eu
exec /usr/sbin/haproxyctl cert sync
+114
View File
@@ -727,6 +727,99 @@ cmd_cert_renew() {
fi
}
cmd_cert_sync() {
# Reconstruit les .pem servis par HAProxy depuis /etc/letsencrypt/live (#1019).
#
# POURQUOI CE VERBE EXISTE. certbot renouvelle dans /etc/letsencrypt/live ;
# HAProxy sert un fichier CONCATENE (chaine + cle) dans $CERTS_DIR. Rien ne
# recopiait l'un vers l'autre : aucun hook de renouvellement n'existait, et
# `cert renew` ci-dessus est ecrit pour acme.sh, absent de cette board.
#
# Le defaut est SILENCIEUX. Le renouvellement reussit, les journaux sont
# propres, le site repond — jusqu'au jour ou la copie servie expire, des
# semaines plus tard. Le lien de cause a effet est alors indechiffrable.
#
# Constate sur gk2 : trois certificats sur six divergeaient, dont un qui
# serait tombe le 14 octobre alors qu'une version valide jusqu'au 2 novembre
# dormait deja sur le disque.
local ecrire=1
[ "${1:-}" = "--dry-run" ] && ecrire=0
local live_root="/etc/letsencrypt/live"
[ -d "$live_root" ] || { log "cert-sync: pas de certbot sur cette board"; return 0; }
mkdir -p "$CERTS_DIR"
local change=0 vus=0
for d in "$live_root"/*/; do
[ -d "$d" ] || continue
local nom chaine cle cible
nom=$(basename "$d")
chaine="$d/fullchain.pem"
cle="$d/privkey.pem"
[ -f "$chaine" ] && [ -f "$cle" ] || continue
vus=$((vus + 1))
cible="$CERTS_DIR/$nom.pem"
# ON COMPARE LE CONTENU, PAS LES DATES. Un mtime plus recent ne prouve
# pas que le contenu a change — et une copie touchee a la main aurait un
# mtime plus recent tout en servant un certificat perime.
local somme_src somme_dst
somme_src=$(cat "$chaine" "$cle" | sha256sum | cut -d' ' -f1)
somme_dst=$([ -f "$cible" ] && sha256sum "$cible" | cut -d' ' -f1 || echo "")
[ "$somme_src" = "$somme_dst" ] && continue
local fin_src fin_dst
fin_src=$(openssl x509 -in "$chaine" -noout -enddate 2>/dev/null | cut -d= -f2)
fin_dst=$([ -f "$cible" ] && openssl x509 -in "$cible" -noout -enddate 2>/dev/null | cut -d= -f2 || echo "")
# ON NE RACCOURCIT JAMAIS LA VIE D'UN CERTIFICAT SERVI.
#
# Le premier jet ecrasait des que le contenu differait. L'essai a blanc
# sur gk2 a montre le piege : deux certificats servis expiraient PLUS
# TARD que la copie de /etc/letsencrypt (live.maegia.tv, 4 novembre
# contre 14 octobre). Les synchroniser aurait raccourci leur duree de
# vie — l'inverse exact du but.
#
# Un .pem servi peut venir d'ailleurs qu'ACME : joker, autre outil,
# depot manuel. La source n'est donc pas autoritaire par nature ; seule
# la date d'expiration l'est.
if [ -n "$fin_dst" ]; then
local ts_src ts_dst
ts_src=$(date -d "$fin_src" +%s 2>/dev/null || echo 0)
ts_dst=$(date -d "$fin_dst" +%s 2>/dev/null || echo 0)
if [ "$ts_src" -le "$ts_dst" ]; then
log "cert-sync: $nom — IGNORE, le certificat servi expire plus tard ($fin_dst) que la source ($fin_src)"
continue
fi
fi
change=$((change + 1))
log "cert-sync: $nom — servi jusqu'au ${fin_dst:-absent}, remplace par $fin_src"
[ "$ecrire" = "1" ] || continue
# Ecriture par fichier temporaire puis renommage : HAProxy peut relire le
# repertoire a tout instant, et un .pem tronque le ferait echouer au
# rechargement — donc tomber pour TOUS les vhosts, pas seulement celui-ci.
local tmp="$cible.nouveau"
cat "$chaine" "$cle" > "$tmp" || { error "cert-sync: ecriture $nom impossible"; rm -f "$tmp"; continue; }
chmod 0600 "$tmp"
mv "$tmp" "$cible"
done
if [ "$ecrire" = "0" ]; then
log "cert-sync: $vus certificat(s), $change a mettre a jour (lecture seule)"
return 0
fi
log "cert-sync: $vus certificat(s), $change mis a jour"
# ON NE RECHARGE QUE SI QUELQUE CHOSE A CHANGE. Recharger a chaque passage
# coupe les connexions en cours sans raison, et ce verbe est appele apres
# CHAQUE renouvellement — donc souvent, pour rien la plupart du temps.
[ "$change" -gt 0 ] && cmd_reload
return 0
}
# ─────────────────────────────────────────────────────────────────────
# CONFIG GENERATION
# ─────────────────────────────────────────────────────────────────────
@@ -806,6 +899,7 @@ frontend http-in
# garde anti-dérive refusait de régénérer — elle protégeait exactement ça.
acl is_acme_challenge path_beg /.well-known/acme-challenge/
use_backend acme_challenge if is_acme_challenge
EOF
if webui_regex=$(_fetch_webui_regex); then
@@ -935,6 +1029,25 @@ EOF
# WAF Inspector Backend (mitmproxy LXC at $waf_ip:$waf_port)
backend mitmproxy_inspector
mode http
# TELEVERSEMENTS LONGS : DELAI SERVEUR RELEVE PAR REQUETE (#1030).
#
# La section `defaults` porte `timeout server 30s`, et c'est bien : elle
# protege TOUS les vhosts d'un amont qui trainerait. Mais un depot de 22 Mio
# a mis 116 s a s'ecrire sur cette board — HAProxy abandonnait a 30, le
# deposant voyait un 504, ET LE SERVEUR TERMINAIT LE TRAVAIL. Chaque essai
# deposait a nouveau : trois depots pour une seule intention.
#
# DEUX LECONS PAYEES A L'ESSAI. `set-timeout` ne vit QUE dans un backend —
# HAProxy refuse « proxy has no backend capability » cote frontend — et il
# ne connait que `server` et `tunnel` : `set-timeout client` n'existe pas.
# `timeout client` reste donc a 30 s, ce qui convient : c'est un delai
# d'INACTIVITE, et un televersement regulier ne l'atteint jamais.
#
# Relever `defaults` aurait couvert le cas en retirant la protection a la
# centaine de vhosts qui n'en ont pas besoin — payer pour tous ce dont un
# seul a besoin.
acl est_depot_long path_beg /api/v1/droplet/depot
http-request set-timeout server 1h if est_depot_long
option forwardfor
http-request set-header X-Real-IP %[src]
# %[query] rend la chaine de requete SANS le « ? » qui l'introduit : une
@@ -1273,6 +1386,7 @@ case "${1:-}" in
cert)
case "${2:-}" in
list) cmd_cert_list ;;
sync) cmd_cert_sync "${3:-}" ;;
add) cmd_cert_add "$3" "$4" ;;
renew) cmd_cert_renew ;;
*) echo "Usage: haproxyctl cert {list|add|renew} [domain]" ;;
+13
View File
@@ -1,3 +1,16 @@
secubox-waf-ng (1.4.1-1~bookworm1) bookworm; urgency=medium
* `--upstream-timeout` porte de 10 s a 120 s (ref #1030). Dix secondes sont
trop courtes pour tout point d'entree qui ecrit sur disque : un depot de
22 Mio a mis 116 s, le WAF abandonnait a 10 s, le deposant voyait un 504 —
et le serveur, lui, TERMINAIT le travail. Chaque essai deposait a nouveau.
* 120 s et non « une minute de plus par securite » : c'est la duree au-dela
de laquelle plus rien de sain ne se passe sur un point d'entree qui accuse
reception avant d'envoyer son alerte. Assez pour un televersement honnete,
assez court pour qu'un amont reellement mort soit encore constate comme tel.
-- Gerald Kerma <devel@cybermind.fr> Thu, 13 Aug 2026 17:45:00 +0200
secubox-waf-ng (1.3.2-1~bookworm1) bookworm; urgency=high
* Le WAF detectait sans jamais pouvoir bannir (ref #1017). Deux defauts
@@ -65,8 +65,20 @@ ExecStartPre=+/bin/chmod 0750 /var/log/secubox/cookie-audit
ExecStartPre=+/bin/mkdir -p /var/log/secubox/waf
ExecStartPre=+/bin/chown -R secubox-waf:secubox-waf /var/log/secubox/waf
ExecStartPre=+/bin/chmod 0750 /var/log/secubox/waf
# DELAI AMONT : 10 s PAR DEFAUT, TROP COURT POUR QUI ECRIT SUR DISQUE (#1030).
#
# Un depot de 22 Mio a mis 116 s a s'ecrire sur cette board. Le WAF abandonnait
# a 10 s, le deposant voyait un 504 — ET LE SERVEUR TERMINAIT LE TRAVAIL. Chaque
# essai deposait donc a nouveau : trois depots pour une seule intention. Un
# delai mal regle devenait un amplificateur du volume recu.
#
# 120 s : assez pour un televersement honnete, assez court pour qu'un amont
# reellement mort soit encore constate comme tel. Ce n'est pas « une minute de
# plus par securite » — c'est la duree au-dela de laquelle plus rien de sain ne
# se passe sur un point d'entree qui accuse reception avant d'envoyer l'alerte.
ExecStart=/usr/sbin/sbxwaf \
--listen 127.0.0.1:8085 \
--upstream-timeout 120s \
--routes /etc/secubox/waf/haproxy-routes.json \
--rules /etc/secubox/waf/waf-rules.json \
--on-demand-vhosts /etc/secubox/waf/on-demand-vhosts.json \