feat(server): split agent mTLS and browser HTTP listeners (VS-62) #84

Merged
Claude-Run merged 1 commit from feat/VS-62-two-listener-agent-mtls-browser-http into main 2026-06-27 20:13:55 +02:00
Member

Bind two listeners with independent TLS modes instead of one socket whose TLS mode was a global either/or. A dedicated agent listener (default 0.0.0.0:15443, configurable via --agent-bind / VERVAIN_AGENT_BIND) presents vervain's own server cert AND requires + verifies a client cert chaining to the dedicated agent CA (VS-63) via a rustls WebPkiClientVerifier, serving only the agent control + relay routes. The browser listener keeps its existing per-listener TLS mode (terminate or offload behind Traefik) and serves the SPA, the browser API, and the token-based enrollment endpoint, which stays reachable without a client cert because the enrolling agent has none yet.

The agent listener comes up alongside the browser listener whenever its address differs from the browser bind, so the typical offload deployment runs both (browser HTTP :15080, agent mTLS :15443). When the two binds are equal (the default terminate-mode single port) the agent routes stay on the main listener and no separate mTLS listener is started, preserving the single-port path.

Remove the insecureSkipVerify re-encrypt workaround: the ProxyCertCache / VERVAIN_CERT_URL / settings.tls.certUrl path and the handshake's extra-server-hash acceptance it fed are deleted, since agents now reach a direct mTLS port instead of seeing a reverse proxy's cert. ServerConfig gains agent_bind / agent_cert_pem / agent_key_pem and ServerHandle drives both listeners' shutdown in lockstep.

Add an integration test that proves, at the TLS layer, that the agent listener rejects a client presenting no cert, accepts an agent-CA-issued client cert, and that the browser listener serves /health without a client cert.

#VS-62

Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

Bind two listeners with independent TLS modes instead of one socket whose TLS mode was a global either/or. A dedicated agent listener (default 0.0.0.0:15443, configurable via --agent-bind / VERVAIN_AGENT_BIND) presents vervain's own server cert AND requires + verifies a client cert chaining to the dedicated agent CA (VS-63) via a rustls WebPkiClientVerifier, serving only the agent control + relay routes. The browser listener keeps its existing per-listener TLS mode (terminate or offload behind Traefik) and serves the SPA, the browser API, and the token-based enrollment endpoint, which stays reachable without a client cert because the enrolling agent has none yet. The agent listener comes up alongside the browser listener whenever its address differs from the browser bind, so the typical offload deployment runs both (browser HTTP :15080, agent mTLS :15443). When the two binds are equal (the default terminate-mode single port) the agent routes stay on the main listener and no separate mTLS listener is started, preserving the single-port path. Remove the insecureSkipVerify re-encrypt workaround: the ProxyCertCache / VERVAIN_CERT_URL / settings.tls.certUrl path and the handshake's extra-server-hash acceptance it fed are deleted, since agents now reach a direct mTLS port instead of seeing a reverse proxy's cert. ServerConfig gains agent_bind / agent_cert_pem / agent_key_pem and ServerHandle drives both listeners' shutdown in lockstep. Add an integration test that proves, at the TLS layer, that the agent listener rejects a client presenting no cert, accepts an agent-CA-issued client cert, and that the browser listener serves /health without a client cert. #VS-62 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(server): split agent mTLS and browser HTTP listeners (VS-62)
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 1m0s
Create release / Create release from merged PR (pull_request) Has been skipped
07be55f83b
Bind two listeners with independent TLS modes instead of one socket whose TLS mode was a global either/or. A dedicated agent listener (default 0.0.0.0:15443, configurable via --agent-bind / VERVAIN_AGENT_BIND) presents vervain's own server cert AND requires + verifies a client cert chaining to the dedicated agent CA (VS-63) via a rustls WebPkiClientVerifier, serving only the agent control + relay routes. The browser listener keeps its existing per-listener TLS mode (terminate or offload behind Traefik) and serves the SPA, the browser API, and the token-based enrollment endpoint, which stays reachable without a client cert because the enrolling agent has none yet.

The agent listener comes up alongside the browser listener whenever its address differs from the browser bind, so the typical offload deployment runs both (browser HTTP :15080, agent mTLS :15443). When the two binds are equal (the default terminate-mode single port) the agent routes stay on the main listener and no separate mTLS listener is started, preserving the single-port path.

Remove the insecureSkipVerify re-encrypt workaround: the ProxyCertCache / VERVAIN_CERT_URL / settings.tls.certUrl path and the handshake's extra-server-hash acceptance it fed are deleted, since agents now reach a direct mTLS port instead of seeing a reverse proxy's cert. ServerConfig gains agent_bind / agent_cert_pem / agent_key_pem and ServerHandle drives both listeners' shutdown in lockstep.

Add an integration test that proves, at the TLS layer, that the agent listener rejects a client presenting no cert, accepts an agent-CA-issued client cert, and that the browser listener serves /health without a client cert.

#VS-62

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
David deleted branch feat/VS-62-two-listener-agent-mtls-browser-http 2026-06-28 00:51:38 +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!84
No description provided.