Need help with securing Traefik dashboard

I need help on securing Traefik dashboard, the config file below, I am getting 404 not found on both foo.bar/dashboard and foo.bar.

version: "3.3"

networks:
  proxy:
    external: true
  
volumes:
  log:
    external: true
  letsencrypt: 

configs:
  traefik_users:
    file: /path/to/users/file/users
  
services:
  traefik:
    image: traefik:v2.2.1
    configs:
      - source: traefik_users
        target: /users
        mode: 444
    ports:
      - "80:80"
      - "443:443"
    command:
      - --api.insecure=false
      - --api.dashboard=true
      - --api

      - --accesslog=true
      - --accesslog.filepath=/var/log/traefik/traefik.log
      - --accesslog.bufferingsize=100
      - --log.level=ERROR

      - --providers.docker=true
      - --providers.docker.endpoint=unix:///var/run/docker.sock
      - --providers.docker.swarmMode=true
      - --providers.docker.exposedByDefault=false
      - --providers.docker.network=proxy

      - --entrypoints.ep-http.address=:80
      - --entrypoints.ep-https.address=:443

      # - --certificatesresolvers.certresolver.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory
      - --certificatesresolvers.certresolver.acme.caServer=https://acme-v02.api.letsencrypt.org/directory
      - --certificatesresolvers.certresolver.acme.tlschallenge=true
      - --certificatesresolvers.certresolver.acme.email=foo.bar@foo.bar
      - --certificatesresolvers.certresolver.acme.storage=/letsencrypt/acme.json
    volumes:
      - log:/var/log
      - letsencrypt:/letsencrypt
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - proxy
    deploy:
      replicas: 1
      restart_policy:
        condition: any
        delay: 3s
      placement:
        constraints: 
          - node.role == manager
      labels:
        - traefik.enable=true

        - traefik.http.routers.r-redirect-to-https.rule=hostregexp(`{host:.+}`)
        - traefik.http.routers.r-redirect-to-https.entrypoints=ep-http
        - traefik.http.routers.r-redirect-to-https.middlewares=mw-redirect-to-https
        - traefik.http.middlewares.mw-redirect-to-https.redirectscheme.scheme=https

        - traefik.http.middlewares.mw-traefik-auth.digestauth.usersfile=/users

        - traefik.http.routers.r-traefik.service=api@internal
        - traefik.http.routers.r-traefik.rule=Host(`foo.bar`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`))
        - traefik.http.routers.r-traefik.entrypoints=ep-https
        - traefik.http.routers.r-traefik.tls=true
        - traefik.http.routers.r-traefik.tls.certresolver=certresolver
        - traefik.http.routers.r-traefik.middlewares=mw-traefik-auth

And the 2d question - I wanted to run Traefik dashboard on a prefix foo.bar:/traefik - how would I let Traefik know that it runs on the prefix? I have tried to prepend the PathPrefixes with /traefik, then stripped the /traefik prefix, but how would I then inform Traefik dashboard that it runs behind a reverse proxy on the /traefik prefix?

Thanks a lot

Incorrect format, put the whole rule in () like:
- traefik.http.routers.r-traefik.rule=(Host(`foo.bar`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`)))

Per the docs the rule has to match /api/ and /dashboard

@cakiwi - thanks for your quick responses!

I've tried to add parentheses - no changes, still 404. Here is my current config file:

  version: "3.3"

  networks:
    proxy:
      external: true
    
  volumes:
    log:
      external: true
    opench_portainer:
      external: true
    letsencrypt: 

  configs:
    traefik_users:
      file: /home/pi/data/traefik_users/users
    
  services:
    traefik:
      image: traefik:v2.2.1
      configs:
        - source: traefik_users
          target: /users
          mode: 444
      ports:
        - "80:80"
        - "443:443"
      command:
        - --api.insecure=false # set to 'false' on production
        - --api.dashboard=true # see https://docs.traefik.io/v2.0/operations/dashboard/#secure-mode for how to secure the dashboard
        - --api

        - --accesslog=true
        - --accesslog.filepath=/var/log/traefik/traefik.log
        - --accesslog.bufferingsize=100
        - --log.level=ERROR

        - --providers.docker=true
        - --providers.docker.endpoint=unix:///var/run/docker.sock
        - --providers.docker.swarmMode=true
        - --providers.docker.exposedByDefault=false
        - --providers.docker.network=proxy

        - --entrypoints.ep-http.address=:80
        - --entrypoints.ep-https.address=:443

        - --certificatesresolvers.certresolver.acme.caServer=https://acme-v02.api.letsencrypt.org/directory
        - --certificatesresolvers.certresolver.acme.tlschallenge=true
        - --certificatesresolvers.certresolver.acme.email=foo.bar@foo.bar        - --certificatesresolvers.certresolver.acme.storage=/letsencrypt/acme.json
      volumes:
        - log:/var/log
        - letsencrypt:/letsencrypt
        - /var/run/docker.sock:/var/run/docker.sock
      networks:
        - proxy
      deploy:
        replicas: 1
        restart_policy:
          condition: any
          delay: 3s
        placement:
          constraints: 
            - node.role == manager
        labels:
          - traefik.enable=true

          - traefik.http.middlewares.mw-redirect-to-https.redirectscheme.scheme=https
          - traefik.http.middlewares.mw-redirect-to-https.redirectscheme.permanent=true
          - traefik.http.routers.r-redirect-to-https.rule=hostregexp(`{host:.+}`)
          - traefik.http.routers.r-redirect-to-https.entrypoints=ep-http
          - traefik.http.routers.r-redirect-to-https.middlewares=mw-redirect-to-https

          - traefik.http.services.dummy-service.loadbalancer.server.port=1357

          - traefik.http.routers.r-traefik.service=api@internal
          - traefik.http.routers.r-traefik.rule=(Host(`foo.bar`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`)))
          - traefik.http.routers.r-traefik.entrypoints=ep-https
          - traefik.http.routers.r-traefik.tls=true
          - traefik.http.routers.r-traefik.tls.certresolver=certresolver

Weird. I took your previous compose , removed the certresolver(only running locally) and updated the rule.
Ran like a charm.

Maybe switch up the logging to DEBUG to see what is going on.

version: "3.3"

networks:
  proxy:
#    external: true
  
volumes:
  log:
#    external: true
  letsencrypt: 

configs:
  traefik_users:
    file: ./users
  
services:
  traefik:
    image: traefik:v2.2.1
    configs:
      - source: traefik_users
        target: /users
        mode: 444
    ports:
      - "80:80"
      - "443:443"
    command:
      - --api.insecure=false
      - --api.dashboard=true
      - --api

      - --accesslog=true
#      - --accesslog.filepath=/var/log/traefik/traefik.log
      - --accesslog.bufferingsize=100
      - --log.level=INFO

      - --providers.docker=true
      - --providers.docker.endpoint=unix:///var/run/docker.sock
      - --providers.docker.swarmMode=true
      - --providers.docker.exposedByDefault=false
      - --providers.docker.network=proxy

      - --entrypoints.ep-http.address=:80
      - --entrypoints.ep-https.address=:443

#      - --certificatesresolvers.certresolver.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory
#      - --certificatesresolvers.certresolver.acme.caServer=https://acme-v02.api.letsencrypt.org/directory
#      - --certificatesresolvers.certresolver.acme.tlschallenge=true
#      - --certificatesresolvers.certresolver.acme.email=foo.bar@foo.bar
#      - --certificatesresolvers.certresolver.acme.storage=/letsencrypt/acme.json
    volumes:
      - log:/var/log
      - letsencrypt:/letsencrypt
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - proxy
    deploy:
      replicas: 1
      restart_policy:
        condition: any
        delay: 3s
      placement:
        constraints: 
          - node.role == manager
      labels:
        - traefik.enable=true

        - traefik.http.middlewares.mw-redirect-to-https.redirectscheme.scheme=https
        - traefik.http.middlewares.mw-redirect-to-https.redirectscheme.permanent=true
        - traefik.http.routers.r-redirect-to-https.rule=hostregexp(`{host:.+}`)
        - traefik.http.routers.r-redirect-to-https.entrypoints=ep-http
        - traefik.http.routers.r-redirect-to-https.middlewares=mw-redirect-to-https

        - traefik.http.services.dummy-service.loadbalancer.server.port=1357

        - traefik.http.routers.r-traefik.service=api@internal
        - traefik.http.routers.r-traefik.rule=(Host(`foo.bar`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`)))
        - traefik.http.routers.r-traefik.entrypoints=ep-https
        - traefik.http.routers.r-traefik.tls=true
#        - traefik.http.routers.r-traefik.tls.certresolver=certresolver

curl -ik https://foo.bar/api/rawdata --resolve foo.bar:443:127.0.0.1
HTTP/2 200 
content-type: application/json
content-length: 957
date: Sun, 24 May 2020 15:51:31 GMT

{"routers":{"r-redirect-to-https@docker":{"entryPoints":["ep-http"],"middlewares":["mw-redirect-to-https@docker"],"service":"dummy-service","rule":"hostregexp(`{host:.+}`)","status":"enabled","using":["ep-http"]},"r-traefik@docker":{"entryPoints":["ep-https"],"service":"api@internal","rule":"(Host(`foo.bar`) \u0026\u0026 (PathPrefix(`/dashboard`) || PathPrefix(`/api`)))","tls":{},"status":"enabled","using":["ep-https"]}},"middlewares":{"mw-redirect-to-https@docker":{"redirectScheme":{"scheme":"https","permanent":true},"status":"enabled","usedBy":["r-redirect-to-https@docker"]}},"services":{"api@internal":{"status":"enabled","usedBy":["r-traefik@docker"]},"dashboard@internal":{"status":"enabled"},"dummy-service@docker":{"loadBalancer":{"servers":[{"url":"http://10.0.0.26:1357"}],"passHostHeader":true},"status":"enabled","usedBy":["r-redirect-to-https@docker"],"serverStatus":{"http://10.0.0.26:1357":"UP"}},"noop@internal":{"status":"enabled"}}}

/api/rawdata works for me too, but /dashboard returns 404 in the browser.

Here is what /api/rawdata returns:


"services": {
"api@internal": {
"status": "enabled",
"usedBy": [
"r-traefik@docker"
]
},
"dashboard@internal": {
"status": "enabled"
},
"dummy-service@docker": {
"loadBalancer": {
"servers": [
{
"url": "[http://10.0.0.26:1357](http://10.0.0.26:1357)"
}
],
"passHostHeader": true
},
"status": "enabled",
"usedBy": [
"r-redirect-to-https@docker"
],
"serverStatus": {
"[http://10.0.0.26:1357](http://10.0.0.26:1357)": "UP"
}
},
"noop@internal": {
"status": "enabled"
}
}

I do not understand how the r-traefik router can route /dashboard request to dashboard@internal - there is no "usedBy" field in the dashboard@internal.

Another thing that I do not understand - why the dummy-service is needed. I saw it in examples on internet, without it the /api/rawdata does not work, although the api@internal does have the "usedBy" field anyway.

Just discovered that if I change the order of the endpoints in the rule - /dashboard works.


- traefik.http.routers.r-traefik.rule=(Host(`foo.bar`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`)))
1 Like

It stopped working after I tried:

  • to use digest authentication - got in an endless login popup loop
  • had to switch back to basic authenticaion
  • secured traefik stopped working

At the end I found a reliable solution - trailing slashes in the router rules, this works so far:

- traefik.http.routers.r-traefik.rule=(Host(`foo.bar`) && (PathPrefix(`/api/`) || PathPrefix(`/dashboard/`)))

Used trailing slashes also in the corresponding DNS record, though not sure if it was needed.