docs(diary): incident 2026-07-09 — "BOOK LIVE conto offline" = cert Traefik di default
Traefik auto-upgrade (latest+Watchtower -> 3.7.7) ha scartato il resolver ACME per acme.json a 660 (3.x pretende 600) -> cert self-signed su tutto il VPS -> DeribitRead SSLError -> book online=False. Zero trade persi (target flat, gate di sicurezza OK). Fix: chmod 600 acme.json + restart; pin traefik:3.7 nel compose. Diario con diagnosi, verifica end-to-end e runbook. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,108 @@
|
|||||||
|
# 2026-07-09 — Incident infra: "BOOK LIVE conto offline" = cert Traefik di default (non un problema del conto)
|
||||||
|
|
||||||
|
**Tipo:** incident operativo (infra VPS), non ricerca. Nessun edge, nessun cambio a strategie/pesi/config
|
||||||
|
di trading. Documentato qui perché ha **bloccato la lettura del conto del book live** e ha prodotto
|
||||||
|
l'alert Telegram `⚠️ BOOK LIVE — conto offline`.
|
||||||
|
|
||||||
|
## Sintomo
|
||||||
|
|
||||||
|
Alert dal `cron_book` orario (`scripts/live/book_execute.py --execute`):
|
||||||
|
|
||||||
|
```
|
||||||
|
⚠️ BOOK LIVE — conto offline
|
||||||
|
nota: salto l'esecuzione, non opero a cieco
|
||||||
|
```
|
||||||
|
|
||||||
|
Nel log (`logs/cron_book.log`) le run recenti mostravano `conto non leggibile (offline) -> stop, non
|
||||||
|
eseguo a cieco`. 10 run offline su 382 totali → **regressione recente**, non uno stato storico.
|
||||||
|
|
||||||
|
## Diagnosi (catena completa)
|
||||||
|
|
||||||
|
Il book legge il conto Deribit mainnet in sola lettura via **Cerbero MCP** (`DeribitRead`,
|
||||||
|
`src/live/deribit.py` → `https://cerbero-mcp.tielogic.xyz`). `book_report` marca `online=False` quando
|
||||||
|
`DeribitRead.mark_price(BTC)` lancia. Riproduzione diretta:
|
||||||
|
|
||||||
|
```
|
||||||
|
mark_price BTC: FAIL -> SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] self-signed certificate
|
||||||
|
```
|
||||||
|
|
||||||
|
Il proxy presentava il **cert self-signed di default di Traefik** (`CN = TRAEFIK DEFAULT CERT`,
|
||||||
|
`notBefore Jul 9 19:55 2026`) al posto del vero Let's Encrypt. `curl -k` → HTTP 401 (backend
|
||||||
|
`cerbero-mcp` healthy, routing OK): **solo la presentazione del cert TLS era rotta**. `requests`
|
||||||
|
verifica il cert → fallisce → `online=False` → il book salta l'esecuzione (comportamento di sicurezza
|
||||||
|
**corretto**: non opera a cieco).
|
||||||
|
|
||||||
|
**Causa a monte** (log di avvio Traefik, 11:37:16Z, container `traefik-traefik-1`):
|
||||||
|
|
||||||
|
```
|
||||||
|
ERR The ACME resolve is skipped from the resolvers list
|
||||||
|
error="unable to get ACME account: permissions 660 for /acme.json are too open, please use 600"
|
||||||
|
resolver=mytlschallenge
|
||||||
|
→ (a cascata) Router uses a nonexistent certificate resolver certificateResolver=mytlschallenge
|
||||||
|
routerName=cerbero-mcp@docker [+ ~14 altri router: git, vault, portfolio, budget, cerbero-bite, ...]
|
||||||
|
```
|
||||||
|
|
||||||
|
Sequenza:
|
||||||
|
1. Immagine `traefik:latest` + **Watchtower** (`com.centurylinklabs.watchtower.enable=true`) →
|
||||||
|
auto-upgrade a **Traefik 3.7.7** con restart alle 11:37 di oggi.
|
||||||
|
2. Traefik 3.x **rifiuta** `acme.json` con permessi più larghi di `600`; il file era `660`
|
||||||
|
(`-rw-rw---- root:adriano`) → **resolver `mytlschallenge` scartato**.
|
||||||
|
3. Tutti i ~15 router che lo referenziano → "nonexistent certificate resolver" → Traefik serve il
|
||||||
|
**cert di default self-signed** a ogni host HTTPS del VPS (non solo cerbero-mcp).
|
||||||
|
4. I cert veri erano **già dentro `acme.json`** (14 domini, incl. `cerbero-mcp.tielogic.xyz`) → nessuna
|
||||||
|
ri-emissione necessaria, **zero rischio rate-limit** Let's Encrypt.
|
||||||
|
|
||||||
|
## Impatto
|
||||||
|
|
||||||
|
- **VPS-wide:** ogni servizio HTTPS dietro Traefik serviva un cert invalido (warning browser, fallimento
|
||||||
|
di ogni client che verifica il TLS). Non solo il trading.
|
||||||
|
- **Trading:** **nessun trade perso.** Target del book **flat** (TP01/SKH01 risk-off) + conto reale
|
||||||
|
~$598 (non finanziato al livello nominale): anche online il book avrebbe fatto HOLD. Il doppio gate
|
||||||
|
di sicurezza ha lavorato come previsto.
|
||||||
|
|
||||||
|
## Fix
|
||||||
|
|
||||||
|
**Immediato** (eseguito dall'utente):
|
||||||
|
```bash
|
||||||
|
sudo chmod 600 /opt/docker/traefik/acme.json
|
||||||
|
docker restart traefik-traefik-1
|
||||||
|
```
|
||||||
|
Il resolver ricarica i cert già presenti in `acme.json` e li ripresenta subito.
|
||||||
|
|
||||||
|
**Durevole** (pin dell'immagine, `/opt/docker/traefik/docker-compose.yml`):
|
||||||
|
```yaml
|
||||||
|
- image: "traefik" # = traefik:latest → Watchtower salta ai major (causa dell'incident)
|
||||||
|
+ image: "traefik:3.7" # Watchtower resta su 3.7.x: patch sì, salto a 3.8+ breaking no
|
||||||
|
```
|
||||||
|
Applicato con `docker compose up -d` (container ricreato su `traefik:3.7`, 20:05:05Z).
|
||||||
|
|
||||||
|
## Verifica (end-to-end, post-fix)
|
||||||
|
|
||||||
|
| Check | Risultato |
|
||||||
|
|---|---|
|
||||||
|
| `acme.json` perms | `600` |
|
||||||
|
| Cert `cerbero-mcp.tielogic.xyz` | issuer **Let's Encrypt** (fino 2026-09-27) |
|
||||||
|
| Traefik log | nessun `ACME resolve is skipped` / `too open` |
|
||||||
|
| Container image | `traefik:3.7` (running) |
|
||||||
|
| `DeribitRead.mark_price` | BTC 63.193 / ETH 1.746 |
|
||||||
|
| `book_report.online` | **True** · `eq_basis=mainnet USDC` · `real_equity=$598.06` · `pos_error=None` |
|
||||||
|
|
||||||
|
## Lezioni
|
||||||
|
|
||||||
|
- **`traefik:latest` + Watchtower = auto-upgrade non deterministico.** Un bump di major (qui il giro
|
||||||
|
di vite sui permessi di `acme.json`) diventa un'interruzione TLS di tutto il VPS senza intervento
|
||||||
|
umano. Pinnare la minor (`3.7`) rende i restart prevedibili pur ricevendo le patch.
|
||||||
|
- **Traefik 3.x pretende `acme.json` a `600`** (non solo warning: **scarta il resolver**). Se il file
|
||||||
|
è group/other-readable, tutti i router cadono sul cert di default.
|
||||||
|
- **Il gate `online` del book ha fatto il suo lavoro:** un problema *infra di lettura* non ha prodotto
|
||||||
|
ordini a cieco. L'alert "conto offline" va letto prima come possibile problema di **connettività/cert
|
||||||
|
al Cerbero MCP**, non del conto Deribit.
|
||||||
|
- **Runbook rapido** per "BOOK LIVE conto offline":
|
||||||
|
1. `python -c "from src.live.deribit import DeribitRead; DeribitRead().mark_price('BTC-PERPETUAL')"`
|
||||||
|
→ se `SSLError`/`CERTIFICATE_VERIFY_FAILED` è il cert del proxy, non il conto.
|
||||||
|
2. `openssl s_client -connect cerbero-mcp.tielogic.xyz:443 -servername cerbero-mcp.tielogic.xyz | openssl x509 -noout -issuer`
|
||||||
|
→ se `TRAEFIK DEFAULT CERT`, Traefik non serve il cert reale.
|
||||||
|
3. `docker logs traefik-traefik-1 | grep -iE "ACME resolve is skipped|too open|nonexistent"`
|
||||||
|
→ conferma resolver scartato; controlla `stat -c %a /opt/docker/traefik/acme.json` (deve essere `600`).
|
||||||
|
|
||||||
|
Nessun file di trading toccato; `config/live.json`, sleeve e pesi **invariati**.
|
||||||
Reference in New Issue
Block a user