Expose Postgres using Traefik V3

Hello!

I'm trying to expose postgres via Traefik.

The postgres service is running in the postgres namespace and exposes the port 5432.

In Traefik, I've done the following:

Created an entrypoint like this:

additionalArguments:
  - "--entryPoints.postgres.address=:5432/tcp"

I've also exposed the port like this:

ports:
  postgres:
    expose: true
    port: 5432
    exposedPort: 5432
    protocol: TCP
    tls:
      enabled: true

I've then created an IngressRouteTCP in the postgres namespace like this:

apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
  name: postgres
  labels:
    traefik.tcp.routers: "postgres"
spec:
  entryPoints:
    - postgres
  routes:
    - match: HostSNI(`*`)
      services:
        - name: postgresql-primary
          namespace: postgres
          port: 5432
  tls:
    secretName: local-my-domain-tls

However, this doesn't work. When looking in the Traefik dashboard, no TCP Routers or TCP Services are listed. I don't see any errors in the logs.

When trying to connect to the database I receive the following error SSL error: Remote host terminated the handshake.

So, I have two questions:

  1. Why doesn't the ingress show up in the dashboard under TCP Services/Routers?
  2. Have I missed anything? I was under the impression that adding the entrypoint, exposing the port and creating a IngressRouteTCP ingress should be enough?

I think that the entrypoint works because I can do the following from my computer:

nc -vz postgres.local.my.domain 5432
Connection to postgres.local.my.domain port 5432 [tcp/postgresql] succeeded!

If I remove the entrypoint, netcat fails.
So the problem seems to be with the communication between Traefik and the Postgres service?

Did found a solution for this problem?

Andre

What's your issue, what's the error message on client and Traefik side?

It seems Postgres supports regular TLS, you should be able to force TLS on client side with sslmode=require. You should specify HostSNI(`domain`) in Traefik config, so LetsEncrypt can create a matching TLS certificate (if you haven't defined it otherwise).

If no domain is specified, Traefik will use a default TLS cert, which the database client will not trust, therefore probably throw an according error message.

@joseftw Thank you for this post! This helped me with my solve my issue exposing the postgres service via IngressRouteTCP which is only the first half of getting this working with TLS. I might've figured out the second half of this.

For the first half of the problem, exposing the postgres port, I had to tweak your answer for the expose due to updates to the Traefik Helm Chart, from:

postgress:
  ...
  expose: true

to:

  postgres:
    ...
    expose:
      default: true

This allowed me connect and test the port using:

nc -zv pg-authentik-rw.example.com 5432

The second hand of the problem has to deal with ALPN Protocol Mismatch. When trying to connect to my posgres instance from pgadmin, i was getting the following error:

connection failed: connection to server at "pg-authentik-rw.example.com", port 5432 failed: SSL error: tlsv1 alert no
application protocol

To solve this issue and be able to connect to postgres using TLS, we need to configure the TLSOption to remove ALPN Protocols.

First, we need to create a TLSOption resource in Kubernetes:

apiVersion: traefik.io/v1alpha1
kind: TLSOption
metadata:
  name: no-alpn
  namespace: authentik
spec:
  alpnProtocols: []

Setting the alpnProtocols: [] to an empty list allows us to remove the default options that are set with TLSOption.

Then in the IngressRouteTCP we need to add this option to the tls.options and reapply the changes

apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
  name: ingress-tcp-pg-authentik-rw
  namespace: authentik
  annotations: 
    kubernetes.io/ingress.class: traefik-external
spec:
  entryPoints:
    - postgres
  routes:
    - match: HostSNI(`pg-authentik-rw.example.com`)
      services:
        - name: pg-authentik-rw
          port: 5432
  tls:
    secretName: prod-example-com-tls
    options:
      name: no-alpn

This allowed me to connect to the postgres using pgadmin with SSL.

Additional Note - This uses a wildcard cert with letsencrypt to allow TLS to cloudnative-pg postgres instance, which is why I use HostSNI(`domain`) and NOT HostSNI(`*`) I believe the latter option is to make Traeffik as a passthrough and let the application handle TLS, with the passthrough: true option.