From ffc69011c4bafe469f1c6f7766f6983eab7d31ab Mon Sep 17 00:00:00 2001 From: nessi Date: Thu, 9 Jul 2026 14:21:11 +0200 Subject: [PATCH] feat: add reverse proxy support with X-Forwarded-Host header handling for agent installer URL generation Add external_base_url helper to detect base URL from X-Forwarded-Host and X-Forwarded-Proto headers with fallback to request.base_url, replace hardcoded request.base_url usage in node_agent_install, node_agent_install_info, and public_node_agent_install endpoints to support reverse proxy deployments, update nginx config to pass X-Forwarded-Host header and use $http_host instead of $host for proper hostname forw --- backend/app/api/v1/router.py | 14 +++++++++++--- nginx/default.conf | 6 +++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/backend/app/api/v1/router.py b/backend/app/api/v1/router.py index c1c88d1..f72ad44 100644 --- a/backend/app/api/v1/router.py +++ b/backend/app/api/v1/router.py @@ -658,6 +658,14 @@ def node_agent_payload(node: Node, agent: NodeAgent | None) -> dict: } +def external_base_url(request: Request) -> str: + forwarded_host = request.headers.get("x-forwarded-host") or request.headers.get("host") + forwarded_proto = request.headers.get("x-forwarded-proto") or request.url.scheme + if forwarded_host: + return f"{forwarded_proto}://{forwarded_host}".rstrip("/") + return str(request.base_url).rstrip("/") + + @api_router.get("/nodes/agents", response_model=list[NodeWithAgentRead]) def nodes_with_agents(_: CurrentUser, db: Session = Depends(get_db)) -> list[dict]: agents = {agent.node_id: agent for agent in db.scalars(select(NodeAgent)).all()} @@ -742,7 +750,7 @@ def node_agent_install(node_id: str, request: Request, user: CurrentUser, db: Se agent.install_count = (agent.install_count or 0) + 1 token = create_token(node.id, "agent", timedelta(days=365), {"node_name": node.name}) write_audit(db, action="agent.install.generated", object_type="node", object_id=node.id, user_id=user.id) - base_url = str(request.base_url).rstrip("/") + base_url = external_base_url(request) return agent_install_script(base_url, token, node) @@ -758,7 +766,7 @@ def node_agent_install_info(node_id: str, request: Request, user: CurrentUser, d agent.install_count = (agent.install_count or 0) + 1 install_token = create_token(node.id, "agent-install", timedelta(days=7), {"node_name": node.name}) write_audit(db, action="agent.install.generated", object_type="node", object_id=node.id, user_id=user.id) - base_url = str(request.base_url).rstrip("/") + base_url = external_base_url(request) install_url = f"{base_url}/api/v1/agents/install/{node.id}?token={install_token}" return { "node_id": node.id, @@ -779,7 +787,7 @@ def public_node_agent_install(node_id: str, token: str, request: Request, db: Se if not node: raise HTTPException(status_code=404, detail="Node not found") agent_token = create_token(node.id, "agent", timedelta(days=365), {"node_name": node.name}) - base_url = str(request.base_url).rstrip("/") + base_url = external_base_url(request) return agent_install_script(base_url, agent_token, node) diff --git a/nginx/default.conf b/nginx/default.conf index 1f9747a..f87cc37 100644 --- a/nginx/default.conf +++ b/nginx/default.conf @@ -4,9 +4,10 @@ server { location /api/ { proxy_pass http://api:8000/api/; - proxy_set_header Host $host; + proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; } @@ -16,7 +17,6 @@ server { location / { proxy_pass http://frontend:80; - proxy_set_header Host $host; + proxy_set_header Host $http_host; } } -