Skip to content
3 min read

Reverse proxy & TLS

Traefik v3 as the single entry point: automatic TLS, security headers, Docker hardening.

ReliableMaintainableSecureHybrid

Context / need

A self-hosted platform runs Docker containers for services of very different natures: public client sites, private internal tools, APIs receiving webhooks. All of them must be served over HTTPS, each with the right exposure, without multiplying entry points or case-by-case configuration.

Constraints

Options considered

Decision & why

Traefik v3, with a fully declarative configuration (static files and Docker labels), versioned in Git. Exposing a new service comes down to a few labels on its container:

# Exposing a service: a few labels are enough (example domain)
services:
  app:
    image: ghcr.io/exemple/app:1.4.2
    networks: [proxy]                # network dedicated to the reverse proxy
    labels:
      traefik.enable: true
      traefik.http.routers.app.rule: Host(`app.exemple.fr`)
      traefik.http.routers.app.entrypoints: websecure   # HTTPS only
      traefik.http.routers.app.middlewares: secure-headers@file

On the TLS side: a wildcard Let’s Encrypt certificate obtained through a DNS-01 challenge via the OVH API. Two concrete advantages. No HTTP port to open to validate the challenge. And subdomains are not enumerated one by one in the public Certificate Transparency logs: a wildcard does not disclose the map of services.

Accepted tradeoff

Traefik becomes the single central piece: if it goes down, the whole edge goes down. On a single-server platform, this point of failure is accepted. In exchange, there is only one surface to harden, monitor and update. Traefik’s learning curve (routers, services, middlewares) is real, but it is paid only once.

Outcome

Every service, public and private alike, goes through the same entry point, with HTTPS everywhere and certificates renewed without intervention since the initial setup.

Docker hardening around the proxy

The reverse proxy is the most exposed component of the platform. It is also the one with the most power, since it discovers containers through the Docker API. Hence a specific hardening:

  • The Docker socket is never mounted directly. Traefik talks to a read-only socket-proxy that only lets through the API endpoints strictly required for service discovery. A compromise of the proxy does not hand over control of the Docker daemon.
  • no-new-privileges on the containers: no privilege escalation possible through setuid binaries.
  • Separate internal networks. The reverse proxy network only contains the services to expose. Databases and supporting services live on internal networks with no route to the edge.
Middleware chain (redirection, HSTS, exposure)

Traefik middlewares are declared once, then composed per service:

  • Global HTTPS redirection: everything arriving over HTTP is redirected, no exceptions.
  • secure-headers: HSTS, X-Content-Type-Options: nosniff, protection against iframe embedding, strict referrer policy.
  • tailscale-only: private services only accept the zero-trust network. Details on the Zero-trust network page.

A service’s exposure is thus declared route by route, never implicitly.