Duo self-hosted verifier WebSocket check reports 403 against a healthy endpoint

Everyone can contribute. Help move this issue forward while earning points, leveling up and collecting rewards.

Summary

The WebSocket connectivity check in gitlab:duo:verify_self_hosted_setup (added in !243425 (merged)) rejects itself. It sends Host: <host>:<port> with the scheme's default port and Origin: <external_url> without one, and Workhorse accepts the upgrade only when the Origin host equals the Host header. On any instance whose external_url has no explicit port the check prints WebSocket endpoint rejected the connection (HTTP 403) and tells the operator to check their Duo seat, licence, or proxy.

Steps to reproduce

  1. Self-managed instance with external_url set without a port (for example https://gitlab.example.com), Duo self-hosted configured and working.
  2. Run gitlab-rake gitlab:duo:verify_self_hosted_setup.
  3. Read the WebSocket section of the output.

What is the current bug behavior?

>> WebSocket endpoint rejected the connection (HTTP 403) ✗

agent_platform_websocket.status: ERROR, http_status: 403. Rails logs 200 for the same request; the 403 comes from the Workhorse upgrade.

What is the expected correct behavior?

The check reports the upgrade as accepted (101) when the endpoint is healthy.

Relevant logs and/or screenshots

Reproduced on omnibus 19.3.1-ee. Replaying the check's handshake from the instance with curl: Host with the port plus Origin without it returns 403; dropping the port from Host, adding it to Origin, or omitting Origin each return 101.

Possible fixes

The headers are built at https://gitlab.com/gitlab-org/gitlab/-/blob/ec55628e9adfd92f1055485d13f2367d5d8b67a3/ee/lib/gitlab/duo/administration/agent_platform_websocket_check.rb#L106-111. Build the host header once, omitting the default port the way browsers do, and derive Origin from it. I have a one-method fix with specs ready to open against this issue.

Edited by 🤖 GitLab Bot 🤖