How to connect to Traefik TCP Services with TLS configuration enabled?
Asked Answered
L

2

17

I am trying to configure Traefik so that I would have access to services via domain names, and that I would not have to set different ports. For example, two MongoDB services, both on the default port, but in different domains, example.localhost and example2.localhost. Only this example works. I mean, other cases probably work, but I can't connect to them, and I don't understand what the problem is. This is probably not even a problem with Traefik.

I have prepared a repository with an example that works. You just need to generate your own certificate with mkcert. The page at example.localhost returns the 403 Forbidden error but you should not worry about it, because the purpose of this configuration is to show that SSL is working (padlock, green status). So don't focus on 403.

Only the SSL connection to the mongo service works. I tested it with the Robo 3T program. After selecting the SSL connection, providing the host on example.localhost and selecting the certificate for a self-signed (or own) connection works. And that's the only thing that works that way. Connections to redis (Redis Desktop Manager) and to pgsql (PhpStorm, DBeaver, DbVisualizer) do not work, regardless of whether I provide certificates or not. I do not forward SSL to services, I only connect to Traefik. I spent long hours on it. I searched the internet. I haven't found the answer yet. Has anyone solved this?

PS. I work on Linux Mint, so my configuration should work in this environment without any problem. I would ask for solutions for Linux.


If you do not want to browse the repository, I attach the most important files:

docker-compose.yml

version: "3.7"

services:
    traefik:
        image: traefik:v2.0
        ports:
            - 80:80
            - 443:443
            - 8080:8080
            - 6379:6379
            - 5432:5432
            - 27017:27017
        volumes:
            - /var/run/docker.sock:/var/run/docker.sock:ro
            - ./config.toml:/etc/traefik/traefik.config.toml:ro
            - ./certs:/etc/certs:ro
        command:
            - --api.insecure
            - --accesslog
            - --log.level=INFO
            - --entrypoints.http.address=:80
            - --entrypoints.https.address=:443
            - --entrypoints.traefik.address=:8080
            - --entrypoints.mongo.address=:27017
            - --entrypoints.postgres.address=:5432
            - --entrypoints.redis.address=:6379
            - --providers.file.filename=/etc/traefik/traefik.config.toml
            - --providers.docker
            - --providers.docker.exposedByDefault=false
            - --providers.docker.useBindPortIP=false

    apache:
        image: php:7.2-apache
        labels:
            - traefik.enable=true
            - traefik.http.routers.http-dev.entrypoints=http
            - traefik.http.routers.http-dev.rule=Host(`example.localhost`)
            - traefik.http.routers.https-dev.entrypoints=https
            - traefik.http.routers.https-dev.rule=Host(`example.localhost`)
            - traefik.http.routers.https-dev.tls=true
            - traefik.http.services.dev.loadbalancer.server.port=80
    pgsql:
        image: postgres:10
        environment:
            POSTGRES_DB: postgres
            POSTGRES_USER: postgres
            POSTGRES_PASSWORD: password
        labels:
            - traefik.enable=true
            - traefik.tcp.routers.pgsql.rule=HostSNI(`example.localhost`)
            - traefik.tcp.routers.pgsql.tls=true
            - traefik.tcp.routers.pgsql.service=pgsql
            - traefik.tcp.routers.pgsql.entrypoints=postgres
            - traefik.tcp.services.pgsql.loadbalancer.server.port=5432
    mongo:
        image: mongo:3
        labels:
            - traefik.enable=true
            - traefik.tcp.routers.mongo.rule=HostSNI(`example.localhost`)
            - traefik.tcp.routers.mongo.tls=true
            - traefik.tcp.routers.mongo.service=mongo
            - traefik.tcp.routers.mongo.entrypoints=mongo
            - traefik.tcp.services.mongo.loadbalancer.server.port=27017
    redis:
        image: redis:3
        labels:
            - traefik.enable=true
            - traefik.tcp.routers.redis.rule=HostSNI(`example.localhost`)
            - traefik.tcp.routers.redis.tls=true
            - traefik.tcp.routers.redis.service=redis
            - traefik.tcp.routers.redis.entrypoints=redis
            - traefik.tcp.services.redis.loadbalancer.server.port=6379

config.toml

[tls]
[[tls.certificates]]
certFile = "/etc/certs/example.localhost.pem"
keyFile = "/etc/certs/example.localhost-key.pem"

Build & Run

mkcert example.localhost # in ./certs/
docker-compose up -d

Prepare step by step

  1. Install mkcert (run also mkcert -install for CA)
  2. Clone my code
  3. In certs folder run mkcert example.localhost
  4. Start container by docker-compose up -d
  5. Open page https://example.localhost/ and check if it is secure connection
  6. If address http://example.localhost/ is not reachable, add 127.0.0.1 example.localhost to /etc/hosts

Certs:

  • Public: ./certs/example.localhost.pem
  • Private: ./certs/example.localhost-key.pem
  • CA: ~/.local/share/mkcert/rootCA.pem

Test MongoDB

  1. Install Robo 3T
  2. Create new connection:
    • Address: example.localhost
    • Use SSL protocol
    • CA Certificate: rootCA.pem (or Self-signed Certificate)
  3. Test tool:

test

Test Redis

  1. Install RedisDesktopManager
  2. Create new connection:
    • Address: example.localhost
    • SSL
    • Public Key: example.localhost.pem
    • Private Key: example.localhost-key.pem
    • Authority: rootCA.pem
  3. Test tool:

test


So far:

  1. Can connect to Postgres via IP (info from Traefik)
jdbc:postgresql://172.21.0.4:5432/postgres?sslmode=disable

enter image description here

jdbc:postgresql://172.21.0.4:5432/postgres?sslfactory=org.postgresql.ssl.NonValidatingFactory

enter image description here


Try telet (IP changes every docker restart):

> telnet 172.27.0.5 5432
Trying 172.27.0.5...
Connected to 172.27.0.5.
Escape character is '^]'.
^]
Connection closed by foreign host.
> telnet example.localhost 5432
Trying ::1...
Connected to example.localhost.
Escape character is '^]'.
^]
HTTP/1.1 400 Bad Request
Content-Type: text/plain; charset=utf-8
Connection: close

400 Bad RequestConnection closed by foreign host.

If I connect directly to postgres, the data is nice. If I connect to via Traefik then I have Bad Request when closing the connection. I have no idea what this means and whether it must mean something.

Loux answered 10/12, 2019 at 9:51 Comment(8)
I can't connect to them -> how did you test it and what was the error?Illume
@JanGaraj I added a step-by-step instructionLoux
Connections to redis (Redis Desktop Manager) ... do not work, but screenshot shows Successful connection - ? Why you are not testing on low level with curl, openssl, telnet, ...? Why you are not testing with netstat if those app ports are really binded for traefik on 127.0.0.1 interface?Illume
Does container with traefik and databases run on same host ?Chessman
@RyabchenkoAlexander yes, in docker containersLoux
try jdbc:postgresql://pgsql:5432/postgres?sslmode=disableChessman
pgsql is service name inside docker network. Not visible outside.Loux
For the Bad Request behavior. It might be that HTTP router takes over if no match to TCP routers are found. (as stated in the doc)Meakem
D
4

At least for the PostgreSQL issue, it seems that the connection is started in cleartext and then upgraded to TLS:

So it is basically impossible to use TLS termination with a proxy if said proxy doesn't support this cleartext handshake + upgrade to TLS function of the protocol.

Dejadeject answered 29/1, 2020 at 1:34 Comment(0)
L
1

Update to @jose-liber's answer:

SNI routing for postgres with STARTTLS has been added to Traefik in this PR. Now Treafik will listen to the initial bytes sent by postgres and if its going to initiate a TLS handshake (Note that postgres TLS requests are created as non-TLS first and then upgraded to TLS requests), Treafik will handle the handshake and then is able to receive the TLS headers from postgres, which contains the SNI information that it needs to route the request properly. This means that you can use HostSNI("example.com") along with tls to expose postgres databases under different subdomains.

As of writing this answer, I was able to get this working with the v3.0.0-beta2 image (Reference)

Lynxeyed answered 13/1, 2023 at 8:53 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.