Incident du 2026-09-14 : injection de packObjectsHook via l'API interne Gitea pour déployer un mineur Monero (XMRig). Contenu et nettoyé le 2026-09-22. Voir incidents/2026-09-14-gitea-cryptominer.md pour le détail complet (analyse, IOC, remédiation, reste à faire). Ajout également des détails techniques sur le setup Gitea (containers, volumes, réseau) découverts pendant l'investigation. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
5.4 KiB
5.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)
Gitea — détails techniques
- Containers Docker (Swarm) :
srv-captain--gitea.1.*(gitea/gitea:1.25.4) +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. - ⚠️ 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)