diff --git a/README.md b/README.md index 79eeb33..36acc42 100644 --- a/README.md +++ b/README.md @@ -12,19 +12,25 @@ Docker Compose deployment for Traefik used as a TCP load balancer with TLS passt ## Configuration -Edit backend targets in `config/dynamic/tcp-services.yml`: +Edit SNI routes and backend targets in `config/dynamic/tcp-services.yml`: ```yaml tcp: + routers: + lab-younerd: + rule: "HostSNI(`lab.younerd.org`)" + service: lab-younerd + tls: + passthrough: true + services: - ssl-backends: + lab-younerd: loadBalancer: servers: - - address: "192.0.2.10:443" - - address: "192.0.2.11:443" + - address: "host.docker.internal:8443" ``` -The example addresses use the documentation-only `192.0.2.0/24` range. Replace them with the real backend IP addresses and ports before deploying. If a backend listens on the Docker host itself, use `host.docker.internal:PORT`. +`HostSNI(...)` matches the hostname sent by the client during the TLS handshake. If a backend listens on the Docker host itself, use `host.docker.internal:PORT`. ## Run diff --git a/config/dynamic/tcp-services.yml b/config/dynamic/tcp-services.yml index 2087d0b..56a9b82 100644 --- a/config/dynamic/tcp-services.yml +++ b/config/dynamic/tcp-services.yml @@ -1,16 +1,28 @@ tcp: routers: - tls-passthrough: + lab-younerd: entryPoints: - tls - rule: "HostSNI(`*`)" - service: ssl-backends + rule: "HostSNI(`lab.younerd.org`)" + service: lab-younerd + tls: + passthrough: true + + azionelab: + entryPoints: + - tls + rule: "HostSNI(`azionelab.org`) || HostSNI(`www.azionelab.org`)" + service: azionelab tls: passthrough: true services: - ssl-backends: + lab-younerd: loadBalancer: servers: - - address: "192.0.2.10:443" - - address: "192.0.2.11:443" + - address: "host.docker.internal:8443" + + azionelab: + loadBalancer: + servers: + - address: "host.docker.internal:9443" diff --git a/docs/deployment.md b/docs/deployment.md index 0e69fb4..7a1ab2f 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -8,12 +8,22 @@ ## Configuration -Update `config/dynamic/tcp-services.yml` with the real backend addresses: +Update `config/dynamic/tcp-services.yml` with the required SNI hostnames and backend addresses: ```yaml -servers: - - address: "10.0.0.10:443" - - address: "10.0.0.11:443" +tcp: + routers: + example: + rule: "HostSNI(`example.org`)" + service: example + tls: + passthrough: true + + services: + example: + loadBalancer: + servers: + - address: "host.docker.internal:8443" ``` If a backend listens on the Docker host, use `host.docker.internal:PORT`. Compose maps that name through Docker's `host-gateway` feature. diff --git a/docs/operations.md b/docs/operations.md index bca3811..492dd66 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -54,4 +54,4 @@ For backends running on the Docker host, use `host.docker.internal:PORT` rather - TLS passthrough prevents Traefik from inspecting HTTP paths, headers, or certificate contents. - Backend certificates and renewal jobs must be monitored on each backend. -- The default configuration routes all SNI names to the same backend pool. Use more specific `HostSNI(...)` rules if different names need different pools. +- SNI routing depends on clients sending the expected hostname during the TLS handshake. Clients that connect by IP address or omit SNI will not match hostname-specific routers. diff --git a/docs/testing.md b/docs/testing.md index bc0b1ab..0be67b6 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -20,6 +20,14 @@ openssl s_client -connect LOAD_BALANCER_HOST:443 -servername EXPECTED_DNS_NAME The certificate shown by `openssl` should be the backend certificate, not a certificate stored by Traefik. +For the included SNI example: + +```bash +openssl s_client -connect LOAD_BALANCER_HOST:443 -servername lab.younerd.org +openssl s_client -connect LOAD_BALANCER_HOST:443 -servername azionelab.org +openssl s_client -connect LOAD_BALANCER_HOST:443 -servername www.azionelab.org +``` + ## Test categories - Docker/Compose validation: `docker compose config`.