feat(safety+audit+deploy): consistency_check + audit log file sink + deploy script
#2 Env switch safety: - mcp_common/environment.py: nuova consistency_check() che previene switch accidentali a mainnet. Solleva EnvironmentMismatchError se resolved=mainnet senza creds["environment"]="mainnet" esplicito, o se declared/resolved mismatch. Override via STRICT_MAINNET=false. - Wirato in app_factory.run_exchange_main al boot. - 6 nuovi test consistency. #3 Audit log persistence: - mcp_common/audit.py: TimedRotatingFileHandler aggiuntivo se env AUDIT_LOG_FILE settato. Rotation midnight UTC, retention 30gg default (AUDIT_LOG_BACKUP_DAYS). Format JSONL con SecretsFilter. - docker-compose.prod.yml: bind mount /var/log/cerbero-mcp + env AUDIT_LOG_FILE per i 4 servizi exchange (write endpoints). - 2 nuovi test file sink. #1 Deploy script: - scripts/deploy.sh: idempotente, fa docker login + clone/pull repo + copia secrets chmod 600 + crea .env + setup audit dir + pull image + up + smoke test pubblico HTTPS. - DEPLOYMENT.md aggiornato: sezioni 2 (script), 3 (safety mainnet), 4 (audit log query), renumber sezioni successive. Test: 488/488 verdi. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Executable
+165
@@ -0,0 +1,165 @@
|
||||
#!/usr/bin/env bash
|
||||
# Cerbero_mcp — deploy script per VPS produzione.
|
||||
#
|
||||
# Pre-requisiti sul VPS (NON gestiti da questo script):
|
||||
# 1. Docker Engine ≥ 24 + plugin docker compose installati.
|
||||
# 2. DNS A record `cerbero-mcp.tielogic.xyz` → IP del VPS.
|
||||
# 3. Porte 80 e 443 aperte sul firewall (per ACME + traffico HTTPS).
|
||||
# 4. PAT Gitea con scope `read:package`, salvato in env `$GITEA_PAT`.
|
||||
# 5. Username Gitea in env `$GITEA_USER` (default: adriano).
|
||||
# 6. Secret JSON exchange + token bearer disponibili in $SECRETS_SRC
|
||||
# (default: ~/cerbero-secrets/), che lo script copierà in
|
||||
# $DEPLOY_DIR/secrets/ con permessi 600.
|
||||
#
|
||||
# Idempotente: rieseguibile per aggiornamenti.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
DEPLOY_DIR="${DEPLOY_DIR:-/opt/cerbero-mcp}"
|
||||
SECRETS_SRC="${SECRETS_SRC:-$HOME/cerbero-secrets}"
|
||||
GITEA_USER="${GITEA_USER:-adriano}"
|
||||
GITEA_REPO_URL="${GITEA_REPO_URL:-ssh://git@git.tielogic.xyz:222/Adriano/Cerbero-mcp.git}"
|
||||
REGISTRY="${REGISTRY:-git.tielogic.xyz}"
|
||||
DOMAIN="${DOMAIN:-cerbero-mcp.tielogic.xyz}"
|
||||
AUDIT_LOG_DIR="${AUDIT_LOG_DIR:-/var/log/cerbero-mcp}"
|
||||
|
||||
echo "=== Cerbero_mcp deploy → $DEPLOY_DIR (domain $DOMAIN) ==="
|
||||
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
# 1. Verifica pre-requisiti
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
command -v docker >/dev/null || { echo "FATAL: docker non installato"; exit 1; }
|
||||
docker compose version >/dev/null || { echo "FATAL: docker compose plugin assente"; exit 1; }
|
||||
|
||||
if [ -z "${GITEA_PAT:-}" ]; then
|
||||
echo "FATAL: env GITEA_PAT non settata. Export del PAT con scope read:package prima."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$SECRETS_SRC" ]; then
|
||||
echo "FATAL: secrets src dir $SECRETS_SRC non esiste."
|
||||
echo " Atteso contenere: deribit.json bybit.json hyperliquid.json alpaca.json"
|
||||
echo " macro.json sentiment.json core.token observer.token"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check DNS resolution (warning only, non blocca)
|
||||
ip_resolved=$(getent hosts "$DOMAIN" | awk '{print $1}' | head -1 || true)
|
||||
if [ -z "$ip_resolved" ]; then
|
||||
echo "WARN: $DOMAIN non risolve via DNS — TLS Let's Encrypt fallirà finché DNS non propaga."
|
||||
else
|
||||
echo "DNS $DOMAIN → $ip_resolved"
|
||||
fi
|
||||
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
# 2. Login al container registry
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
echo "=== docker login $REGISTRY ==="
|
||||
echo "$GITEA_PAT" | docker login "$REGISTRY" -u "$GITEA_USER" --password-stdin
|
||||
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
# 3. Setup dir + clone/pull repo
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
sudo mkdir -p "$DEPLOY_DIR"
|
||||
sudo chown "$USER:$USER" "$DEPLOY_DIR"
|
||||
|
||||
if [ -d "$DEPLOY_DIR/.git" ]; then
|
||||
echo "=== Aggiornamento repo $DEPLOY_DIR ==="
|
||||
git -C "$DEPLOY_DIR" pull --ff-only
|
||||
else
|
||||
echo "=== Clone repo $GITEA_REPO_URL → $DEPLOY_DIR ==="
|
||||
git clone "$GITEA_REPO_URL" "$DEPLOY_DIR"
|
||||
fi
|
||||
|
||||
cd "$DEPLOY_DIR"
|
||||
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
# 4. Copia secrets con permessi 600
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
mkdir -p secrets
|
||||
echo "=== Copia secrets da $SECRETS_SRC ==="
|
||||
for f in deribit.json bybit.json hyperliquid.json alpaca.json macro.json sentiment.json core.token observer.token; do
|
||||
if [ -f "$SECRETS_SRC/$f" ]; then
|
||||
cp "$SECRETS_SRC/$f" "secrets/$f"
|
||||
chmod 600 "secrets/$f"
|
||||
echo " ok: secrets/$f"
|
||||
else
|
||||
echo " WARN: $SECRETS_SRC/$f assente — il servizio relativo fallirà al boot."
|
||||
fi
|
||||
done
|
||||
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
# 5. Crea/aggiorna .env (preserva esistente)
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
if [ ! -f .env ]; then
|
||||
echo "=== Creazione .env iniziale (testnet di default) ==="
|
||||
cat > .env <<EOF
|
||||
# Cerbero_mcp deploy config — modifica per passare a mainnet
|
||||
ACME_EMAIL=adrianodalpastro@tielogic.com
|
||||
GATEWAY_HTTP_PORT=80
|
||||
GATEWAY_HTTPS_PORT=443
|
||||
WRITE_ALLOWLIST="127.0.0.1/32 ::1/128 172.16.0.0/12"
|
||||
|
||||
IMAGE_TAG=latest
|
||||
IMAGE_PREFIX=git.tielogic.xyz/adriano/cerbero-mcp
|
||||
|
||||
# Environment exchange (true=testnet, false=mainnet).
|
||||
# IMPORTANTE: per mainnet aggiungi anche "environment":"mainnet" al secret JSON
|
||||
# corrispondente, altrimenti il boot abortisce per safety (vedi consistency_check).
|
||||
DERIBIT_TESTNET=true
|
||||
BYBIT_TESTNET=true
|
||||
HYPERLIQUID_TESTNET=true
|
||||
ALPACA_PAPER=true
|
||||
|
||||
# Permette mainnet senza creds["environment"]="mainnet" esplicito (sconsigliato).
|
||||
STRICT_MAINNET=true
|
||||
|
||||
# Audit log persistente per write endpoint (place_order, cancel, ecc.).
|
||||
AUDIT_LOG_DIR=$AUDIT_LOG_DIR
|
||||
|
||||
# Watchtower polling auto-update (sec).
|
||||
WATCHTOWER_POLL_INTERVAL=300
|
||||
EOF
|
||||
echo " $DEPLOY_DIR/.env creato. Rivedi prima del primo up."
|
||||
else
|
||||
echo "=== .env preesistente — non sovrascritto ==="
|
||||
fi
|
||||
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
# 6. Audit log dir host (volume bind)
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
sudo mkdir -p "$AUDIT_LOG_DIR"
|
||||
sudo chown 1000:1000 "$AUDIT_LOG_DIR" # uid del container app
|
||||
echo "Audit log dir: $AUDIT_LOG_DIR (chown 1000:1000)"
|
||||
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
# 7. Pull image + up
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
echo "=== docker compose pull + up ==="
|
||||
docker compose -f docker-compose.prod.yml --env-file .env pull
|
||||
docker compose -f docker-compose.prod.yml --env-file .env up -d
|
||||
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
# 8. Verifica stato
|
||||
# ──────────────────────────────────────────────────────────────
|
||||
sleep 5
|
||||
echo "=== Stato container ==="
|
||||
docker compose -f docker-compose.prod.yml --env-file .env ps
|
||||
|
||||
echo
|
||||
echo "=== Smoke test (health check via gateway pubblico) ==="
|
||||
sleep 10 # tempo per Caddy di richiedere cert
|
||||
if curl -sf -o /dev/null -m 10 "https://$DOMAIN/mcp-macro/health"; then
|
||||
echo " ✅ https://$DOMAIN/mcp-macro/health → 200"
|
||||
else
|
||||
echo " ⚠️ https://$DOMAIN/mcp-macro/health non risponde (DNS o cert non ancora pronti?)"
|
||||
echo " Riprova fra 30s o controlla: docker compose -f docker-compose.prod.yml logs gateway"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "=== Deploy completato ==="
|
||||
echo "Comandi utili:"
|
||||
echo " Logs: docker compose -f docker-compose.prod.yml --env-file .env logs -f <service>"
|
||||
echo " Audit: tail -f $AUDIT_LOG_DIR/*.audit.jsonl"
|
||||
echo " Restart: docker compose -f docker-compose.prod.yml --env-file .env restart <service>"
|
||||
echo " Stop: docker compose -f docker-compose.prod.yml --env-file .env down"
|
||||
Reference in New Issue
Block a user