- 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>
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 utilisateurcloud, à vérifier/clarifier (voirinstall-serveur.md, le compte créé estzuck). - CapRover: https://captain.cloud.sans.pub
- Gitea:
git.sans.pub(port SSH 2242,User gitdans~/.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 CapRoverlist: lister les appsinfo NOM: détails d'une appenv NOM: variables d'environnementscale NOM N: changer le nombre d'instancesrestart 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 avecenvVars,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/datadans 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) .gitconfigglobal (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)
- Dépôts bare :
- Réseau : container Gitea sur
captain-overlay-network, IP interne observée10.0.1.75. Le reverse-proxy CapRover (captain-nginx) apparaît côté Gitea sous l'IP10.0.1.105pour toutes les requêtes publiques (pas d'IP client réelle sans configX-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'utilisateurgiteane couvre que%/TCP, paslocalhost. - Variables d'env CapRover de l'app
gitea(vérifié viadocker service inspect srv-captain--gitea) :DB_HOST,DB_PASSWD,DB_TYPE,DB_USER,RUN_MODE. ⚠️ Aucune n'est au formatGITEA__<section>__<clé>attendu par l'image officielle pour réinjecter dansapp.iniau démarrage — elles sont donc inertes pour Gitea (c'estapp.inidans le volume qui fait foi). En cas de rotation de secret (mot de passe DB notamment), mettre à jourapp.ini+ redémarrer suffit fonctionnellement ; penser quand même à resynchroniserDB_PASSWDcô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 despackObjectsHookmalveillants dans.gitconfig. Toujours vérifier ce fichier ne contient pas d'entrées[uploadpack] packObjectsHookinattendues.
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
- 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']
- 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 :
curla un problème d'encodage dans cet environnement — utiliserpython3à 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)