From 6c889f156173d3c8d23122357fb8fe39f4533734 Mon Sep 17 00:00:00 2001 From: Gilles Soulier Date: Sat, 30 May 2026 10:07:00 +0200 Subject: [PATCH] =?UTF-8?q?fix(mcp+alembic):=20d=C3=A9sactive=20DNS=20rebi?= =?UTF-8?q?nding=20(421)=20+=20recha=C3=AEne=20migrations=20006?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MCP : - FastMCP recevait Host=localhost (sans port) mais le pattern par défaut allowed_hosts=["localhost:*", ...] EXIGE un port → 421 Invalid Host header pour tout accès non-localhost (ex: Hermes via http://10.0.0.50:3001/mcp) - Désactive enable_dns_rebinding_protection : le Bearer MCP_API_KEY est la vraie barrière (protection rebinding = anti-attaque navigateur, inutile ici) - nginx /mcp : retour à Host $host (le rewrite localhost était cassé) Alembic : - Collision : 006_notes_urls et 006_product_tags partageaient revision='006' → "Multiple head revisions" au démarrage - Renumérote notes_urls en 0061, chaîné après product_tags Chaîne finale : 005 -> 006 (product_tags) -> 0061 (notes_urls) -> 007 v0.5.15 Co-Authored-By: Claude Sonnet 4.6 --- .../{006_notes_urls.py => 0061_notes_urls.py} | 13 ++++++++----- ...007_shopping_list_type_and_item_enrichment.py | 4 ++-- backend/app/api/mcp_server.py | 16 +++++++++++++++- frontend/nginx.conf | 2 +- frontend/package.json | 2 +- 5 files changed, 27 insertions(+), 10 deletions(-) rename backend/alembic/versions/{006_notes_urls.py => 0061_notes_urls.py} (56%) diff --git a/backend/alembic/versions/006_notes_urls.py b/backend/alembic/versions/0061_notes_urls.py similarity index 56% rename from backend/alembic/versions/006_notes_urls.py rename to backend/alembic/versions/0061_notes_urls.py index 49b2adc..60cc7f4 100644 --- a/backend/alembic/versions/006_notes_urls.py +++ b/backend/alembic/versions/0061_notes_urls.py @@ -1,16 +1,19 @@ -"""006 - ajout colonne urls (JSONB) sur notes.items +"""0061 - ajout colonne urls (JSONB) sur notes.items -Revision ID: 006 -Revises: 005 +Revision ID: 0061 +Revises: 006 Create Date: 2026-05-30 + +Note : renumérotée 0061 (au lieu de 006) pour résoudre une collision avec +006_product_tags. Chaînée après product_tags : 005 -> 006 -> 0061 -> 007. """ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects.postgresql import JSONB -revision = '006' -down_revision = '005' +revision = '0061' +down_revision = '006' branch_labels = None depends_on = None diff --git a/backend/alembic/versions/007_shopping_list_type_and_item_enrichment.py b/backend/alembic/versions/007_shopping_list_type_and_item_enrichment.py index c843130..c23aef5 100644 --- a/backend/alembic/versions/007_shopping_list_type_and_item_enrichment.py +++ b/backend/alembic/versions/007_shopping_list_type_and_item_enrichment.py @@ -1,7 +1,7 @@ """007 - list_type sur shopping.lists, url/description/image_url sur list_items Revision ID: 007 -Revises: 006 +Revises: 0061 Create Date: 2026-05-30 """ @@ -9,7 +9,7 @@ from alembic import op import sqlalchemy as sa revision = '007' -down_revision = '006' +down_revision = '0061' branch_labels = None depends_on = None diff --git a/backend/app/api/mcp_server.py b/backend/app/api/mcp_server.py index 2ab8f65..f76244b 100644 --- a/backend/app/api/mcp_server.py +++ b/backend/app/api/mcp_server.py @@ -4,6 +4,7 @@ from datetime import datetime, timedelta, timezone, date as date_type from decimal import Decimal from mcp.server.fastmcp import FastMCP +from mcp.server.transport_security import TransportSecuritySettings from sqlalchemy import select, and_, text, or_ from sqlalchemy.orm import selectinload @@ -15,7 +16,20 @@ from app.models.shopping import ShoppingList, ListItem, Product _VALID_STATUSES = {"pending", "done", "cancelled"} _VALID_PRIORITIES = {"low", "medium", "high"} -mcp = FastMCP("HomeHub", stateless_http=True, streamable_http_path="/") +# La protection DNS rebinding (défaut FastMCP) valide le header Host contre +# ["127.0.0.1:*", "localhost:*", "[::1]:*"]. Elle est conçue contre les attaques +# navigateur sur des services localhost. Ici l'accès se fait depuis des agents +# externes (Hermes) via l'IP du serveur, et la vraie barrière est le Bearer token +# MCP_API_KEY (cf. MCPAuthMiddleware). On désactive donc cette protection devenue +# redondante et bloquante (sinon 421 "Invalid Host header" sur toute IP non-localhost). +mcp = FastMCP( + "HomeHub", + stateless_http=True, + streamable_http_path="/", + transport_security=TransportSecuritySettings( + enable_dns_rebinding_protection=False, + ), +) def _serialize(obj): diff --git a/frontend/nginx.conf b/frontend/nginx.conf index c95d5e9..2e99e20 100644 --- a/frontend/nginx.conf +++ b/frontend/nginx.conf @@ -27,7 +27,7 @@ server { location /mcp { proxy_pass http://backend:8000/mcp; - proxy_set_header Host localhost; + proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_http_version 1.1; proxy_set_header Connection ""; diff --git a/frontend/package.json b/frontend/package.json index d4869ed..f26a67a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "homehub-frontend", "private": true, - "version": "0.5.14", + "version": "0.5.15", "type": "module", "scripts": { "dev": "vite",