fosrl / fosrl/cli

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

Open
#69 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Go
Stars
49
Forks
18
Avg merge
2d 8h
Merged PRs (30d)
7

Description

### 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):

```go
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`:

```go
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

```bash
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.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.