claude-code-setup — Backup sécurisé de la config Claude Code
Mettre en place un repo GitHub privé pour sauvegarder la configuration Claude Code (settings, hooks, skills) avec chiffrement GPG et nettoyage PII, en évitant les 3 failles classiques.
[TUTO-16] claude-code-setup — Backup sécurisé de la config Claude Code
Objectif
Configurer un backup automatique de ~/.claude/ vers un repo GitHub privé, chiffré GPG pour les fichiers sensibles, avec nettoyage des PII avant commit. Ce tuto couvre les 3 failles classiques : secrets commités, dry-run silencieusement ignoré, et path traversal via .backupinclude.
Prérequis
- Claude Code WSL2 installé (voir TUTO-09)
- Clé GPG disponible (
gpg --list-secret-keys) - Repo GitHub privé
claude-code-setupcréé
Étape 0 — Ce qui ne doit JAMAIS être committé
Avant d’écrire la moindre ligne de script, identifier les données à protéger :
| Fichier | Risque | Traitement |
|---|---|---|
~/.claude.json | emailAddress, accountUuid, userID, anonymousId, githubRepoPaths, oauthAccount, overageCreditGrantCache | Nettoyage jq + chiffrement GPG |
~/.claude/.credentials | Token Anthropic | Jamais committé — exclure absolument |
~/.claude/settings.json | Tokens MCP (ctx7sk-, Bearer…) | Scan + nettoyage avant commit |
~/.claude/hooks/ | Scripts shell — pas de secrets si bien écrits | Committer en clair |
⚠️ Sécurité :
.credentialsest le token Anthropic. Le committer même chiffré augmente la surface d’exposition sans bénéfice — il se régénère viaclaude auth login. L’exclure est non négociable.
Étape 1 — Nettoyage PII de claude.json
Le nettoyage jq doit couvrir les champs PII structurels et les patterns de tokens :
jq '
walk(
if type == "object" then
with_entries(
if (.key | test(
"^(emailAddress|accountUuid|organizationUuid|userID|anonymousId|
oauthAccount|overageCreditGrantCache|githubRepoPaths|clientDataCache|
groveConfigCache|changelogLastFetched|firstStartTime|
claudeCodeFirstTokenDate|lastPlanModeUse|numStartups|
companion|additionalModelOptionsCache)$"; "xi"
)) then .value = "REDACTED"
else .
end
)
else . end
) |
walk(
if type == "string" then
gsub("(?:sk-ant-|ghp_|ghs_|gho_|ghr_|github_pat_|ctx7sk-|Bearer\\s)[A-Za-z0-9_\\-]+";
"REDACTED")
else . end
)
' ~/.claude.json > /tmp/claude.template.json
⚠️ Sécurité : les patterns de tokens évoluent — GitHub ajoute des formats régulièrement (gho_, ghr_, github_pat_ depuis 2021). Vérifier la liste à chaque rotation de token.
Vérification obligatoire après nettoyage :
# Aucun email, UUID ou token ne doit rester
jq '[paths(scalars)] | map(
select(test("email|uuid|Id|account|credit|billing|paths|anonymous|grove|changelog"; "i"))
)' /tmp/claude.template.json
# Aucun pattern de token résiduel
grep -iE "sk-ant-|ghp_|ghs_|gho_|ctx7sk-|Bearer " /tmp/claude.template.json \
&& echo "🔴 SECRETS DÉTECTÉS — NE PAS COMMITTER" \
|| echo "✅ Template propre"
Étape 2 — Implémenter —dry-run dès le début
Un script de backup sans --dry-run utilisable est une fausse sécurité. Pattern à placer
en tête du script, avant set -euo pipefail :
DRY_RUN=false
[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=true
# Wrapper pour les commandes avec effet de bord
run() {
if $DRY_RUN; then
echo "[DRY-RUN] $*" >&2
else
"$@"
fi
}
Utilisation dans le script :
run git push origin main
run gpg --encrypt --output "$DEST.gpg" "$SOURCE"
Test de validation :
bash backup-to-github.sh --dry-run 2>&1 | grep "DRY-RUN"
# Vérifier qu'AUCUN nouveau commit n'apparaît sur GitHub après ce run
git log --oneline -3 # le hash ne doit pas avoir changé
⚠️ Sécurité : un
--dry-runsilencieusement ignoré est pire qu’une absence de dry-run. Il donne une fausse confiance lors des tests — tester le vrai comportement du flag avant de considérer le script prêt pour la production.
Étape 3 — Protéger le répertoire skills/ avec .backupinclude
Ne pas sauvegarder tous les skills aveuglément — certains (ECC, génériques) n’ont aucune valeur et gonflent le repo inutilement.
Créer ~/.claude/skills/.backupinclude listant un skill par ligne :
rust-patterns
tdd-workflow
comfyui-image-gen
Dans le script de backup :
SKILLS_DIR="$HOME/.claude/skills"
BACKUP_SKILLS_DIR="$REPO_DIR/skills"
while IFS= read -r skill_dir; do
# Validation : interdire les path traversal
if [[ "$skill_dir" == *..* || "$skill_dir" == /* || "$skill_dir" == */* ]]; then
log "DANGER : entrée rejetée (path traversal potentiel) : '$skill_dir'"
continue
fi
src="$SKILLS_DIR/$skill_dir"
[[ -d "$src" ]] || { log "AVERTISSEMENT : skill introuvable : '$skill_dir'"; continue; }
run rsync -a --delete "$src/" "$BACKUP_SKILLS_DIR/$skill_dir/"
done < "$SKILLS_DIR/.backupinclude"
⚠️ Sécurité : une entrée
../../.sshdans.backupincludepermet à un attaquant qui contrôle ce fichier d’exfiltrer n’importe quel répertoire du système. La validation*..* || /*bloque cette classe d’attaque. Vérifier le contenu de.backupincludeavant chaque modification.
Étape 4 — Chiffrer les fichiers sensibles avec GPG
Pour claude.template.json et settings.json après scan :
GPG_RECIPIENT="[email protected]"
# Chiffrement asymétrique (clé publique du destinataire)
gpg --recipient "$GPG_RECIPIENT" \
--encrypt \
--armor \
--output "$REPO_DIR/claude.template.json.gpg" \
/tmp/claude.template.json
# Vérification : le fichier clair n'est PAS dans le repo
ls "$REPO_DIR" | grep -v ".gpg" | grep "claude"
# Attendu : aucune ligne (seul le .gpg est présent)
⚠️ Sécurité : si la clé GPG est perdue, le backup est irrécupérable. Exporter la clé privée vers un support offline (clé USB chiffrée, papier) :
gpg --armor --export-secret-keys [email protected] > cle-privee-offline.asc
Checklist ☑ finale
-
~/.claude/.credentialsabsent du repo (vérifier :ls "$REPO_DIR" | grep credentials) -
claude.template.json: 0 PII résiduel (test jq ci-dessus) -
--dry-runfonctionnel : aucun push réel après exécution -
.backupinclude: validation path traversal active - Clé GPG exportée hors-machine pour restauration
Dépannage
| Problème | Vérification |
|---|---|
jq walk() non disponible | Mettre à jour jq : sudo apt install jq (version ≥ 1.6) |
| GPG chiffrement échoue | gpg --list-keys → vérifier que la clé publique est importée |
--dry-run ne filtre pas tous les appels | Vérifier que chaque commande avec effet de bord utilise run() |
Skill non trouvé dans .backupinclude | Vérifier l’orthographe exacte du dossier dans ~/.claude/skills/ |
Références
- TUTO-02f : pattern GPG backup (série OpenClaw — archivée, pattern encore valide)
jq walk(): https://jqlang.github.io/jq/manual/#walk- TUTO-09 — Claude Code WSL2 installation
- TUTO-09b — Hooks avancés Claude Code