docs: rotation des secrets Gitea effectuée + CLI CapRover documenté

- INTERNAL_TOKEN, LFS_JWT_SECRET et mot de passe MySQL rotés le 2026-09-23
- DB_PASSWD CapRover resynchronisé, service redémarré et vérifié
- Confirmation que les env vars CapRover de l'app gitea (DB_PASSWD etc.)
  ne sont pas au format GITEA__section__clé donc inertes pour app.ini
- Documentation de l'installation/usage du CLI caprover via fnm

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Pierre Martin
2026-09-23 00:51:58 +02:00
co-authored by Claude Sonnet 5
parent 8f584a8672
commit 66c356848b
2 changed files with 35 additions and 2 deletions
+16
View File
@@ -78,6 +78,21 @@ Aucun service en erreur connu (vérifié 2026-02-19). `mail` tourne correctement
**Prérequis** : définir `CAPROVER_PASSWORD` dans `.env` (voir `.envrc`) **Prérequis** : définir `CAPROVER_PASSWORD` dans `.env` (voir `.envrc`)
### CLI CapRover (alternative à l'API brute)
Installé localement via `fnm` (Node géré sans toucher au système) :
```bash
curl -fsSL https://fnm.vercel.app/install | bash # une seule fois
fnm install --lts && fnm use lts-latest
npm install -g caprover
caprover login # à faire soi-même dans un vrai terminal (TTY requis pour le mot de passe)
```
Ensuite : `caprover list` (machines connectées), `caprover api -n <nom-machine> -t /chemin/api -m GET -d '{}'` pour des appels génériques (toujours passer `-d` même vide, sinon prompt interactif qui plante sans TTY).
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)
## Gitea — détails techniques ## 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.25.4) + `srv-captain--gitea-db.1.*` (mysql:5.7)
@@ -88,6 +103,7 @@ Aucun service en erreur connu (vérifié 2026-02-19). `mail` tourne correctement
- Logs applicatifs : `/data/gitea/log/gitea.log` (rotation quotidienne en `.gz`) - Logs applicatifs : `/data/gitea/log/gitea.log` (rotation quotidienne en `.gz`)
- **Réseau** : container Gitea sur `captain-overlay-network`, IP interne observée `10.0.1.75`. Le reverse-proxy CapRover (`captain-nginx`) apparaît côté Gitea sous l'IP `10.0.1.105` pour toutes les requêtes publiques (pas d'IP client réelle sans config `X-Forwarded-For`/trusted proxies). - **Réseau** : container Gitea sur `captain-overlay-network`, IP interne observée `10.0.1.75`. Le reverse-proxy CapRover (`captain-nginx`) apparaît côté Gitea sous l'IP `10.0.1.105` pour toutes les requêtes publiques (pas d'IP client réelle sans config `X-Forwarded-For`/trusted proxies).
- **Connexion à la base MySQL depuis le host/container** : passer par `-h 127.0.0.1` (TCP), pas par défaut socket local (`localhost`) — le grant MySQL de l'utilisateur `gitea` ne couvre que `%`/TCP, pas `localhost`. - **Connexion à la base MySQL depuis le host/container** : passer par `-h 127.0.0.1` (TCP), pas par défaut socket local (`localhost`) — le grant MySQL de l'utilisateur `gitea` ne couvre que `%`/TCP, pas `localhost`.
- **Variables d'env CapRover de l'app `gitea`** (vérifié via `docker service inspect srv-captain--gitea`) : `DB_HOST`, `DB_PASSWD`, `DB_TYPE`, `DB_USER`, `RUN_MODE`. ⚠️ Aucune n'est au format `GITEA__<section>__<clé>` attendu par l'image officielle pour réinjecter dans `app.ini` au démarrage — elles sont donc **inertes** pour Gitea (c'est `app.ini` dans le volume qui fait foi). En cas de rotation de secret (mot de passe DB notamment), mettre à jour `app.ini` + redémarrer suffit fonctionnellement ; penser quand même à resynchroniser `DB_PASSWD` côté CapRover pour ne pas laisser une copie obsolète du secret.
- **⚠️ Voir `incidents/2026-09-14-gitea-cryptominer.md`** : l'API interne Gitea (`/api/internal/manager/add-logger`) a été exploitée pour injecter des `packObjectsHook` malveillants dans `.gitconfig`. Toujours vérifier ce fichier ne contient pas d'entrées `[uploadpack] packObjectsHook` inattendues. - **⚠️ Voir `incidents/2026-09-14-gitea-cryptominer.md`** : l'API interne Gitea (`/api/internal/manager/add-logger`) a été exploitée pour injecter des `packObjectsHook` malveillants dans `.gitconfig`. Toujours vérifier ce fichier ne contient pas d'entrées `[uploadpack] packObjectsHook` inattendues.
## Suivi des tâches ## Suivi des tâches
+19 -2
View File
@@ -1,6 +1,6 @@
# Incident : mineur de cryptomonnaie via Gitea (packObjectsHook) # Incident : mineur de cryptomonnaie via Gitea (packObjectsHook)
- **Statut** : contenu, remédiation en cours (rotation des secrets à finaliser) - **Statut** : contenu, secrets serveur rotés (2026-09-23). Reste : upgrade Gitea, restriction `/api/internal/*`, mots de passe utilisateurs/tokens/webhooks.
- **Date de compromission** : 2026-09-14 ~15:16-15:17 - **Date de compromission** : 2026-09-14 ~15:16-15:17
- **Date de détection** : 2026-09-22 (via un `git clone` cassé) - **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 - **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
@@ -73,11 +73,28 @@ Aucune anomalie détectée au-delà de l'injection `packObjectsHook` :
**Conclusion** : l'attaque semble limitée à l'exécution de commande via le hook git (cryptojacking), sans prise de compte ni persistance au niveau applicatif Gitea. Mais l'attaquant avait un accès en lecture au système de fichiers du conteneur, donc à `app.ini` (secrets en clair) — voir rotation ci-dessous. **Conclusion** : l'attaque semble limitée à l'exécution de commande via le hook git (cryptojacking), sans prise de compte ni persistance au niveau applicatif Gitea. Mais l'attaquant avait un accès en lecture au système de fichiers du conteneur, donc à `app.ini` (secrets en clair) — voir rotation ci-dessous.
## Rotation des secrets — effectuée le 2026-09-23
Confirmé via `docker service inspect` puis via l'API CapRover (double vérification) : les variables d'env CapRover de l'app `gitea` sont `RUN_MODE`, `DB_TYPE`, `DB_HOST`, `DB_USER`, `DB_PASSWD` — aucune au format `GITEA__<section>__<clé>` attendu par l'image officielle pour être réinjectée dans `app.ini`. Elles sont donc **inertes** pour Gitea (c'est `app.ini` dans le volume qui fait foi). Conséquence : `INTERNAL_TOKEN`/`LFS_JWT_SECRET` n'ont pas de variable CapRover à mettre à jour ; seul `DB_PASSWD` avait une copie dormante à resynchroniser.
Effectué :
- [x] `INTERNAL_TOKEN` régénéré (`gitea generate secret INTERNAL_TOKEN`) dans `app.ini`
- [x] `LFS_JWT_SECRET` régénéré (`gitea generate secret LFS_JWT_SECRET`) dans `app.ini`
- [x] Mot de passe MySQL de l'utilisateur `gitea`@`%` changé (`ALTER USER ... IDENTIFIED BY ...`)
- [x] `app.ini` mis à jour avec les 3 nouvelles valeurs (backup : `app.ini.bak-20260923004549`)
- [x] Service Gitea redémarré (`docker service update --force srv-captain--gitea`), reconnexion DB confirmée dans les logs, aucune erreur
- [x] Clone git testé et fonctionnel après rotation
- [x] Variable CapRover `DB_PASSWD` resynchronisée via l'API (`/user/apps/appDefinitions/update`), vérifiée par comparaison de hash
Outillage utilisé : CLI `caprover` (installé localement via `fnm` + `npm install -g caprover`, authentifié avec `caprover login` — voir section CapRover CLI dans `CLAUDE.md`).
## Reste à faire ## Reste à faire
- [ ] Mettre à jour Gitea vers la dernière version patchée (partir de `1.25.4`) - [ ] 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) - [ ] Vérifier/restreindre l'accès à `/api/internal/*` au niveau du reverse-proxy (défense en profondeur, même si Gitea patch le bug)
- [ ] Rotation des secrets (voir section dédiée plus bas / demander le détail à Claude si besoin) - [ ] 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
- [ ] Régénérer les 4 tokens de webhook CapRover (déploiement tmp-fc, dechets-agglo-muretain, etherpad, domi-marche) et mettre à jour les URLs de webhook correspondantes dans Gitea
- [ ] Vérifier l'intégrité des autres services sur le même hôte (mouvement latéral) — rien trouvé à ce stade, mais l'hôte partagé fait tourner Vaultwarden, Nextcloud, mail - [ ] Vérifier l'intégrité des autres services sur le même hôte (mouvement latéral) — rien trouvé à ce stade, mais l'hôte partagé fait tourner Vaultwarden, Nextcloud, mail
- [ ] Envisager un scan antirootkit / vérification d'intégrité des binaires système sur l'hôte, vu la durée de la compromission (8 jours) - [ ] Envisager un scan antirootkit / vérification d'intégrité des binaires système sur l'hôte, vu la durée de la compromission (8 jours)
- [ ] Revoir si `DOMAIN = localhost` dans `app.ini` (`[server]`) est correct ou un reliquat de config - [ ] Revoir si `DOMAIN = localhost` dans `app.ini` (`[server]`) est correct ou un reliquat de config