Rest provider PUT giving 404 when API auth is enabled

I set up a very simple docker-compose.yml based on Traefik documentation where API has basicAuth

When I include --providers.rest.insecure=true I'm able to PUT a file to localhost:8080/api/providers/rest (with or without user credentials).

When I remove --providers.rest.insecure=true I'm not able to PUT a file anymore...

  • When I include the correct credentials I get 404 page not found
  • When I include incorrect credentials I get 401 Unauthorize

Only log that occurs when providing the correct credentials is:

reverse-proxy_1  | time="2019-11-05T22:06:09Z" level=debug msg="Authentication succeeded" middlewareType=BasicAuth middlewareName=myAuth@docker

I opened a bug report on GitHub but it was flagged by a bot because it "might be a configuration problem or relates to something that doesn't look like a bug."

Here's my docker-compose.yml:

version: '3'

services:
  reverse-proxy:
    # The official v2.0 Traefik docker image
    image: traefik:v2.0
    # Enables the web UI and tells Traefik to listen to docker
    command:
      - "--api=true"
      - "--api.debug=true"
      # - "--api.insecure=true"
      - "--providers.rest=true"
      # - "--providers.rest.insecure=true"
      - "--ping=true"
      - "--providers.docker"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web-secure.address=:443"
      - "--entryPoints.traefik.address=:8080"
      - "--accesslog.filters.retryattempts=true"
      - "--accesslog=true"
      - "--log.level=DEBUG"
    ports:
      # The HTTP port
      - "80:80"
      # The Web UI (enabled by --api.insecure=true)
      - "8080:8080"
    volumes:
      # So that Traefik can listen to the Docker events
      - /var/run/docker.sock:/var/run/docker.sock
    labels:
      - "traefik.http.routers.api.rule=Host(`localhost`)"
      - "traefik.http.routers.api.service=api@internal"
      - "traefik.http.routers.api.middlewares=myAuth"
      - "traefik.http.middlewares.myAuth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"

(I left the insecure lines commented so it's easy to toggle)

I know I was able to PUT a file in Traefik 1.7 with user credentials, so either I'm missing something, configured incorrectly, or the provider has lost this functionality. As other posts have indicated, there's no documentation page for the Rest provider anymore which has made it difficult to understand what configuration is necessary and appropriate.

Hello,

version: '3'

services:
  reverse-proxy:
    image: traefik:v2.0.4
    command:
      - "--ping=true"
      - "--api=true"
      # - "--api.insecure=true"
      - "--providers.rest=true"
      # - "--providers.rest.insecure=true"
      - "--providers.docker"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web-secure.address=:443"
      - "--accesslog=true"
      - "--accesslog.filters.retryattempts=true"
      - "--log.level=DEBUG"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    labels:
      - "traefik.http.routers.api.rule=Host(`api.localhost`)"
      - "traefik.http.routers.api.service=api@internal"
      - "traefik.http.routers.api.entryPoints=web"
      - "traefik.http.routers.api.middlewares=myAuth"
      - "traefik.http.middlewares.myAuth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"

      - "traefik.http.routers.rest.rule=Host(`rest.localhost`)"
      - "traefik.http.routers.rest.service=rest@internal"
      - "traefik.http.routers.rest.entryPoints=web"
# API
$ curl api.localhost/api/rawdata
401 Unauthorized

$ curl -u test2:test2 api.localhost/api/rawdata
{"routers":{"api@docker":{"entryPoints":["web"],"middlewares":["myAuth@docker"],"service":"api@internal","rule":"Host(`api.localhost`)","status":"enabled","using":["web"]},"rest@docker":{"entryPoints":["web"],"service":"rest@internal","rule":"Host(`rest.localhost`)","status":"enabled","using":["web"]}},"middlewares":{"myAuth@docker":{"basicAuth":{"users":["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/","test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"]},"status":"enabled","usedBy":["api@docker"]}},"services":{"reverse-proxy-tem@docker":{"loadBalancer":{"servers":[{"url":"http://172.27.0.2:80"}],"passHostHeader":true},"status":"enabled"}}}docker":{"loadBalancer":{"servers":[{"url":"http://172.27.0.2:80"}],"passHostHeader":true},"status":"enabled"}}}

# REST
$ curl -v  -d "{}" -X PUT rest.localhost/api/providers/rest
{}

When I tried running that file I wasn't able to access anything.

These are the commands I was originally testing with and the responses using my file:

$ curl -X PUT -d @t.json -u test2:test2 "localhost:8080/api/providers/rest"
404 page not found

$ curl localhost:8080 -u test2:test2
<a href="/dashboard/">Found</a>.

When I was running your file:

$ curl localhost:8080 -u test2:test2
curl: (7) Failed to connect to localhost port 8080: Connection refused

$ curl -X PUT -d @t.json -u test2:test2 "localhost:8080/api/providers/rest"
curl: (7) Failed to connect to localhost port 8080: Connection refused

# Using your commands
$ curl api.localhost/api/providers/docker
curl: (6) Could not resolve host: api.localhost

$ curl -v  -d "{}" -X PUT rest.localhost/api/providers/rest
* Could not resolve host: rest.localhost
* Closing connection 0
curl: (6) Could not resolve host: rest.localhost

api.localhost and rest.localhost are not resolved because I suppose you are using another OS than linux.

FYI it works for me (on Linux)

As you are not able to resolve the domain, you have to use PathPrefix:

version: '3'

services:
  reverse-proxy:
    image: traefik:v2.0.4
    command:
      - "--ping=true"
      - "--api=true"
      # - "--api.insecure=true"
      - "--providers.rest=true"
      # - "--providers.rest.insecure=true"
      - "--providers.docker"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web-secure.address=:443"
      - "--accesslog=true"
      - "--accesslog.filters.retryattempts=true"
      - "--log.level=DEBUG"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    labels:
      - "traefik.http.routers.api.rule=Host(`localhost`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
      - "traefik.http.routers.api.priority=1"
      - "traefik.http.routers.api.service=api@internal"
      - "traefik.http.routers.api.entryPoints=web"
      - "traefik.http.routers.api.middlewares=myAuth"
      - "traefik.http.middlewares.myAuth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"

      - "traefik.http.routers.rest.rule=Host(`localhost`) && PathPrefix(`/api/providers/rest`)"
      - "traefik.http.routers.rest.priority=10"
      - "traefik.http.routers.rest.service=rest@internal"
      - "traefik.http.routers.rest.entryPoints=web"
# API
$ curl localhost/api/rawdata
401 Unauthorized

$ curl -u test2:test2 localhost/api/rawdata
{"routers":{"api@docker":{"entryPoints":["web"],"middlewares":["myAuth@docker"],"service":"api@internal","rule":"Host(`localhost`) \u0026\u0026 (PathPrefix(`/api`) || PathPrefix(`/dashboard`))","priority":1,"status":"enabled","using":["web"]},"rest@docker":{"entryPoints":["web"],"service":"rest@internal","rule":"Host(`localhost`) \u0026\u0026 PathPrefix(`/api/providers/rest`)","priority":10,"status":"enabled","using":["web"]}},"middlewares":{"myAuth@docker":{"basicAuth":{"users":["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/","test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"]},"status":"enabled","usedBy":["api@docker"]}},"services":{"reverse-proxy-tem@docker":{"loadBalancer":{"servers":[{"url":"http://172.27.0.2:80"}],"passHostHeader":true},"status":"enabled"}}}

# REST
$ curl -v  -d "{}" -X PUT localhost/api/providers/rest
{}

Gotcha. Yeah I'm running macOS Mojave and should have specified that earlier.

So with that latest file I'm able to PUT a file without providing credentials:

$ curl -d @t.json -X PUT localhost/api/providers/rest
{"http":{...})

To clarify, I want there to be auth for both the dashboard and when PUTting.

so it's easy:

version: '3'

services:
  reverse-proxy:
    image: traefik:v2.0.4
    command:
      - "--ping=true"
      - "--api=true"
      - "--providers.rest=true"
      - "--providers.docker"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web-secure.address=:443"
      - "--accesslog=true"
      - "--accesslog.filters.retryattempts=true"
      - "--log.level=DEBUG"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    labels:
      # API
      - "traefik.http.routers.api.rule=Host(`localhost`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
      - "traefik.http.routers.api.priority=1"
      - "traefik.http.routers.api.service=api@internal"
      - "traefik.http.routers.api.entryPoints=web"
      - "traefik.http.routers.api.middlewares=myAuth"

      # REST
      - "traefik.http.routers.rest.rule=Host(`localhost`) && PathPrefix(`/api/providers/rest`)"
      - "traefik.http.routers.rest.priority=10"
      - "traefik.http.routers.rest.service=rest@internal"
      - "traefik.http.routers.rest.entryPoints=web"
      - "traefik.http.routers.rest.middlewares=myAuth"

      # Middlewares
      - "traefik.http.middlewares.myAuth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"


# API
$ curl localhost/api/rawdata
401 Unauthorized

$ curl -u test2:test2 localhost/api/rawdata
{"routers":{"api@docker":{"entryPoints":["web"],"middlewares":["myAuth@docker"],"service":"api@internal","rule":"Host(`localhost`) \u0026\u0026 (PathPrefix(`/api`) || PathPrefix(`/dashboard`))","priority":1,"status":"enabled","using":["web"]},"rest@docker":{"entryPoints":["web"],"service":"rest@internal","rule":"Host(`localhost`) \u0026\u0026 PathPrefix(`/api/providers/rest`)","priority":10,"status":"enabled","using":["web"]}},"middlewares":{"myAuth@docker":{"basicAuth":{"users":["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/","test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"]},"status":"enabled","usedBy":["api@docker"]}},"services":{"reverse-proxy-tem@docker":{"loadBalancer":{"servers":[{"url":"http://172.27.0.2:80"}],"passHostHeader":true},"status":"enabled"}}}

# REST
$ curl -u test2:test2 -d "{}" -X PUT localhost/api/providers/rest
{}

$ curl -d "{}" -X PUT localhost/api/providers/rest
Enter host password for user '{}':

Aside: on windows I had success putting api.localhost and the like to the host file. Maybe the same would work for macOS?

Thanks @ldez! Yep I understand the middlewares fix and realized that after my last comment.

After diffing my original file to yours, the reason I wasn't able to get it working was because I didn't realize raefik.http.routers.rest or rest@internal existed as concepts. I knew about api's equivalents, though.

1 Like