Docker-ssl-passthrough problem

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).

  1. I have created a Certificate Local CA, and registered this with my system
  2. 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

  1. My node ssl server app uses the certificate in step 2.
  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.
  3. 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
  1. The server certificate is a treafik self-generated one, no my server's certificate!
  2. 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.

2 Likes

Hello,

You are trying to use Traefik v2 with configuration (TOML and labels) of the Traefik v1, but configuration of Traefik v1 is not supported by Traefik v2.

Please read the documentation about Traefik v2:

Hello Idez,

so the right configuration in this case how would be for v2.0?

I have the same situation and Im having some hard time to figure this out,

basically what I need is an end to end SSL from Traefik to the backend service.

Thanks,
Eriol.

I also need SSL HTTP passthrough. I've studied the docs and only found passthrough for TCP. How can we stop traefik serving its default cert for a container and let the container take care of it itself?

1 Like

I'm also looking for SSL PassThrough for HTTP. Have you found any solution yet ?

Seems your service accepts only HTTP while passthrough works only for TCP. I have the same problem.

got it:

    labels:
      - "traefik.enable=true"
      - "traefik.tcp.routers.myservice.rule=HostSNI(`my.domain`)"
      - "traefik.tcp.routers.myservice.tls.passthrough=true"
      - "traefik.tcp.routers.myservice.service=myservice"
      - "traefik.tcp.services.myservice.loadbalancer.server.port=443"
      - "traefik.tcp.routers.myservice.entrypoints=websecure"

the service/container should be listening on 443 for ssl

So I can't use any another port, only 443 and it will automatically recognize it? Right?

i only tried 443 but in theory you could use any. change:

- "traefik.tcp.services.myservice.loadbalancer.server.port=443"

accordingly

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteTCP
metadata:
  name: ca-ingress
  labels:
    app: ca
spec:
  entryPoints:
    - traefik
  routes:
  - match: HostSNI(`ca.SOMEDOMAIN.HERE`)
    services:
      - name: ca-service
        port: 7054
  tls:
    # secretName: ca-tls
    passthrough: true

I have this, but it does not work.

i dont recognise that format. im using v2

It's Ingress config for k8s, but basically will be converted to the same config as yours.
I still get 404 page not found

Hi @kopaygorodsky, can you provide a full reproduction case (I need the Deployment + Service + Ingress on one side, and the Traefik deployment on another side) please?
I see that your IngressRouteTCP is associated to the entry point traefik only: we cannot see the static configuration with the entrypoint definitions (poorts, etc.).

I'm also interested in the following elements to help you:

  • Traefik logs (all of them) in DEBUG mode
  • Result of the command curl --verbose --insecure https://ca.SOMEDOMAIN.HERE:<port of entrypoint traefik>/ ?