Traefik 的 Dashboard 默认是没有用户认证的,也就是说你需要自己通过其他方法来实现身份认证。最简单的方法就是用 Basic Auth 中间件,还有一种稍微复杂一点的方案,就是在 Dashboard 路由中使用 AccessToken 作为身份认证的凭证。

但是上述两种方式都不够灵活,在团队中,我们可能希望能动态增加身份认证,并且能够灵活的管理。

前提条件

Zitadel 是开源的外部身份认证提供者,如果不了解 Zitadel 的可以去看我的这篇文章 开源身份认证和授权解决方案

Traefik 配置

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
      # 为 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
      # 为 Traefik Dashboard 设置中间件
      - traefik.http.routers.traefik-dashboard-api.middlewares=unauthentication@docker,authentication@docker

      # 为 Traefik 设置 /oauth2 路由,转发到 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 配置

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 配置

Zitadel Create Traefik Project Zitadel Config Zitadel Auth Redirect Config

通过上述的配置就可以实现为 Traefik Dashboard 设置用户认证了。

I hope this is helpful, Happy hacking…