feat(web): resolve real client IP behind a trusted reverse proxy #102

Merged
Claude-Run merged 1 commit from feat/VS-83-trusted-proxy-client-ip into main 2026-07-03 20:11:30 +02:00
Member

Add a system-level trusted-proxy CIDR list (settings.tls.trustedProxies, env override VERVAIN_TRUSTED_PROXIES) parsed into ipnet::IpNet with precedence env > config > default empty, mirroring settings.tls.mode. When the immediate TCP peer is inside a trusted range, the server resolves the real client IP from X-Forwarded-For using the rightmost-untrusted rule; otherwise the forwarded header is ignored and the socket peer is used, so a client outside the trusted set cannot spoof its source IP. An empty list preserves today's socket-peer behavior byte-for-byte.

The resolver lives in vervain-config (resolve_client_ip, pure and unit-tested) and is exposed to the web layer as a ClientAddr axum extractor that reads ConnectInfo plus the header and consults AppState.trusted_proxies. All browser/HTTP-listener client-IP sites switch to it: grouprelay, enroll-bundle, CrowdSec bouncer, login rate limit, MCP tunnel, and the control channel. The agent mTLS listener handler keeps the raw peer because agents dial it directly and are never behind the HTTP proxy.

The setting is operator-only system config: it is not in RUNTIME_EDITABLE_SETTINGS_KEYS, so it is neither readable nor writable through the settings overlay web UI / REST API, matching settings.tls.mode. Documented alongside the existing TLS-offload docs in the README.

#VS-83

Add a system-level trusted-proxy CIDR list (settings.tls.trustedProxies, env override VERVAIN_TRUSTED_PROXIES) parsed into ipnet::IpNet with precedence env > config > default empty, mirroring settings.tls.mode. When the immediate TCP peer is inside a trusted range, the server resolves the real client IP from X-Forwarded-For using the rightmost-untrusted rule; otherwise the forwarded header is ignored and the socket peer is used, so a client outside the trusted set cannot spoof its source IP. An empty list preserves today's socket-peer behavior byte-for-byte. The resolver lives in vervain-config (resolve_client_ip, pure and unit-tested) and is exposed to the web layer as a ClientAddr axum extractor that reads ConnectInfo<SocketAddr> plus the header and consults AppState.trusted_proxies. All browser/HTTP-listener client-IP sites switch to it: grouprelay, enroll-bundle, CrowdSec bouncer, login rate limit, MCP tunnel, and the control channel. The agent mTLS listener handler keeps the raw peer because agents dial it directly and are never behind the HTTP proxy. The setting is operator-only system config: it is not in RUNTIME_EDITABLE_SETTINGS_KEYS, so it is neither readable nor writable through the settings overlay web UI / REST API, matching settings.tls.mode. Documented alongside the existing TLS-offload docs in the README. #VS-83
feat(web): resolve real client IP behind a trusted reverse proxy
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 2m10s
Create release / Create release from merged PR (pull_request) Has been skipped
b72cc6aa9b
Add a system-level trusted-proxy CIDR list (settings.tls.trustedProxies, env override VERVAIN_TRUSTED_PROXIES) parsed into ipnet::IpNet with precedence env > config > default empty, mirroring settings.tls.mode. When the immediate TCP peer is inside a trusted range, the server resolves the real client IP from X-Forwarded-For using the rightmost-untrusted rule; otherwise the forwarded header is ignored and the socket peer is used, so a client outside the trusted set cannot spoof its source IP. An empty list preserves today's socket-peer behavior byte-for-byte.

The resolver lives in vervain-config (resolve_client_ip, pure and unit-tested) and is exposed to the web layer as a ClientAddr axum extractor that reads ConnectInfo<SocketAddr> plus the header and consults AppState.trusted_proxies. All browser/HTTP-listener client-IP sites switch to it: grouprelay, enroll-bundle, CrowdSec bouncer, login rate limit, MCP tunnel, and the control channel. The agent mTLS listener handler keeps the raw peer because agents dial it directly and are never behind the HTTP proxy.

The setting is operator-only system config: it is not in RUNTIME_EDITABLE_SETTINGS_KEYS, so it is neither readable nor writable through the settings overlay web UI / REST API, matching settings.tls.mode. Documented alongside the existing TLS-offload docs in the README.

#VS-83
Claude-Run deleted branch feat/VS-83-trusted-proxy-client-ip 2026-07-03 20:11:30 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/vervain-server!102
No description provided.