Skip to content

[Bug] --tls-client-cert flag is not used for API health check, breaking mTLS setups #69

@jock0

Description

@jock0

Describe the Bug

The --tls-client-cert flag on pangolin up client is documented and accepted by the CLI, but the certificate is not used for the initial API health check. As a result, when the Pangolin server is fronted by a reverse proxy (e.g. Traefik) configured with clientAuthType: RequireAndVerifyClientCert, the client always fails before the tunnel can be established.

Root cause

In internal/client/up.go (≈ line 270 in 0.8.0):

healthClient, err = api.InitClient(endpoint, "")
...
healthOk, healthErr := healthClient.CheckHealth()
if healthErr != nil || !healthOk {
    err := fmt.Errorf("the server appears to be down: %w", healthErr)
    ...
}

The healthClient is initialized via api.InitClient(endpoint, "") with no reference to opts.TlsClientCert. The TLS client certificate is only attached later, when building the TunnelConfig:

tunnelConfig := olmpkg.TunnelConfig{
    ...
    TlsClientCert: opts.TlsClientCert,
    ...
}

So the CheckHealth() call on the API client always runs without a client certificate. When the Pangolin server enforces mTLS, the TLS handshake fails with tls: certificate required and the CLI exits before the tunnel is ever started.

Environment

  • OS Type & Version: Linux Debian 12
  • Pangolin Version: 1.17.0
  • Traefik Version: v3.6.13
  • Client Version: pangolin up client (CLI 0.8.0) : Tested on 0.6.0 and 0.8.0 (linux_amd64).

To Reproduce

sudo /usr/local/bin/pangolin up \
  --id  \
  --secret  \
  --endpoint https://tunnel.example.com \
  --override-dns=false \
  --tls-client-cert /path/to/client.p12 \
  --log-level debug \
  --attach

Output:

A new version is available: 0.8.0 (current: 0.6.0)
Run 'pangolin update' to update to the latest version
Error: the server appears to be down: server unreachable: Get "https://tunnel.example.com/api/v1": remote error: tls: certificate required
Please check that the server is running and accessible.

Expected Behavior

When --tls-client-cert is provided, the certificate should be used for all TLS handshakes the client makes against the Pangolin server, including the initial CheckHealth() call and any other API calls performed during startup, not only the tunnel runtime.

Workarounds tried

  1. PKCS12 file generated with step certificate p12 ... --no-password --insecure — same error.
  2. Concatenated PEM bundle (cert + key) passed to --tls-client-cert — same error.
  3. Upgrade 0.6.0 → 0.8.0 via pangolin update — same error.

Impact

This makes mTLS-protected Pangolin admin/API endpoints unusable with the CLI client. The only current workaround on the deployment side is to remove mtls-strict from the api-router (and ws-router) entrypoints, which weakens the security posture by exposing the API to anyone who can reach the entrypoint.

Notes

  • The newt client (1.11.0) does not have this problem because its --tls-client-cert-file and --tls-client-key are honored both for the WebSocket connection and the token endpoint.
  • I would be happy to test a patched build if a fix is proposed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions