From da376a630e2634b9916ce867b3c22b897f5b1aeb Mon Sep 17 00:00:00 2001 From: Gemini Date: Fri, 4 Sep 2026 21:51:26 +0100 Subject: [PATCH] docs(runbook): piege cle virtuelle Nabil-Key vs cles dediees Hermes (ticket infra-2026-09-014) --- _INDEX.md | 4 + ...l-key-nabil-key-bifrost-hermes-20260904.md | 120 ++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 common/piege-virtual-key-nabil-key-bifrost-hermes-20260904.md diff --git a/_INDEX.md b/_INDEX.md index 98fa8ed..8cf976c 100644 --- a/_INDEX.md +++ b/_INDEX.md @@ -10,6 +10,7 @@ | Runbook | Date | Tags | |---------|------|------| +| [Piège clé virtuelle Nabil-Key (bfk-0cd1fb...) vs clés dédiées Hermes : fuite budgétaire 8 $/mois, audit exhaustif (perso/tt/nyora), correction chirurgicale vision perso, et tests d'inférence réels (ticket infra-2026-09-014)](common/piege-virtual-key-nabil-key-bifrost-hermes-20260904.md) | 2026-09-04 | infra, bifrost, virtual-key, nabil-key, budget, hermes, audit, gemini | | [Architecture stub forwarder Bifrost (NAS -> VPS via socat/SOCKS5 tailscale-nyora-bridge:1055), procédure de rollback immédiat (<5s), piège allow_all_keys / config.json au boot, et injection session x-bf-eh-x-opencode-session sur bifrost-proxy (tickets infra-2026-09-010 et 008)](common/migration-vps-bifrost-stub-forwarder-20260904.md) | 2026-09-04 | infra, bifrost, vps, tailscale, socat, rollback, allow-all-keys, opencode-go | | [Decommission veille-backend/veille-frontend (API FastAPI) au profit d une page statique bolbol.tn/veille (localStorage, tags, recherche, lu/non lu) ; workflow n8n Ingest Opportunites desactive (cible supprimee)](common/veille-decommission-api-page-statique-20260904.md) | 2026-09-04 | veille, veille-nexum, decommission, page-statique, localstorage, n8n, ports-registry | | [mcp-nas : root conteneur bloqué par ACL Synology sur certains dossiers .claude-staging (drwxrws---) — passer par ssh nas-host / claude-ops plutôt que le root namespacé de mcp-nas](common/mcp-nas-acl-synology-claude-staging-20260902.md) | 2026-09-02 | infra, mcp-nas, acl, synology, permissions, ssh, claude-staging, piege | @@ -79,6 +80,7 @@ | Runbook | Date | Tags | |---------|------|------| +| [Piège clé virtuelle Nabil-Key (bfk-0cd1fb...) vs clés dédiées Hermes : fuite budgétaire 8 $/mois, audit exhaustif (perso/tt/nyora), correction chirurgicale vision perso, et tests d'inférence réels (ticket infra-2026-09-014)](common/piege-virtual-key-nabil-key-bifrost-hermes-20260904.md) | 2026-09-04 | infra, bifrost, virtual-key, nabil-key, budget, hermes, audit, gemini | | [Passerelle MCP dédiée NyoraNotes (multi-agent, StreamableHTTP port 3098, scoping strict par dossier, connecteur DSH validé)](common/nyora-notes-mcp-gateway-deploiement-20260826.md) | 2026-08-26 | nyora-notes-mcp, mcp, streamable-http, scoping, dsh, tailscale | | [Réduction hermes-hub en dispatcher minimal & stabilisation DSH (historique, WebSockets, mémoire NyoraNotes)](common/decommission-switcher-hub-stabilisation-dsh-20260826.md) | 2026-08-26 | dsh, hermes-hub, dispatcher, websocket, history-fix, nyora-notes | | [Hermes Hub — Déploiement et Exploitation Multi-Univers sur VPS (FastAPI, proxy async, design tokens, Tailscale)](common/hermes-hub-deploiement.md) | 2026-08-19 | hermes, hub, multi-univers, vps, tailscale, cloudflare-access, fastapi | @@ -272,3 +274,5 @@ - [hermes-nyora/dsh-vps-secrets-en-dur-incident.md](hermes-nyora/dsh-vps-secrets-en-dur-incident.md) -- INCIDENT SECURITE : BIFROST_API_KEY et OPENCODE_DIRECT_API_KEY commitees en clair dans entrypoint.sh d'un repo Gitea initialement PUBLIC (bolbol/dsh-vps). Detecte par hermes-nyora en relisant avant validation. Repo passe prive, BIFROST_API_KEY rotee en base Bifrost (governance_virtual_keys) et testee fonctionnelle, entrypoint.sh corrige (fail loud au lieu de fallback secret). OPENCODE_DIRECT_API_KEY (= cle maitresse OpenCode.ai partagee, BIFROST_OPENCODE_KEY) reste a roter par Nabil (compte externe). - [hermes-nyora/dsh-vps-rotation-cles-incident-apikey-invalide.md](hermes-nyora/dsh-vps-rotation-cles-incident-apikey-invalide.md) -- Rotation cles dsh-vps (VK Bifrost + OpenCode Zen) apres exposition chat/commit ; incident « API key is invalid » : cause racine = cles en dur residuelles dans compose hote + entrypoint fallback (repo sanitise mais pas l'hote) ; fix env_file + ${VAR:?} + recreation, revocation confirmee, tour UI vert. Pieges : restart ne relit pas .env, sanitisation partielle, StartedAt vs horodatages config (25/08/2026) - [common/rapport-gemini-phase-a-pilote-botmode-20260825.md](common/rapport-gemini-phase-a-pilote-botmode-20260825.md) -- Phase A bascule hermes-nyora v2026.8.19 (0.20.5) : backup pre-upgrade (81Mo), bascule image unique sur nyora, continuite 100% validee (Telegram, Cron, MCP Context-Hub, Veille, Notes, SMTP), verrou reseau 8642 etanche (perso/tt bloques, 401 local), Gate 1 reussi (interception deny rules + safe root), Gate 2 documente (roster actif si ui_meta[\x27hermes-bots\x27] dans profile.yaml, injection sur titre \x27Bot Chat\x27, messagerie headless via hermes chat -Q), Browser Use mode teste (uvx 0.1.9, fallback natif auto), VPS Contabo verifie sans 8642 publie (25/08/2026) +- [common/piege-virtual-key-nabil-key-bifrost-hermes-20260904.md](common/piege-virtual-key-nabil-key-bifrost-hermes-20260904.md) -- Piège clé virtuelle Nabil-Key (bfk-0cd1fb...) vs clés dédiées Hermes : fuite budgétaire 8 $/mois, audit exhaustif de la flotte NAS (perso/tt/nyora), détection et correction chirurgicale de la fuite résiduelle vision perso (ligne 134 config.yaml), et validation par inférences réelles de bout en bout (04/09/2026) + diff --git a/common/piege-virtual-key-nabil-key-bifrost-hermes-20260904.md b/common/piege-virtual-key-nabil-key-bifrost-hermes-20260904.md new file mode 100644 index 0000000..0d99916 --- /dev/null +++ b/common/piege-virtual-key-nabil-key-bifrost-hermes-20260904.md @@ -0,0 +1,120 @@ +# Piège Clé Virtuelle Nabil-Key vs Clé Dédiée Consommateur Bifrost (04/09/2026) + +## 1. Contexte & Problématique + +Chaque instance Hermes et consommateur de la passerelle **Bifrost** dispose d'une clé virtuelle dédiée (Virtual Key ou VK) associée à une politique budgétaire propre dans la table `governance_budgets` : +- `vk-hermes-tt` $\rightarrow$ budget TT +- `vk-hermes-nyora` $\rightarrow$ budget Nyora +- `vk-hermes-perso` $\rightarrow$ budget Perso +- `Nabil-Key` $\rightarrow$ budget personnel Nabil (plafond strict : 8 \$/mois) + +### Le Piège +Lors de configurations initiales ou de migrations manuelles, la valeur secrète de **`Nabil-Key`** (`bfk-0cd1fba7d440ca2eefd30b28a7349b7d28422dcbc49e982d`) a été copiée par erreur dans `/opt/data/config.yaml` d'instances Hermes (`hermes-nyora`, `hermes-perso`). + +### Symptômes +1. **Facturation croisée** : L'ensemble des coûts de tokens (appels LLM principaux et/ou appels `auxiliary.vision` / `auxiliary.approval`) est débité du budget personnel de 8 \$/mois au lieu de la sphère appropriée. +2. **Risque de coupure inopinée** : Lorsque le budget de 8 \$ est atteint, Bifrost rejette toutes les requêtes portant cette clé avec une erreur HTTP 429 / 401 (`budget exhausted`), bloquant à la fois l'usage personnel et l'instance Hermes concernée. + +--- + +## 2. Procédure de Diagnostic + +### Étape 1 : Récupérer les clés virtuelles officielles depuis Bifrost +Sur l'hôte hébergeant Bifrost (actuellement VPS Contabo `100.94.90.119` ou snapshot de base) : +```bash +python3 -c " +import sqlite3 +conn = sqlite3.connect('/home/dsh-agent/bifrost/data/config.db') +c = conn.cursor() +c.execute('SELECT name, value, is_active FROM governance_virtual_keys;') +for name, val, active in c.fetchall(): + print(f'{name}: {val[:12]}... (actif={active})') +" +``` +*Correspondances attendues :* +- `vk-hermes-nyora` : commence par `sk-bf-90d6...` +- `vk-hermes-perso` : commence par `sk-bf-bb06...` +- `vk-hermes-tt` : commence par `sk-bf-2669...` +- `Nabil-Key` : commence par `bfk-0cd1...` (à proscrire dans les conteneurs d'agents) + +### Étape 2 : Auditer `config.yaml` dans chaque instance Hermes +Sur le NAS Synology : +```bash +for inst in hermes-agent-tt hermes-agent-nyora hermes-agent-perso; do + echo "=== $inst ===" + docker exec $inst python3 -c ' +import yaml +cfg = yaml.safe_load(open("/opt/data/config.yaml")) +m_key = cfg.get("model", {}).get("api_key", "") +print(" model.api_key:", m_key[:12] if m_key else "vide") +for k, v in cfg.get("auxiliary", {}).items(): + if isinstance(v, dict) and v.get("api_key"): + print(f" auxiliary.{k}.api_key:", v["api_key"][:12]) +' +done +``` +**Alerte immédiate** si une clé commence par `bfk-0cd1...`. + +--- + +## 3. Procédure de Correction Chirurgicale + +Ne jamais réécrire l'intégralité du fichier `config.yaml` (risque d'écraser des réglages runtime ou de corrompre l'indentation YAML). Utiliser un remplacement chirurgical par `sed`. + +### 1. Sauvegarde préalable obligatoire +```bash +docker exec cp /opt/data/config.yaml /opt/data/config.yaml.bak-$(date +%Y%m%d)-cle-perso +``` + +### 2. Remplacement chirurgical de la valeur +```bash +# Exemple pour hermes-agent-perso avec sa clé vk-hermes-perso +docker exec sed -i 's/bfk-0cd1fba7d440ca2eefd30b28a7349b7d28422dcbc49e982d//g' /opt/data/config.yaml +``` + +### 3. Contrôle du diff +```bash +docker exec diff -u /opt/data/config.yaml.bak-$(date +%Y%m%d)-cle-perso /opt/data/config.yaml +``` +*Le diff ne doit comporter strictement que les lignes d'affectation de clé modifiées.* + +### 4. Redémarrage propre du conteneur +```bash +docker restart +``` + +--- + +## 4. Validation Post-Correction par Inférence Réelle + +Exécuter un appel réel depuis l'intérieur du conteneur pour valider le bon fonctionnement de bout en bout : + +```bash +docker exec python3 -c ' +import yaml, urllib.request, json, time + +cfg = yaml.safe_load(open("/opt/data/config.yaml")) +m = cfg.get("model", {}) +base_url = m.get("base_url", "http://bifrost-proxy/v1").rstrip("/") +api_key = m.get("api_key", "") + +url = base_url + "/chat/completions" +payload = { + "model": m.get("default", "mimo-v2.5"), + "messages": [{"role": "user", "content": "ping"}], + "max_tokens": 10 +} +req = urllib.request.Request( + url, + headers={ + "Content-Type": "application/json", + "Authorization": f"Bearer {api_key}" + }, + data=json.dumps(payload).encode() +) +with urllib.request.urlopen(req, timeout=15) as resp: + data = json.loads(resp.read().decode()) + print("Inférence OK :", data.get("model"), "en", data.get("extra_fields", {}).get("latency"), "ms") +' +``` +*Attendu : `Inférence OK : mimo-v2.5 en ms`.*