Repository navigation
v0.26.0
v0.26.0
This release fixes a number of security issues found during a security review. Some of the fixes change the
behavior of the proxy in ways that may require config changes. Please read the breaking changes below before
upgrading.
⚠️ Breaking changes
- PKI server certificates must be allowed explicitly. Users can no longer request server certificates for
arbitrary DNS names. The newserverCertificatesoption ofpkilists the DNS names (or one-label wildcards)
that can be requested, and optionally which users or groups can request them. Without it, only client
certificates can be issued. For example:pki: - name: "EXAMPLE CA" endpoint: https://pki-internal.example.com/certs serverCertificates: - dnsNames: - "*.internal.example.com" acl: - bob@example.com
jwksUrimust be an https URL intrustedIssuers.- Bearer tokens and ID token cookies must have an
expclaim. Tokens issued by TLSPROXY always have one. - The Host header must match one of the backend's
serverNames. This was already enforced forHTTPand
HTTPSbackends, but only after authentication. It is now checked first, and also forLOCALandCONSOLE
backends. Requests with a different Host header get421 Misdirected Request. - Forwarded headers are set by the proxy.
X-Forwarded-HostandX-Forwarded-Protoare now set on forwarded
requests, and the client'sForwarded,X-Forwarded-Host, andX-Forwarded-Protoheaders are removed. Client
headers with the same name asX-Forwarded-For,X-Forwarded-Client-Cert,X-tlsproxy-user-id, or any
forwardHttpHeaderskey, ignoring case and with_instead of-(e.g.X_tlsproxy_user_id), are removed.
forwardHttpHeaderscan still override or removeX-Forwarded-HostandX-Forwarded-Proto. - Backends can't set TLSPROXY's cookies.
Set-Cookieheaders from backends forTLSPROXYAUTH,
TLSPROXYIDTOKEN,TLSPROXYNONCE,TLSPROXYSAMLNONCE, or__tlsproxySidare dropped. - SSH certificate authority:
- DSA keys are rejected.
- A
ttlthat is not a positive number of seconds is rejected with400 Bad Request. Larger values are still
capped atmaximumCertificateLifetime.
- Login links expire. The links used on the login, logout, and permission denied pages expire after a week,
and must point to the same backend. - Device authorization page. The device verification page now has two steps: after entering the user code,
the page shows which client is requesting access and with which scopes, before the user can approve or deny the
request. - Local OIDC server:
- The
redirect_urisent to the token endpoint must be the same as in the authorization request. - When the client sends a PKCE
code_challenge, the token request must include a matchingcode_verifier. - An authorization request must be approved by the same user who made it.
- The
- Passkeys:
- The origin of passkey assertions is checked, like it already was for registrations.
- Authenticators with a signature counter must increase it on every login. Authenticators that don't implement
a counter are not affected.
- Connection limits:
- The TLS handshake timeout is reduced from 2 minutes to 30 seconds.
- Connections and requests that would wait more than 30 seconds for
forwardRateLimitare rejected instead of
waiting indefinitely. - Each stream of a non-HTTP/3 QUIC connection counts toward
forwardRateLimit. - The plain HTTP server (
httpAddr) has 10-second timeouts, and accepts at most 1000 concurrent connections, or
maxOpenif it is lower. - At most 50000 pending logins are kept per identity provider, and the local OIDC server keeps at most 10000
pending authorization or device authorization requests. URLs longer than 4096 bytes can't be used as the
destination of a login.
- ACLs: an ACL entry that is the name of a group is no longer also compared to the user's email address.
tlsclient --ocspverifies OCSP responses against the verified certificate chain, and rejects responses
signed by certificates that aren't authorized to sign OCSP responses.
🔒 Security fixes
- SSO rules and exceptions were matched against the raw request path. A path like
/public/../adminmatched an
exception for/public/, and was then forwarded as/adminwithout authentication. - Bearer tokens signed by any trusted issuer's key were accepted as if they had been issued by TLSPROXY.
- The SSH certificate authority issued certificates that never expire when given a negative or very large
ttl. - Forwarding a request to a backend with HTTP/3 crashed the proxy.
- ID tokens issued for one backend could be used on another backend with a different Host header.
- OCSP responses, stapled or fetched, could be signed by any certificate issued by the same CA, including the
revoked certificate itself. Fetched responses weren't checked for the serial number or freshness. - Any PKI user could get a server certificate for any DNS name.
- The plain HTTP server had no timeouts, and idle connections could use up all the available file descriptors.
- Unauthenticated device authorization requests could make the local OIDC server very slow.
- Open redirects after login and after setting the ID token cookie.
- SAML responses weren't tied to the browser that started the login, which allowed an attacker to log a victim
into the attacker's account. - Client-supplied headers could spoof the user identity header for CGI-style backends.
- The scopes required by built-in endpoints (pki, ssh, etc) weren't checked when no SSO rule matched the path.
- The CSRF check was skipped for any request with an
Authorizationheader, even when cookies were used for
authentication. - Passkey logins were accepted from other origins, and the existence of passkeys for an email address could be
detected. - A user could approve another user's OIDC authorization request and get tokens despite the client's ACL.
- Revoking a certificate issued by the built-in PKI didn't close connections already using it.
- The built-in PKI could serve a CRL that didn't include the latest revocations.
- Memory use could be increased without limit with ECH client hellos with arbitrary server names, and with
pending login requests.
🌟 New features
- New
maxOpenPerIPoption to limit the number of open connections from a single client IP address. There is no
limit by default. - New
serverCertificatesoption forpki(see above). - The local OIDC server supports PKCE (RFC 7636), and advertises
code_challenge_methods_supported.
🔧 Misc
- Hardening:
- Constant-time comparison of OIDC client secrets.
- Client-provided server names are quoted in QUIC logs.
- More secrets are redacted from the config shown on the metrics page: static
forwardHttpHeadersvalues and
ECH webhook URLs. proxy.mjsonly sends the CSRF token to the same origin.- A warning is logged when
--passphraseis used.$TLSPROXY_PASSPHRASEshould be used instead.
- Bug fixes:
- Data races on the ECH keys and in the PKI's OCSP/CRL signing certificate rotation.
- Panic on
/.sso/for backends without SSO. - Possible panic when re-authorizing connections after a backend was removed.
- The session chain claim (
th) wasn't carried over after an OIDC login. - The ephemeral certificate manager (for testing) keeps a bounded number of certificates in memory, and reissues
expired certificates.
- New tests for all the changes above.
- Update go dependencies:
- upgraded github.com/ProtonMail/go-crypto v1.4.1 => v1.5.2
- upgraded github.com/beevik/etree v1.7.1 => v1.8.1
- upgraded github.com/c2FmZQ/ech v0.4.2 => v0.4.3
- upgraded github.com/c2FmZQ/tlsproxy/jwks v0.0.0-20260902181210-9e31d44e7146 => v0.0.0-20261007172857-b4d54d07896c
- upgraded github.com/fxamacker/cbor/v2 v2.9.3 => v2.9.6
- upgraded github.com/quic-go/quic-go v0.62.0 => v0.63.0
- upgraded golang.org/x/crypto v0.56.0 => v0.57.0
- upgraded golang.org/x/net v0.58.0 => v0.59.0
- upgraded golang.org/x/sys v0.47.0 => v0.48.0
- upgraded golang.org/x/text v0.41.0 => v0.42.0
- upgraded golang.org/x/time v0.15.0 => v0.16.0