I'm trying to use the traefik-v2 (alpha7) passthrough feature with docker. My complete sample is here, but I will post the details below.
I ultimately want to run an identity provider called keycloak locally with TLS, as this is required in the OpenID Connect spec. The sample express server is much simpler to diagnose and resolve the proxy problems i am seeing.
I have one container called node-ssl-helloworld, which is a simple node express server that listens on port 8888 using SSL (TLS).
- I have created a Certificate Local CA, and registered this with my system
- I have also created an SSL certificate using the CA from step 1, with the name demo.local. I have updated my hosts file as follows:
# Host Database /etc/hosts
#
# localhost is used to configure the loopback interface
# when the system is booting. Do not change this entry.
##
127.0.0.1 demo.local
255.255.255.255 broadcasthost
::1 localhost
fe80::1%lo0 localhost
- My node ssl server app uses the certificate in step 2.
- Running the server in straight docker mapping port 8888:8888 (after building it) allows me to connect using demo.local:8888/helloworld -- my chrome browser indicates that the SSL connection is fine and the certificate OK.
- Running the following command:
$ curl -iv -k demo.local:8888/helloworld
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to demo.local (127.0.0.1) port 8888 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* error setting certificate verify locations, continuing anyway:
* CAfile: rootSSL.pem
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: C=IN; ST=State; L=City; O=Organization; OU=OrganizationUnit; CN=demo; emailAddress=demo@example.com
* start date: Jun 24 20:06:57 2019 GMT
* expire date: Jun 23 20:06:57 2021 GMT
* issuer: C=CA; ST=Ontario; L=Toronto; O=TestSSL; OU=n/a; CN=Local Certificate; emailAddress=demo@demo.com
* SSL certificate verify ok.
> GET /helloworld HTTP/1.1
> Host: demo.local:8888
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< X-Powered-By: Express
X-Powered-By: Express
< Content-Type: text/html; charset=utf-8
Content-Type: text/html; charset=utf-8
< Content-Length: 12
Content-Length: 12
< ETag: W/"c-ZIpqb//9qgutsjuLr5C2Fo3Razo"
ETag: W/"c-ZIpqb//9qgutsjuLr5C2Fo3Razo"
< Date: Wed, 26 Jun 2019 00:26:00 GMT
Date: Wed, 26 Jun 2019 00:26:00 GMT
< Connection: keep-alive
Connection: keep-alive
<
Hello World
* Connection #0 to host demo.local left intact
gets you the expected result - like in the browser, everything looks good.
First is my docker-compose.yml
version: "3"
services:
ssl-helloworld:
container_name: ssl-helloworld
image: ssl-helloworld:latest
deploy:
replicas: 1
resources:
limits:
cpus: "0.1"
memory: 50M
restart_policy:
condition: on-failure
# enabling this will make things
# ssl work, but the traffic is not
# going through traefik then
# ports:
# - "8888:8888"
networks:
- webnet
labels:
- traefik.backend=ssl-helloworld
- traefik.frontend.rule=Host:demo.local
- traefik.docker.network=webnet
- traefik.port=8888
- traefik.enable=true
traefik:
container_name: traefik
image: traefik:v2.0.0-alpha7
restart: always
ports:
- "8888:8888"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.toml:/traefik.toml
- /tmp:/tmp # for logging
networks:
- webnet
networks:
webnet:
external : true
So we just have traefik, and my ssl helloworld service. Traefik is configured with the traefik.toml file, which is listed below
debug = true
logLevel = "DEBUG"
[Log]
filePath = "/tmp/traefik.log"
format = "json"
[accessLog]
filePath = "/tmp/access.log"
format = "json"
#defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.traefik]
address = ":8080"
[entryPoints.http]
address = ":80"
[entryPoints.https]
address = ":8888"
[tcp]
[tcp.routers.to-ssl-helloworld]
rule = "HostSNI(`*`)"
service = "ssl-helloworld"
[tcp.routers.to-ssl-helloworld.tls]
passthrough = true
[https]
[https.routers]
[https.routers.ssl-helloworld]
rule = "Host(`demo.local`)"
service = "ssl-helloworld"
# playing with this does not help
# [[tls]]
# [tls.certificate]
# certFile = "/tmp/server.crt"
# keyFile = "/tmp/server.key"
# [tlsStores]
# [tlsStores.default]
# [tlsStores.default.defaultCertificate]
# certFile = "/tmp/server.crt"
# keyFile = "/tmp/server.key"
[api]
entryPoint = "traefik"
dashboard = true
[web]
address = ":8080"
[retry]
[docker]
endpoint = "unix:///var/run/docker.sock"
domain = "demo.local"
watch = true
exposedByDefault = false
Running docker-compose up -d brings up both the reverse proxy and my ssl server.
Expected behavior
$ curl -iv -k https: //demo.local:8888/helloworld
returns the same result as before, as we are presumably doing SSL passthrough directly. I also would expect that the non TLS-handshake traffic goes to the service and that the result string comes back as normal.
Actual behavior
The following is returned by the command
$ curl -iv -k https://demo.local:8888/helloworld
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to demo.local (127.0.0.1) port 8888 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=TRAEFIK DEFAULT CERT
* start date: Jun 26 00:42:22 2019 GMT
* expire date: Jun 25 00:42:22 2020 GMT
* issuer: CN=TRAEFIK DEFAULT CERT
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7fb654004a00)
> GET /helloworld HTTP/2
> Host: demo.local:8888
> User-Agent: curl/7.54.0
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
< HTTP/2 404
HTTP/2 404
< content-type: text/plain; charset=utf-8
content-type: text/plain; charset=utf-8
< x-content-type-options: nosniff
x-content-type-options: nosniff
< content-length: 19
content-length: 19
< date: Wed, 26 Jun 2019 00:42:59 GMT
date: Wed, 26 Jun 2019 00:42:59 GMT
<
404 page not found
* Connection #0 to host demo.local left intact
- The server certificate is a treafik self-generated one, no my server's certificate!
- Getting a 404, so presumably the traffic does not get routed to the container.
If you can offer any advice about how I can get this to work, that would be appreciated. Feel free to create a branch on the repo and push it.