Files
Pierre MartinandClaude Sonnet 5 2e0ae9a91c docs: upgrade Gitea 1.25.4 -> 1.27.3 + leçon sur appDefinitions/update
- 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 <noreply@anthropic.com>
2026-09-23 01:11:03 +02:00

7.4 KiB

Serveur sans.pub

Lorsque tu interroges le serveur, ne lances que des commandes en LECTURE SEULE (non destructrices). Sauf à la demande explicite de l'utilisateur.

Accès

  • SSH: ssh zuck@sans.pub (alias ~/.ssh/config : Host sans.pub) — sudo sans mot de passe. Note : ce document mentionnait auparavant un utilisateur cloud, à vérifier/clarifier (voir install-serveur.md, le compte créé est zuck).
  • CapRover: https://captain.cloud.sans.pub
  • Gitea: git.sans.pub (port SSH 2242, User git dans ~/.ssh/config)

Infrastructure

  • OS: Debian 9 (kernel 4.9.0-12-amd64)
  • Docker: 19.03.12 (mode Swarm)
  • Disque: 914 Go total, ~193 Go utilisés (23%)
  • RAM: 23 Go (~6 Go utilisés, swap quasi vide — vérifié 2026-02-19)

Structure Caprover

  • /captain/data/ - données persistantes
  • /captain/generated/ - configs générées
  • /captain/temp/ - fichiers temporaires

Services actifs (~40 services)

Outils collaboratifs

  • etherpad - éditeur collaboratif
  • ethercalc - tableur collaboratif
  • cryptpad - suite bureautique chiffrée
  • nextcloud - cloud personnel

Développement

  • gitea - forge Git
  • adminer - gestion BDD

Communication

  • writefreely - blog
  • peertube - vidéos
  • rss + rss-bridge - flux RSS

Sécurité/Outils

  • bitwarden - gestionnaire mots de passe
  • privatebin - partage de texte chiffré
  • ots - secrets temporaires
  • it-tools - boîte à outils
  • cyberchef - manipulation de données

Sites perso

  • pierre-martin - site perso
  • sans-pub-site - site principal
  • une-espace - autre projet

Enquêtes

  • limesurvey - sondages

Services en erreur

Aucun service en erreur connu (vérifié 2026-02-19). mail tourne correctement (1/1, poste.io).

Commandes et skills Claude

Commande

  • /supervision - analyse complète de la santé du serveur (invocation manuelle)

Skill (auto-activé)

  • caprover - gestion des apps via API CapRover
    • list : lister les apps
    • info NOM : détails d'une app
    • env NOM : variables d'environnement
    • scale NOM N : changer le nombre d'instances
    • restart NOM : redémarrer une app

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) :

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)

⚠️ 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.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/<owner>/<repo>.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)
    • .gitconfig global (compte système utilisé par Gitea pour exécuter git) : /data/gitea/home/.gitconfig
    • 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).
  • 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.

Suivi des tâches

Voir todo.txt (format todo.txt standard)

Mettre à jour une app CapRover

TOUJOURS passer par l'API CapRover — jamais via docker service update directement (CapRover ne le verrait pas).

Procédure

  1. Login (obtenir un token) :
import urllib.request, json

data = json.dumps({'password': 'CAPROVER_PASSWORD'}).encode()
req = urllib.request.Request('https://captain.cloud.sans.pub/api/v2/login', data=data, headers={'Content-Type': 'application/json'}, method='POST')
with urllib.request.urlopen(req) as r:
    token = json.loads(r.read().decode())['data']['token']
  1. Déployer une nouvelle image :
headers = {'Content-Type': 'application/json', 'x-captain-auth': token, 'x-namespace': 'captain'}
captain_def = json.dumps({'schemaVersion': 2, 'imageName': 'IMAGE:TAG'})
payload = json.dumps({'appName': 'NOM_APP', 'captainDefinitionContent': captain_def, 'gitHash': ''}).encode()
req = urllib.request.Request('https://captain.cloud.sans.pub/api/v2/user/apps/appData/NOM_APP', data=payload, headers=headers, method='POST')
with urllib.request.urlopen(req) as r:
    print(r.read().decode())  # {"status":100,"description":"Deploy is done"}

Note : curl a un problème d'encodage dans cet environnement — utiliser python3 à la place.

Commandes utiles

# Voir les logs d'un service
ssh cloud@sans.pub "docker service logs srv-captain--NOM --tail 100"

# Espace disque des volumes
ssh cloud@sans.pub "docker system df -v"

# Nettoyer les images inutilisées
ssh cloud@sans.pub "docker image prune -a"

Attention

  • Debian 9 est obsolète (fin de support LTS)
  • RAM : surveiller si ajout de nouveaux services (mail = 1,78 Go, scenari = 862 Mo)