Can traefik terminate an SSL connection and create a new SSL connection to the backend service

Trying to migrate from Nginx to Traefik. With Nginx you can create a new TLS connection to the backend.
As far as I can tell, Traefik only supports a clear text connection to the backend service.
This is a showstopper for us unfortunately as we like Traefik much better than Nginx in general.
From https://docs.traefik.io/routing/routers/#tls

When a TLS section is specified, it instructs Traefik that the current router is dedicated to HTTPS requests only (and that the router should ignore HTTP (non TLS) requests). Traefik will terminate the SSL connections (meaning that it will send decrypted data to the services).

1 Like

Hi @workasr, the documentation about TLS at the "Router" level is for the "incoming" part of the requests.

As you can read in https://docs.traefik.io/routing/overview/, the Traefik object you're looking for is a "Service" (https://docs.traefik.io/routing/services/), which represents the backend services (e.g. your applications behind traefik).

Depending on the kind of dynamic configuration provider (File, Docker, Kubernetes, etc.), there are different way to re-establish a new SSL/TLS connection to the backend (assuming that your backend service is serving a valid TLS certificate chain. If not, let us know as the certificate or the CA authority has to be provided to Traefik, or TLS connection made insecure with insecureSkipVerify):

  • With file provider, set the url directive of the server(s) definition to https://<ip of backend>:443 for instance
  • With docker (engine or swarm), set the server.scheme label to the value https:
- "traefik.http.services.myservice.loadbalancer.server.scheme=http"

as per https://docs.traefik.io/routing/providers/docker/#services .

@dduportal Is this possible in principle on TCP routers?

I'm not sure (ping @juliens after you're back from kubecon :wink: ) but I would say it's not possible with TCP router:

  • TLS happens at Layer 7, and depends on the application protocol.
  • Traefik TCP routing with TLS only happens because, if (and only if) the layer-7 protocol uses standard TLS with handshake + supports SNI, then Traefik can somehow "peak" the first TCP packets and do the TLS handshake part.
  • Dialling with a backend in TCP seems (for a naive learner as I am but 50% chances I'm wrong) and initiating TLS would require some knowledge of the layer-7 protocol used.

Well it terminates TLS does not it? -no peeking involved. So from router to service it will be non-TLS. I was wondering if it is possible to re-encrypt it a similar way the HTTP(S) router can.

Can you give me all the examples you know of (I would be surprised if there will be more that you can count on one hand) of applications you can use traefik with in TCP/TLS mode (that is not HTTP)? @dduportal

Fair point :smiley:

I tried the following with success:

  • MongoDB
  • IMAP(S)
  • FTPS
  • POP(S)
    (haven't tried SMTP but should work as well)

I know these won't work (because no SNI support, or custom TLS with no handshake):

  • PostgreSQL
  • MySQL/MariaDB
  • SSH

=> I bet that the challenge of listing (TCP+TLS compliant protocols) is the same as with HaProxy and Nginx, but I'm not able to find a list of these that could be common. Maybe we should start one on the Traefik documentation, WDYT?

@dduportal
Hey there, I have a similar problem, maybe you could give me a hint since i read it like you were successful?

I am trying to achieve a working traefik 2.0 file provider configuration to permit client connections to a dockerized mailserver.
The server is currently using self signed certs for imaps access. Is it possible to use the self signed certs internally (from the docker container to the traefik endpoint) and let's encrypt certs from the traefik entrypoint to the client (I read an article that this should be the way to go)?

The config below works, but is using passtrough/self signed certs. I would like to leverage let's encrypt certs for the ssl connection, possible?

traefik.yml

log:
  level: DEBUG

serversTransport:
  insecureSkipVerify: true
  maxIdleConnsPerHost: 75

entryPoints:
  imaps:
    address: ":25600"

api:
  dashboard: true


providers:
  file:
    filename: "/etc/traefik/dynamic_conf.yml"
    watch: true 

certificatesResolvers:
  mydomain:
    acme:
      email: admin@mydomain.org
      storage: acme.json
      dnsChallenge:
        provider: exec
        delayBeforeCheck: 0

dynamic_conf.yml

tcp:
  routers:

    dovecot:
      entryPoints:
        - "imaps"
      service: dovecot
      rule: "HostSNI(`*`)"
      tls:
        passthrough: true

#not working: terminating TLS by default
#      rule: "HostSNI(`mail.mydomain.org`)"
#      tls: {}

#not working:
#      rule: "HostSNI(`mail.mydomain.org`)"
#      tls:
#        certResolver: mydomain

  services:

    dovecot:
      loadBalancer:
        terminationDelay: -1
        servers:
          - address: "dovecot:993"

@dduportal, @juliens - could you please confirm if this configuration (terminate SSL and create a new TCP TLS session with backend service) works with any provider? There have been multiple queries but not responses about this scenario :frowning:

So, if you use the HTTP part of Traefik, you can tell Traefik to "decrypt" TLS (add tls on the router) and then reencrypt in TLS when communicating with your backend (add protocol="https" on the service).

For TCP part, you can "decrypt" TLS (add tls on the router) but for now, you can't say that you want to communicate in TLS with your backend (may be one could create a proposal, if it doesn't exist yet).
But on TCP, you can set the passthrough options on the tls part of the router, with this options, Traefik will not "decrypt" TLS and it will just send the "crypted" data to the backend.

2 Likes

It seems this thread is what I've been looking for in solving my problem:

I need to:

  1. replace an incoming HTTP URL with an HTTPS URL
  2. I'm not sure if traefik needs to convert the message so the service running on HTTPS can read it
  3. when the docker service running on the HTTPS URL responds, I need traefik to convert it to HTTP so the remote server (expecting HTTP) understands it.

I've also described this in multiple ways in my thread because apparently it was hard to communicate it.
Examples in my thread.

@dduportal thoughts?

@juliens

Assuming an http decrypt/re-encrypt scenario as you suggested:

So, if you use the HTTP part of Traefik, you can tell Traefik to "decrypt" TLS (add tls on the router) and then reencrypt in TLS when communicating with your backend (add protocol="https" on the service).

Does traefik verify the certificate presented by the backend against a known CA or does it just accept any certificate it's presented?

Hello @kevdog,
You can define RootCAs https://doc.traefik.io/traefik/routing/overview/#rootcas or if you want to accept everything you can configure https://doc.traefik.io/traefik/routing/overview/#insecureskipverify.
Those configurations are in the static configuration, but in 2.4 (in RC for now) you will be able to configure it in the dynamic configuration, at the service level https://doc.traefik.io/traefik/master/routing/services/#serverstransport

1 Like

@juliens

Just a quick question regarding best implementation method. If I'm using LE certs on the backend servers I usually use the /etc/ssl/certs/ca-certificates.crl file to verify the LE CA root. I know including this rather large file is likely overkill, particularly if all I need is one RootCA from the file, however on the other hand I'm aware this certificates file is commonly updated.

Is it best to bind mount this file from the host within the traefik container, or create a Dockerfile and add alpine's ca-certificates package and use alpine's version? Is there a preferred method here?? Usually if I have a custom CA I usually add it this ca-certificates.crt file via the update-certificates method anyway.

@juliens

Just a bigger question regarding Layer 7 re-encryption using the docker provider. Part of the issue with using TLS certs on the backend is the URL domain name needs to match the name on the certificate.

With the docker provider if you were to add something like:

"traefik.http.services.myservice.loadbalancer.server.scheme=https"
"traefik.http.services.myservice.loadbalancer.server.port=9980"

This is going to setup a service with endpoint of https://:9980

The IP address unless it was included as a SANS variable isn't going to match the name on the certificate. You mentioned a work-around-as using insecureskipverify as the means to get around this on a global level (unless running 2.4 whereby you could run an insecureskipverify on site to site basis rather than globally). For the docker provider I suppose a workaround would be to write a service within the dynamic config file and have the docker container reference the service name contained within the file -- this just gets kind of messy however and takes away from the dynamic abilities of using docker/traefik.