TCP catch all only working with TLS

I'm trying to expose coturn TCP over Traefik (latest version 3.7.5). Yet I've hit a wall where Traefik will only forwards TLS traffic.

Naturally I've set up a TCP router (with TLS passthrough since coturn runs TCP/TLS and raw TCP on the same port) and service on the container with the following labels:

traefik.enable=true
traefik.tcp.routers.coturn.entrypoints=stun
traefik.tcp.routers.coturn.rule=HostSNI(`*`)
traefik.tcp.routers.coturn.service=coturn
traefik.tcp.routers.coturn.priority=1
traefik.tcp.routers.coturn.tls.passthrough=true
traefik.tcp.routers.coturns.entrypoints=stunsecure
traefik.tcp.routers.coturns.rule=HostSNI(`*`)
traefik.tcp.routers.coturns.service=coturn
traefik.tcp.routers.coturns.priority=1
traefik.tcp.routers.coturns.tls.passthrough=true
traefik.tcp.services.coturn.loadbalancer.server.port=5555
traefik.tcp.services.coturn.loadbalancer.serversTransport=ppv2@file

I've double and triple checked this and am like 99.99% sure there isn't a configuration issue here. (I've turned TLS on and off, changes priority, switched fron HostSNI to ClientIP disabled one of the two entrypoints, etc. and it didn't change)

The ppv2 is defined like this:

tcp:
  serversTransports:
    ppv2:
      proxyProtocol:
        version: 2

And my static config looks like this:

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entrypoint:
          to: "websecure"
          scheme: "https"
  
  websecure:
    address: ":443"
    allowACMEByPass: true
    http3:
      advertisedPort: '443'
    http:
      tls:
        certResolver: letsencrypt
      encodedCharacters:
        allowEncodedSlash: false
        allowEncodedBackSlash: false
        allowEncodedNullCharacter: false
        allowEncodedSemicolon: false
        allowEncodedPercent: false
        allowEncodedQuestionMark: false
        allowEncodedHash: false
    transport:
      respondingTimeouts:
        readTimeout: 0
        writeTimeout: 0
        idleTimeout: 0
    forwardedHeaders:
      trustedIPs:
       - 10.0.0.0/8
       - 172.16.0.0/12
       - 192.168.0.0/16

  admin:
    address: ":8000"

  stun:
    address: ":3478/tcp"

  stunsecure:
    address: ":5349/tcp"

log:
  format: common
  level: TRACE

accessLog:
  format: json
  filePath: "/var/log/traefik/access.log"

api:
  dashboard: true

providers:
  docker:
    exposedByDefault: false
    watch: true
  file:
    filename: "/etc/traefik/dynamic.yml"

certificatesResolvers:
  letsencrypt:
    acme:
      email: mail@expunged.internal
      tlsChallenge: {}

serversTransports:
  forwardingTimeouts:
    responseHeaderTimeout: "0s"
    idleConnTimeout: "0s"

There are HTTP routers running (that are working) but I've verified through dashboard and API that none of them are using the stun and stunsecure entrypoints.

In all cases the logs show that the TCP router and passthrough are being created. Also the access logs show a connection being made and Traefik respond yet crucially it does so with no router selected and so it responds with a HTTP 404.

Now, this happens ONLY when doing a bare connection (i.e. testing via Telnet or HTTP). When doing a HTTPS connection (so TLS) the logs say that a TCP connection was created, I get no 404 but rather a browser timeout (as I should since it's not HTTP) and the Coturn logs show a successfull (well, as successfull as HTTPS to a stun server can be) connection.

I've tried pretty much everything and can't find my mistake. How the hell can Traefik route successfully with TLS but not without it? I know HostSNI can't handle specific domains without TLS but this here is a catchall. Also the same error occured when using ClientIP(`0.0.0.0/0`) so it's not HostSNI that's wrong here.

What am I missing? Is this a bug? WTH?

Just to note that this is lowest priority:

HostSNI(`*`) is used as wildcard. For it to work, you don’t need TLS enabled. TCP forwards the full traffic to target without TLS enabled, try to remove .tls.passthrough=true.

It seems strange that you forward non-secure and secure connections to the same target port. Can the target service handle both on the same port? Can it handle ProxyProtocol, which is used for some connections?

What does Traefik debug log tell you, maybe trace is a bit too much?

Update: maybe instead of TCP you actually need UDP (some very old doc).

UDP is already handled via a Firewall forward. Only TCP is used with Traefik since Coturn only supports proxy protocol using TCP.

Yes, it is strange for Coturn to accept traffic for both TLS and non TLS on the same port but that is how it works. Even without proxy protocol it's noted in the docs that Coturn accepts TLS on the non TLS and non TLS on the TLS port. When enabling proxy protocol it always opens only one port for both.

Also increasing the priority to 10 and removing the TLS passthrough actually worked. I could swear I've tried both individually and they didn't work. Maybe it was a combination?

I'm just worried about the following: Are you sure Traefik will not terminate TLS now? I need Coturn to handle TLS itself, that's why I used the passthrough.

If you don’t enable TLS on the entrypoint or router, Traefik will just ignore it and treat it as a simple TCP data connection, forwarding every byte.

oh ok. I was under the impression that Traefik would always terminate TLS if not explicitely told to passthrough