SKILL: dashboard-remote-proxy
Trigger: Use when the dashboard service (bound to loopback) rejects remote access (e.g., via Tailscale/VPN) with a 400 Invalid Host header error.
Goal: Allow secure, authenticated-proxy access to a locally-bound dashboard service without compromising the dashboard's internal Host header security checks.
Rationale
The dashboard service implements a security check for the Host header to prevent DNS rebinding or unauthorized cross-origin requests. It strictly validates that incoming requests use a trusted hostname (usually localhost or 127.0.0.1).
When a user accesses the dashboard via a remote IP (e.g., 100.x.x.x:9119), the browser sends Host: 100.x.x.x, causing the dashboard to reject the request with 400 Invalid Host header.
Instead of disabling the dashboard's internal security, we introduce a lightweight Nginx reverse proxy layer that intercepts the request and rewrites the header to localhost before forwarding it to the dashboard.
Implementation (Nginx)
The following configuration should be placed in /etc/nginx/conf.d/dashboard.conf:
nginx
server {
listen 9119;
server_name _;
location / {
# Proxy to the dashboard service on loopback
proxy_pass http://127.0.0.1:9009;
# CRITICAL: Rewrite Host header to satisfy dashboard security check
proxy_set_header Host localhost;
# Forward real client info for logging/audit
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Pass through WebSockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Verification Protocol
To verify the fix works as intended, execute the following from the host:
-
Test Direct Loopback:
curl -I http://127.0.0.1:9119/
Expected: HTTP 200 OK
-
Test Spoofed Host (Remote Access Simulation):
curl -I -H "Host: 100.92.9.40" http://127.0.0.1:9119/
Expected: HTTP 200 OK (The Nginx proxy correctly rewrites the header, masking the spoofed Host)
Pitfalls & Notes
- Avoid Socat: Do not use socat for this bridge. socat performs a raw stream forward and cannot modify HTTP headers. Nginx is required for the proxy_set_header manipulation.
- WebSocket Compatibility: If the dashboard relies on WebSockets, ensure the proxy passes the Upgrade and Connection headers (as shown in the config snippet above).
- Security: This implementation relies on the dashboard process itself to handle authentication for the incoming request, even if the proxy itself is open on the port.
SKILL: dashboard-remote-proxy
Trigger: Use when the dashboard service (bound to loopback) rejects remote access (e.g., via Tailscale/VPN) with a 400 Invalid Host header error.
Goal: Allow secure, authenticated-proxy access to a locally-bound dashboard service without compromising the dashboard's internal Host header security checks.
Rationale
The dashboard service implements a security check for the Host header to prevent DNS rebinding or unauthorized cross-origin requests. It strictly validates that incoming requests use a trusted hostname (usually localhost or 127.0.0.1).
When a user accesses the dashboard via a remote IP (e.g., 100.x.x.x:9119), the browser sends Host: 100.x.x.x, causing the dashboard to reject the request with 400 Invalid Host header.
Instead of disabling the dashboard's internal security, we introduce a lightweight Nginx reverse proxy layer that intercepts the request and rewrites the header to localhost before forwarding it to the dashboard.
Implementation (Nginx)
The following configuration should be placed in /etc/nginx/conf.d/dashboard.conf:
nginx
server {
listen 9119;
server_name _;
}
Verification Protocol
To verify the fix works as intended, execute the following from the host:
Test Direct Loopback:
curl -I http://127.0.0.1:9119/
Expected: HTTP 200 OK
Test Spoofed Host (Remote Access Simulation):
curl -I -H "Host: 100.92.9.40" http://127.0.0.1:9119/
Expected: HTTP 200 OK (The Nginx proxy correctly rewrites the header, masking the spoofed Host)
Pitfalls & Notes