docs(runbook): piege cle virtuelle Nabil-Key vs cles dediees Hermes (ticket infra-2026-09-014)
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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 <nom_conteneur> 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 <nom_conteneur> sed -i 's/bfk-0cd1fba7d440ca2eefd30b28a7349b7d28422dcbc49e982d/<VRAIE_CLE_VK>/g' /opt/data/config.yaml
|
||||
```
|
||||
|
||||
### 3. Contrôle du diff
|
||||
```bash
|
||||
docker exec <nom_conteneur> 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 <nom_conteneur>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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 <nom_conteneur> 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 <latence> ms`.*
|
||||
Reference in New Issue
Block a user