Traefik v2 is my current reverse-proxy of choice - mostly because of the good integration with docker. This setup is for a non-terminating-TLS TCP proxy - ie. TLS is being terminated by something other than traefik, so traefik does not need a certificate for the proxied domain, and the proxied server is functioning as if it were directly on the internet.

Why do this? I've got an internet-facing server running traefik (called "TheProxyHost"), and another server (called "TheOtherHost") without a public IP on which I am doing some development work - I want TheOtherHost to be able to fetch its own Let's Encrypt certificate and serve traffic as if it were directly on the internet (where it will eventually be, after development is done). There is no special config on TheOtherHost - as far as it is concerned, it is receiving traffic directly from clients.

This document was tested with Traefik v2.4.8 and assumes that you already have a basic working setup (lots of online tutorials for the basics). There needs to be a DNS record pointing TheOtherHost.example.org to the IP of TheProxyHost, so that our clients out there on the internet can find us (on TheProxyHost). We're assuming that our client apps are smart enough to use SNI to tell TheProxyHost which domain they are trying to connect to.

There are 2 things we need in our traefik config - a router specifying what incoming traffic we want to proxy, and a service defining where that traffic is to be sent. Mine are defined in a dynamic provider file at /etc/traefik/conf.d/TheOtherHost.yaml on TheProxyHost (the host with the public IP):

tcp:
  routers:
    TheOtherHost:
      # the following line has the domain we want to proxy "TheOtherHost.example.org"
      rule: "HostSNI(`TheOtherHost.example.org`)"
      # the following line needs to match the name of the Service in the next stanza
      service: "TheOtherHost"
      # will route TLS requests (and ignore non tls requests)
      tls:
        passthrough: true

  services:
    TheOtherHost:
      loadBalancer:
        servers:
          # specify the address/port of TheOtherHost
          - address: "10.0.0.100:443"

The dynamic file provider config is specified in traefik docker-compose.yml (on the proxying host, not TheOtherHost) like this:

---SNIP---
services:
  traefik:
    image: "traefik:v2.4.8"
---SNIP---
    command:
---SNIP---
      # https://doc.traefik.io/traefik/providers/file/
      - "--providers.file.directory=/etc/traefik/conf.d"
      # watch for changes
      - "--providers.file.watch=true"
---SNIP---

The conf.d directory is volume-mounted into the container on TheProxyHost, like this in docker-compose.yml:

volumes:
  - "/srv/traefik/conf.d:/etc/traefik/conf.d:ro"

References