# Handoff 2026-09-01 — n8n-mcp SSRF-Block ✅ GEFIXT + in Portainer-Stack 174 gefaltet ## Kontext Aus TP10 (Terpenfinder Auto-Refresh, Repo `blaueblueten-n8n`) ausgelagerter bf-infra-Task: der n8n-MCP war einsatzfähig, aber seine Anbindung an die **n8n-API** war blockiert. Diese Session hat den Blocker behoben und die Container-Definition sauber verortet. ## Befund (Ausgangslage) - **Netz war nie das Problem:** aus dem `n8n-mcp`-Container erreichten **beide** Wege n8n — intern `http://n8n:5678` → HTTP 200, öffentlich `https://n8n.emsenhuber.net/healthz` → HTTP 200. - **Einziger Blocker = App-eigener SSRF-Guard** von n8n-mcp (v2.57.1 → nach Redeploy 2.77.0): `N8N_API_URL=http://n8n:5678` löst auf die private Docker-Bridge-IP `172.19.0.3` auf, und der Guard lehnte private IPs im Default-Modus `strict` ab (`SSRF protection: Private IP addresses not allowed`, `connected:false`). Der API-Client validiert `N8N_API_URL` über genau diesen Guard (`/app/dist/services/n8n-api-client.js:104` → `SSRFProtection.validateWebhookUrl`). - **SSoT-Drift (mit-gefixt):** der laufende Container war ein **Waise** aus `~/dev/homelab-cline/n8n-mcp-deploy/docker-compose.yml` (kein Git-Repo), und dieses File stimmte mit dem laufenden Container nicht überein (File: `https://n8n.emsenhuber.net` + Netz `web`; Container: `http://n8n:5678` + Netz `n8n_default`). `n8n`+`n8n-redis` liefen dagegen im Portainer-Stack **174** (`n8n`, Endpoint 20 = macmini). ## Fix (durchgeführt 2026-09-01) Entscheidung (User): **intern + permissive**, und **n8n-mcp in Portainer-Stack 174 falten**. 1. **SSRF entschärft:** `WEBHOOK_SECURITY_MODE=permissive` gesetzt → private/LAN-IP für `N8N_API_URL=http://n8n:5678` erlaubt. Bleibt auf der Docker-Bridge, **kein** CF/Tunnel/Caddy- Umweg, keine 10-s-DNS-Falle, unabhängig von der Edge. Vertretbar, weil dieser Container ausschließlich mit dem trusted LAN-n8n redet. 2. **Waise beseitigt:** orphan-Container entfernt (`docker rm -f n8n-mcp`), n8n-mcp als **dritter Service** in Stack 174 aufgenommen (neben `n8n`+`redis`, gemeinsames Default-Netz `n8n_default`, `depends_on: n8n`). Stack via Portainer-API redeployt (`PUT /api/stacks/174?endpointId=20`, `prune:false`). 3. **Erhalten geblieben:** Bearer (`AUTH_TOKEN`==`MCP_AUTH_TOKEN`, len 64) **unverändert** → bestehende Clients (opencode `http://10.40.20.167:3001/mcp`) laufen weiter; Host-Port-Bind `10.40.20.167:3001:3000` → Caddy-Route `n8n-mcp.emsenhuber.net` (Caddyfile Z. 75) intakt; Daten-Volume als **`external`** wiederverwendet (`n8n-mcp-deploy_n8n-mcp-data`, `nodes.db` bleibt) — eigener `N8N_API_KEY` von n8n-mcp (≠ n8n) unverändert. ## Verifikation (nicht behauptet — gemessen) - `SSRFProtection.validateWebhookUrl("http://n8n:5678")` im Container → `valid:true`, `resolved:172.19.0.3`, `mode:permissive`. - MCP-Tool `n8n_health_check` (voller Handshake) → `{"success":true,"status":"ok", "apiUrl":"http://n8n:5678","mcpVersion":"2.77.0","upToDate":true}`. - `n8n`+`n8n-redis` behielten Container-ID **und** `StartedAt` (2026-07-29) → **kein n8n-Downtime** beim Redeploy (nur der neue Service wurde erstellt). ## SSoT / Ablage - **Master der Stack-Definition = Portainer-Stack 174** (`n8n`, Endpoint 20, macmini). - Gitignored Repo-Mirror + Rollback: `docs/macmini-vlan20/stacks/174-n8n.yml` (neuer Stand) und `…/174-n8n.orig-pre-mcp-fold-20260901.yml` (Vorzustand). Neu gitignored: `docs/macmini-vlan20/stacks`. - INVENTORY-Eintrag `mcp/INVENTORY.md` (n8n-mcp) aktualisiert. ## Offene Punkte - 🔴 **TP30 Posten #8:** beim Ausgeben des Stack-174-Files ist `N8N_SMTP_PASS` (Gmail-App-PW `johnny.claw.0x00@gmail.com`) ins Transkript geraten (Redaktionsmuster hatte `PASSWORD`, nicht bare `PASS`). **Rotation fällig** — Google App-Passwort widerrufen + Stack-174-Env nachziehen. - ⚠️ **Toter Orphan:** `~/dev/homelab-cline/n8n-mcp-deploy/` (kein Git) kann gelöscht werden — ein erneutes `docker compose up` dort würde sonst einen Namenskonflikt mit dem Stack-174-Container erzeugen. Nicht kritisch (nur bei Handstart). - ⚠️ **Client-401 in THIS Claude-Code-Session:** der n8n-mcp-Server kam in dieser Session mit `AUTH_HEADER_REJECTED` (401) hoch — das ist ein **Client-Config-Thema** (`~/.claude.json`-Bearer leer/falsch, vgl. globale CLAUDE.md „kein Shell → `{env:}` leer"), **nicht** der hier gefixte Server. Der Server antwortet LAN-direkt korrekt; opencode ist unbetroffen. ## Auswirkung auf TP10 Keine für den Feature-Pfad: TP10 läuft über **Cloudflare Worker Cron**, nicht über n8n. Der Fix macht n8n-mcp aber wieder als **Werkzeug** nutzbar (n8n-Workflow-Validierung, künftig optional TP10-als-n8n-Workflow). Rückantwort ans zuweisende Projekt: `~/dev/blaueblueten-n8n/docs/rueckantwort-n8n-mcp-fix-20260901.md`.