Traefik’s Dashboard doesn’t have user authentication by default, which means you need to implement authentication through other methods. The simplest method is to use the Basic Auth middleware, and another slightly more complex solution is to use an AccessToken as authentication credentials in the Dashboard route.

However, both methods above are not flexible enough. In a team environment, we might want to dynamically add authentication and manage it flexibly.

Prerequisites

Zitadel is an open-source external identity provider. If you’re not familiar with Zitadel, you can check out my article Open Source Identity Authentication and Authorization Solutions.

Traefik Configuration

services:
  traefik:
    image: traefik:latest
    labels:
      - traefik.tls.stores.default.defaultgeneratedcert.resolver=step-ca
      - traefik.tls.stores.default.defaultgeneratedcert.domain.main=svc.dev
      - traefik.tls.stores.default.defaultgeneratedcert.domain.sans=*.svc.dev

      - traefik.enable=true
      - traefik.docker.network=traefik

      - traefik.http.routers.traefik-dashboard.tls=true
      - traefik.http.routers.traefik-dashboard.tls.certresolver=step-ca
      - traefik.http.routers.traefik-dashboard.rule=Host(`traefik.svc.dev`)
      - traefik.http.routers.traefik-dashboard.entrypoints=http,https
      - traefik.http.routers.traefik-dashboard.service=dashboard@internal
      # Set middleware for Traefik Dashboard
      - traefik.http.routers.traefik-dashboard.middlewares=unauthentication@docker,authentication@docker

      - traefik.http.routers.traefik-dashboard-api.tls=true
      - traefik.http.routers.traefik-dashboard-api.tls.certresolver=step-ca
      - traefik.http.routers.traefik-dashboard-api.rule=Host(`traefik.svc.dev`) && PathPrefix(`/api`)
      - traefik.http.routers.traefik-dashboard-api.entrypoints=http,https
      - traefik.http.routers.traefik-dashboard-api.service=api@internal
      # Set middleware for Traefik Dashboard
      - traefik.http.routers.traefik-dashboard-api.middlewares=unauthentication@docker,authentication@docker

      # Set up /oauth2 route for Traefik, forwarding to OAuth2 proxy
      - traefik.http.routers.traefik-oauth.tls=true
      - traefik.http.routers.traefik-oauth.tls.certresolver=step-ca
      - traefik.http.routers.traefik-oauth.rule=Host(`traefik.svc.dev`) && PathPrefix(`/oauth2`)
      - traefik.http.routers.traefik-oauth.entrypoints=http,https
      - traefik.http.routers.traefik-oauth.service=authentication
      - traefik.http.routers.traefik-oauth.middlewares=unauthentication@docker

      - traefik.http.middlewares.authentication.forwardauth.address=https://authentication.svc.dev/oauth2/auth
      - traefik.http.middlewares.authentication.forwardauth.trustForwardHeader=true
      - traefik.http.middlewares.authentication.forwardauth.authResponseHeaders=X-Auth-Request-User,X-Auth-Request-Preferred-Username,X-Auth-Request-Access-Token,X-Auth-Request-Groups,Authorization,Set-Cookie,Location
      - traefik.http.middlewares.authentication.forwardauth.tls.insecureSkipVerify=true

      - traefik.http.middlewares.unauthentication.errors.query=/oauth2/sign_in
      - traefik.http.middlewares.unauthentication.errors.status=401-403
      - traefik.http.middlewares.unauthentication.errors.service=authentication
    restart: always
    hostname: traefik
    networks:
      - traefik
    command: 
      - --api=true
      - --api.insecure=false
      - --api.dashboard=true

      - --log.level=ERROR

      - --ping=true

      - --providers.file=true
      - --providers.file.watch=true
      - --providers.file.directory=/etc/traefik/config
      - --providers.file.debugloggeneratedtemplate=true

      - --providers.docker=true
      - --providers.docker.watch=true
      - --providers.docker.network=traefik
      - --providers.docker.useBindPortIP=false
      - --providers.docker.endpoint=unix:///var/run/docker.sock

      - --entrypoints.http.address=:80
      - --entrypoints.http.http.redirections.entryPoint.to=https
      - --entrypoints.http.http.redirections.entryPoint.scheme=https
      - --entryPoints.http.http.redirections.entrypoint.permanent=true

      - --entrypoints.https.address=:443
      - --entryPoints.https.http3.advertisedport=443
      - --entryPoints.https.http.tls.certResolver=step-ca
      - --entryPoints.https.http.tls.domains[0].main=svc.dev
      - --entryPoints.https.http.tls.domains[0].sans=*.svc.dev

      - --serverstransport.insecureskipverify=true
    volumes:
      - ./certs/:/certs/:rw
      - ./config/:/etc/traefik/config/:ro
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - TZ=Asia/Shanghai
    container_name: traefik

networks:
  traefik:
    external: true

OAuth2 Proxy Configuration

services:
  authentication:
    image: quay.io/oauth2-proxy/oauth2-proxy:v7.7.1
    labels:
      - traefik.enable=true
      - traefik.http.routers.authentication.tls=true
      - traefik.http.routers.authentication.tls.certresolver=step-ca
      - traefik.http.routers.authentication.rule=Host(`authentication.svc.dev`) && PathPrefix(`/oauth2/`)
      - traefik.http.routers.authentication.service=authentication
      - traefik.http.routers.authentication.entrypoints=http,https
      - traefik.http.routers.authentication.middlewares=authentication-headers@docker

      - traefik.http.services.authentication.loadbalancer.server.port=4180

      - traefik.http.middlewares.authentication-headers.headers.sslHost=svc.dev
      - traefik.http.middlewares.authentication-headers.headers.frameDeny=true
      - traefik.http.middlewares.authentication-headers.headers.stsSeconds=315360000
      - traefik.http.middlewares.authentication-headers.headers.stsPreload=true
      - traefik.http.middlewares.authentication-headers.headers.sslRedirect=true
      - traefik.http.middlewares.authentication-headers.headers.forceSTSHeader=true
      - traefik.http.middlewares.authentication-headers.headers.browserXssFilter=true
      - traefik.http.middlewares.authentication-headers.headers.contentTypeNosniff=true
      - traefik.http.middlewares.authentication-headers.headers.stsIncludeSubdomains=true
    volumes:
      - ./templates:/templates
    restart: no
    hostname: authentication
    networks:
      - traefik
    environment:
      - OAUTH2_PROXY_PROVIDER=oidc
      - OAUTH2_PROXY_PROVIDER_DISPLAY_NAME=ZITADEL
      - OAUTH2_PROXY_PROVIDER_CA_FILES=/root_ca.crt

      - OAUTH2_PROXY_HTTP_ADDRESS=0.0.0.0:4180

      - OAUTH2_PROXY_EMAIL_DOMAINS=*
      - OAUTH2_PROXY_OIDC_ISSUER_URL=https://zitadel.svc.dev

      - OAUTH2_PROXY_CLIENT_ID=
      - OAUTH2_PROXY_CLIENT_SECRET=

      - OAUTH2_PROXY_COOKIE_NAME=authentication
      - OAUTH2_PROXY_COOKIE_SECURE=true
      - OAUTH2_PROXY_COOKIE_SECRET=
      - OAUTH2_PROXY_COOKIE_DOMAINS=.svc.dev
      - OAUTH2_PROXY_WHITELIST_DOMAINS=*.svc.dev

      - OAUTH2_PROXY_REVERSE_PROXY=true
      - OAUTH2_PROXY_PASS_ACCESS_TOKEN=true

      - OAUTH2_PROXY_CUSTOM_TEMPLATES_DIR=/templates

      - OAUTH2_PROXY_INSECURE_OIDC_ALLOW_UNVERIFIED_EMAIL=true

      - OAUTH2_PROXY_UPSTREAMS=file:///dev/null
      - OAUTH2_PROXY_UPSTREAM_TIMEOUT=30s

      - OAUTH2_PROXY_SSL_INSECURE_SKIP_VERIFY=true
      - OAUTH2_PROXY_SSL_UPSTREAM_INSECURE_SKIP_VERIFY=false
    container_name: authentication

volumes:
  step-ca:
    name: step-ca
    external: true

networks:
  traefik:
    external: true

Zitadel Configuration

Zitadel Create Traefik Project Zitadel Config Zitadel Auth Redirect Config

With the above configuration, you can implement user authentication for the Traefik Dashboard.

I hope this is helpful, Happy hacking…