From 2e0ae9a91c446c81e4247bc6c0f687fdc3919464 Mon Sep 17 00:00:00 2001 From: Pierre Martin Date: Wed, 23 Sep 2026 01:11:03 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20upgrade=20Gitea=201.25.4=20->=201.27.3?= =?UTF-8?q?=20+=20le=C3=A7on=20sur=20appDefinitions/update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Backup complet (dump SQL + volume) avant upgrade - Documente l'incident secondaire (502 temporaire sur git.sans.pub) causé par un payload appDefinitions/update incomplet ayant réinitialisé containerHttpPort à sa valeur par défaut, et le correctif appliqué - Ajoute un avertissement dans CLAUDE.md : cette API remplace la config, ne la fusionne pas Co-Authored-By: Claude Sonnet 5 --- CLAUDE.md | 4 +++- incidents/2026-09-14-gitea-cryptominer.md | 18 ++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 9c5a8a6..a4083d5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -93,9 +93,11 @@ Endpoints utiles : - `GET /user/apps/appDefinitions` → liste toutes les apps avec `envVars`, `volumes`, `ports`, etc. - `POST /user/apps/appDefinitions/update` (payload : `appName` + les champs à conserver/modifier) → met à jour la config d'une app **sans redéployer l'image** (mais redémarre le service) +⚠️ **`appDefinitions/update` remplace, ne fusionne pas** : un champ absent du payload est réinitialisé à sa valeur par défaut (vécu en prod le 2026-09-23 : `containerHttpPort` oublié → réinitialisé à `80` au lieu de `3000` → 502 sur git.sans.pub pendant plusieurs minutes, cf `incidents/2026-09-14-gitea-cryptominer.md`). **Toujours faire un `GET /user/apps/appDefinitions` juste avant et renvoyer l'objet complet**, en ne modifiant que le(s) champ(s) voulu(s). + ## Gitea — détails techniques -- **Containers Docker** (Swarm) : `srv-captain--gitea.1.*` (gitea/gitea:1.25.4) + `srv-captain--gitea-db.1.*` (mysql:5.7) +- **Containers Docker** (Swarm) : `srv-captain--gitea.1.*` (gitea/gitea:1.27.3, mis à jour le 2026-09-23) + `srv-captain--gitea-db.1.*` (mysql:5.7) - **Volume de données** : `captain--gitea-data` → monté sur `/data` dans le container - Dépôts bare : `/data/git/repositories//.git` - Config app : `/data/gitea/conf/app.ini` (contient des secrets en clair : `INTERNAL_TOKEN`, `LFS_JWT_SECRET`, mot de passe MySQL — jamais versionner ce fichier) diff --git a/incidents/2026-09-14-gitea-cryptominer.md b/incidents/2026-09-14-gitea-cryptominer.md index 29d4627..bb35e4e 100644 --- a/incidents/2026-09-14-gitea-cryptominer.md +++ b/incidents/2026-09-14-gitea-cryptominer.md @@ -1,6 +1,6 @@ # Incident : mineur de cryptomonnaie via Gitea (packObjectsHook) -- **Statut** : contenu, secrets serveur rotés (2026-09-23). Reste : upgrade Gitea, restriction `/api/internal/*`, mots de passe utilisateurs/tokens/webhooks. +- **Statut** : contenu, secrets serveur rotés et Gitea mis à jour vers 1.27.3 (2026-09-23). Reste : restriction `/api/internal/*`, mots de passe utilisateurs/tokens/webhooks. - **Date de compromission** : 2026-09-14 ~15:16-15:17 - **Date de détection** : 2026-09-22 (via un `git clone` cassé) - **Impact** : exécution de code sur le serveur hébergeant Gitea (et potentiellement l'hôte partagé, ~40 services CapRover) ; minage de cryptomonnaie (Monero) actif pendant ~8 jours @@ -88,9 +88,23 @@ Effectué : Outillage utilisé : CLI `caprover` (installé localement via `fnm` + `npm install -g caprover`, authentifié avec `caprover login` — voir section CapRover CLI dans `CLAUDE.md`). +## Mise à jour Gitea 1.25.4 → 1.27.3 — effectuée le 2026-09-23 + +Corrige 15 advisories de sécurité publiées le 2026-08-29 (dont au moins 2 RCE en sévérité haute : bypass d'approbation sur les runners self-hosted, RCE via rendu externe de markup). Aucune ne correspond exactement au vecteur de cet incident (API interne), mais l'upgrade était largement justifiée. + +Effectué : +- [x] Backup complet avant upgrade : dump SQL (`mysqldump`, 114 tables) + tarball du volume de données complet (2,1 Go), dans `/home/zuck/backups/gitea-upgrade-20260923005619/` sur le serveur +- [x] Déploiement de `gitea/gitea:1.27.3` via l'API CapRover (`POST /user/apps/appData/gitea` avec `captainDefinitionContent`) +- [x] Démarrage vérifié sans erreur de migration, `Git version: 2.54.0` + +### Incident secondaire causé pendant l'upgrade (auto-résolu le jour même) + +La mise à jour CapRover de `DB_PASSWD` faite plus tôt (rotation des secrets, cf section dédiée) avait été envoyée avec un payload **incomplet** — le champ `containerHttpPort` n'était pas inclus. CapRover l'a réinitialisé à sa valeur par défaut (`80`) au lieu de conserver `3000` (le port réel utilisé par l'image officielle Gitea). Conséquence : **`git.sans.pub` a répondu en 502 pendant plusieurs minutes** (visible dans les logs nginx avec du trafic réel externe), le temps de diagnostiquer (`connect() failed (111: Connection refused)` vers `10.0.1.x:80`) et de renvoyer un payload complet avec `containerHttpPort: 3000` restauré. + +**Leçon** : toute mise à jour via `POST /user/apps/appDefinitions/update` doit **toujours renvoyer l'intégralité des champs de la définition actuelle** (récupérés via un `GET /user/apps/appDefinitions` juste avant), pas seulement le(s) champ(s) qu'on veut changer — l'API ne fait pas de merge partiel, elle remplace. + ## Reste à faire -- [ ] Mettre à jour Gitea vers la dernière version patchée (partir de `1.25.4`) - [ ] Vérifier/restreindre l'accès à `/api/internal/*` au niveau du reverse-proxy (défense en profondeur, même si Gitea patch le bug) - [ ] Changer les mots de passe des 3 comptes Gitea (pierre, Sans.pub, Duogeeks) par précaution - [ ] Révoquer et régénérer les 4 tokens d'accès personnels (LML, Piaire CI/Deploy, Piaire, Laptop .npmrc) — penser à mettre à jour partout où ils sont utilisés