Run traefik behind traefik reverse proxy

I would really like to be able to serve a project, which is a microservice cluster running using docker-compose, behind a reverse-proxy powered by traefik, so that I forward all requests to a specific subdomain to my second traefik proxy using TCP routing. From there I would like to route my requests using the specified path to each service.

I know that I could simply merge both reverse-proxies, but I need them to be separate, because the product needs to be installed on different systems with different environments.

If what I plan is possible using traefik.enable labels and constraints (still haven't figured it out yet how to use constraints yet) I would be really happy if someone could share a code snippet or smt like that.

To show what i exactly mean:

In theory this should be possible as traefik supports all the things that should enable this, like tcp forwarding with tls passthrough. My problem right now is that the two traefik instances, both started in seperate docker compose files should read each others labels, as this results in being not able to define the dashboard path, because two separate routers are defined. So I need to figure out a way to somehow get the requests from traefik instance 1 to traefik instance 2 to redirect the from there using the incoming http and https request (https for the services and http for the acme client to generate letsencrypt certificates). I think this might be able to do using a file provider, but I will have look on it tomorrow.

3 Likes

Hello,
I am trying to do something similar. Had you been able to figure it out?

Any luck with this? I am trying to do the exact same thing

Hello,
I realize this is an old topic, but I recently came across this issue because I have basically the same use case as @kdankert ...and I managed to get it working!
In the following example, I will be referring to the Traefik instances as specified in @kdankert 's post.

A couple of things to keep in mind:

  • I am using separate domains for the Traefik1 and Traefik2 dashboards.
  • Traefik1 is using a TCP router to forward requests to Traefik2, as requests are still encrypted when passing through Traefik1.
  • I am using constraints to limit the containers each Traefik instance creates routes for.
  • I already created a second network in order to demonstrate the isolation of each proxy's "subcontainers"

Traefik1 configuration:

traefik.yml

## STATIC CONFIGURATION
log:
  level: INFO

api:
  insecure: false
  dashboard: true

entryPoints:
  websecure:
    address: ":443"

providers:
  file:
    filename: dynamic.yml
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
    constraints: Label(`my.zone`, `zone1`)

certificatesResolvers:
  lets-encr:
    acme:
      email: <your email>
      storage: acme.json
      dnsChallenge:
        provider: cloudflare
        resolvers:
          - "1.1.1.1:53"
          - "8.8.8.8:53"

docker-compose.yml

version: "3.3"

services:
  traefik1:
    image: "traefik:v2.6.1"
    container_name: "traefik1"
    restart: "always"
    hostname: "traefik1"
    ports:
      - "443:443"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./dynamic.yml:/dynamic.yml:ro"
      - "./traefik.yml:/traefik.yml:ro"
      - "./acme.json:/acme.json"
    labels:
      - "my.zone=zone1"
      - "traefik.enable=true"
      - "traefik.http.routers.traefik1.entrypoints=websecure"
      - "traefik.http.routers.traefik1.tls.certresolver=lets-encr"
      - "traefik.http.routers.traefik1.tls.domains[0].main=traefik.$MY_DOMAIN1"
      - "traefik.http.routers.traefik1.rule=Host(`traefik1.$MY_DOMAIN1`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
      - "traefik.http.routers.traefik1.service=api@internal"
    # adding DNS mapping to /etc/hosts in order to avoid Traefik issues in case of wrong container starting order
    extra_hosts:
      - "$TRAEFIK2_DNS_MAPPER"
    # used to pass environment variables into the container in order to support environment substition in traefik files
    env_file:
      - ".env"

networks:
  default:
    external:
      name: $TRAEFIK1_NET

dynamic.yml

tcp:
  routers:
    to_traefik2:
      entrypoints:
        - "websecure"
      rule: HostSNI({{ env "TAEAFIK2_HOST_SNI" }})
      tls:
        passthrough: true
      service: traefik2

  services:
    traefik2:
      loadBalancer:
        servers:
          - address: {{ env "TRAEFIK2_NET1_ADDRESS" }}

.env

MY_DOMAIN1=<domain for Traefik1's dashboard>
TRAEFIK1_NET=traefik1_net
CF_API_EMAIL=<your CF API email>
CF_ZONE_API_TOKEN=<your Zone API Token>
CF_DNS_API_TOKEN=-<your DNS token>
TRAEFIK2_DNS_MAPPER=traefik2:<static ip of traefik2 in NET1>

TAEAFIK2_HOST_SNI=`traefik.<Traefik2's dashboard domain>`
TRAEFIK2_NET1_ADDRESS=traefik2:443

Traefik2 configuration:

traefik.yml

## STATIC CONFIGURATION
log:
  level: INFO

api:
  insecure: false
  dashboard: true

entryPoints:
  websecure:
    address: ":443"

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
    constraints: Label(`my.zone`, `zone2`)

certificatesResolvers:
  lets-encr:
    acme:
      email: <your email>
      storage: acme.json
      dnsChallenge:
        provider: cloudflare
        resolvers:
          - "1.1.1.1:53"
          - "8.8.8.8:53"

docker-compose.yml

version: '3.3'
services:
  traefik2:
    image: "traefik:v2.6.1"
    container_name: "traefik2"
    restart: "always"
    hostname: "treafik2"
    env_file:
      - ".env_traefik"
    ports:
      - "443"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./traefik.yml:/traefik.yml:ro"
      - "./acme.json:/acme.json"
    labels:
      - "my.zone=zone2"
      - "traefik.enable=true"
      - "traefik.http.routers.traefik2.entrypoints=websecure"
      - "traefik.http.routers.traefik2.tls.certresolver=lets-encr"
      - "traefik.http.routers.traefik2.tls.domains[0].main=traefik.$MY_DOMAIN2"
      - "traefik.http.routers.traefik2.rule=Host(`traefik.$MY_DOMAIN2`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
      - "traefik.http.routers.traefik2.service=api@internal"
    networks:
      traefik1_net:
        ipv4_address: $TRAEFIK2_NET1_IPV4
      traefik2_net: null

networks:
  traefik1_net:
    external:
      name: $TRAEFIK1_NET
  traefik2_net:
    external:
      name: $TRAEFIK2_NET

.env

MY_DOMAIN2=<domain for Traefik2's dashboard>
TRAEFIK1_NET=traefik1_net
TRAEFIK2_NET=traefik2_net
TRAEFIK2_NET1_IPV4=<static ip of traefik2 in NET1>

.env_traefik

# this .env file should not be used for substitution in docker-compose.yml as the environment variables will only be available in the running container
CF_API_EMAIL=<your CF API email>
CF_ZONE_API_TOKEN=<your Zone API Token>
CF_DNS_API_TOKEN=-<your DNS token>
2 Likes

Thank you, this post has helped a lot.
In my case I wanted to use the certResolver of Traefik2. For this you have to forward the HTTP traffic as described here. It is also important to increase the priority of the rules in dynamic.yml, so that the HTTP requests are not intercepted. this script allows you to view the priorities of your rules.

this is the modified dynamic.yml:

http:
  routers:
    web_to_traefik2:
      entryPoints:
        - "web"
      rule: Host({{ env "TAEAFIK2_HOST" }})
      service: traefik2_web
      priority: 2147483649

  services:
    traefik2_web:
      loadBalancer:
        servers:
          - url: "http://$TAEAFIK2_HOST"
        passHostHeader: true

tcp:
  routers:
    websecure_to_traefik2:
      entrypoints:
        - "websecure"
      rule: HostSNI({{ env "TAEAFIK2_HOST_SNI" }})
      tls:
        passthrough: true
      service: traefik2_websecure
      priority: 2147483648

  services:
    traefik2_websecure:
      loadBalancer:
        servers:
          - address: {{ env "TRAEFIK2_NET1_ADDRESS" }}