docker_auth icon indicating copy to clipboard operation
docker_auth copied to clipboard

ACL is not working properly

Open smedelyan opened this issue 3 years ago • 9 comments

Description

I cannot push if allow push-only for a user. I also saw another case when "pull-only" suddenly allowed push for user (UP: example in the next comment), but example configuration below is for push-only

Config:

server:
  addr: ":5001"
  certificate: "/auth/cert.pem"
  key: "/auth/key.pem"

token:
  issuer: "Acme auth server"  # Must match issuer in the Registry config.
  expiration: 900

users:
  # Password is specified as a BCrypt hash. Use `htpasswd -nB USERNAME` to generate.
  "test":
    password: "$2y$05$mdG8K4U.5d4h7RYqVw7vWeN7F8qztzgn5CvzKQfFtOY8Fz7WD..9a" # 123

acl:
  - match: {account:"test"}
    actions: ["push"]
  # Access is denied by default.

Then:

$ docker login <url>
Username: test
Password:
WARNING! Your password will be stored unencrypted in /home/.../.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

$ docker push <url>/hello-world
The push refers to repository [<url>/hello-world]
e07ee1baac5f: Preparing
denied: requested access to the resource is denied

Logs:

$ docker-compose up docker-auth
Starting registry_docker-auth_1 ... done
Attaching to registry_docker-auth_1
docker-auth_1  | I1111 11:59:51.147314       1 main.go:213] docker_auth 20211004 build 20211004-192635/1.8.0@b7b9d6e0
docker-auth_1  | I1111 11:59:51.148561       1 main.go:61] Config from /config/auth_config.yml (1 users, 1 ACL static entries)
docker-auth_1  | I1111 11:59:51.148588       1 acl.go:109] Created ACL Authorizer with 1 entries
docker-auth_1  | I1111 11:59:51.148602       1 main.go:110] Cert file: /auth/cert.pem
docker-auth_1  | I1111 11:59:51.148609       1 main.go:111] Key file : /auth/key.pem
docker-auth_1  | I1111 11:59:51.149253       1 main.go:142] Serving on :5001
docker-auth_1  | I1111 12:00:06.272664       1 server.go:488] Auth request: {test:***@172.28.0.1:57484 []}
docker-auth_1  | I1111 12:00:06.277488       1 server.go:317] Authn static test -> true, map[], <nil>
docker-auth_1  | I1111 12:00:06.284409       1 server.go:429] New token for {test:***@172.28.0.1:57484 []} map[]: {"iss":"Acme auth server","sub":"test","aud":"Docker registry","exp":1636632906,"nbf":1636631996,"iat":1636632006,"jti":"2653821073426952576","access":[]}
docker-auth_1  | I1111 12:00:14.957211       1 server.go:488] Auth request: {test:***@172.28.0.1:57512 [{repository  hello-world [pull push]}]}
docker-auth_1  | I1111 12:00:14.963503       1 server.go:317] Authn static test -> true, map[], <nil>
docker-auth_1  | I1111 12:00:14.963559       1 acl.go:121] {test pull,push repository hello-world} matched {"Match":{},"Actions":["push"],"Comment":null} (Comment: (nil))
docker-auth_1  | I1111 12:00:14.963897       1 server.go:339] Authz static ACL {test pull,push repository hello-world} -> [push], %!s(<nil>)
docker-auth_1  | I1111 12:00:14.972476       1 server.go:429] New token for {test:***@172.28.0.1:57512 [{repository  hello-world [pull push]}]} map[]: {"iss":"Acme auth server","sub":"test","aud":"Docker registry","exp":1636632914,"nbf":1636632004,"iat":1636632014,"jti":"7158462588365033085","access":[{"type":"repository","name":"hello-world","actions":["push"]}]}

Registry logs:

$ docker logs -f registry_registry_1
time="2021-11-11T11:59:31.930641155Z" level=warning msg="No HTTP secret provided - generated random secret. This may cause problems with uploads if multiple registries are behind a load-balancer. To provide a shared secret, fill in http.secret in the configuration file or set the REGISTRY_HTTP_SECRET environment variable." go.version=go1.11.2 instance.id=05158f38-23d6-4780-a8c7-05035cf011e9 service=registry version=v2.7.1
time="2021-11-11T11:59:31.930714673Z" level=info msg="Starting upload purge in 42m0s" go.version=go1.11.2 instance.id=05158f38-23d6-4780-a8c7-05035cf011e9 service=registry version=v2.7.1
time="2021-11-11T11:59:31.930740319Z" level=info msg="redis not configured" go.version=go1.11.2 instance.id=05158f38-23d6-4780-a8c7-05035cf011e9 service=registry version=v2.7.1
time="2021-11-11T11:59:31.944043636Z" level=info msg="using inmemory blob descriptor cache" go.version=go1.11.2 instance.id=05158f38-23d6-4780-a8c7-05035cf011e9 service=registry version=v2.7.1
time="2021-11-11T11:59:31.945053953Z" level=info msg="listening on [::]:5000" go.version=go1.11.2 instance.id=05158f38-23d6-4780-a8c7-05035cf011e9 service=registry version=v2.7.1
time="2021-11-11T12:00:05.505275156Z" level=warning msg="error authorizing context: authorization token required" go.version=go1.11.2 http.request.host="localhost:5000" http.request.id=ba47e826-f941-4ede-b915-2a09c7e2ff29 http.request.method=GET http.request.remoteaddr="172.28.0.1:56328" http.request.uri="/v2/" http.request.useragent="docker/19.03.13 go/go1.13.15 git-commit/4484c46d9d kernel/4.15.0-162-generic os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.13 \(linux\))"
172.28.0.1 - - [11/Nov/2021:12:00:05 +0000] "GET /v2/ HTTP/1.0" 401 87 "" "docker/19.03.13 go/go1.13.15 git-commit/4484c46d9d kernel/4.15.0-162-generic os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.13 \\(linux\\))"
172.28.0.1 - - [11/Nov/2021:12:00:07 +0000] "GET /v2/ HTTP/1.0" 200 2 "" "docker/19.03.13 go/go1.13.15 git-commit/4484c46d9d kernel/4.15.0-162-generic os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.13 \\(linux\\))"
time="2021-11-11T12:00:07.009598351Z" level=info msg="authorized request" go.version=go1.11.2 http.request.host="localhost:5000" http.request.id=933b2b18-dbe1-45db-97ae-18ab29f48767 http.request.method=GET http.request.remoteaddr="172.28.0.1:56346" http.request.uri="/v2/" http.request.useragent="docker/19.03.13 go/go1.13.15 git-commit/4484c46d9d kernel/4.15.0-162-generic os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.13 \(linux\))"
time="2021-11-11T12:00:07.009723766Z" level=info msg="response completed" go.version=go1.11.2 http.request.host="localhost:5000" http.request.id=933b2b18-dbe1-45db-97ae-18ab29f48767 http.request.method=GET http.request.remoteaddr="172.28.0.1:56346" http.request.uri="/v2/" http.request.useragent="docker/19.03.13 go/go1.13.15 git-commit/4484c46d9d kernel/4.15.0-162-generic os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.13 \(linux\))" http.response.contenttype="application/json; charset=utf-8" http.response.duration=3.025691ms http.response.status=200 http.response.written=2
time="2021-11-11T12:00:14.227668609Z" level=warning msg="error authorizing context: authorization token required" go.version=go1.11.2 http.request.host="localhost:5000" http.request.id=313bbbb9-7d81-47c8-b594-31da0fab8c31 http.request.method=GET http.request.remoteaddr="172.28.0.1:56356" http.request.uri="/v2/" http.request.useragent="docker/19.03.13 go/go1.13.15 git-commit/4484c46d9d kernel/4.15.0-162-generic os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.13 \(linux\))"
172.28.0.1 - - [11/Nov/2021:12:00:14 +0000] "GET /v2/ HTTP/1.0" 401 87 "" "docker/19.03.13 go/go1.13.15 git-commit/4484c46d9d kernel/4.15.0-162-generic os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.13 \\(linux\\))"
172.28.0.1 - - [11/Nov/2021:12:00:15 +0000] "HEAD /v2/hello-world/blobs/sha256:2db29710123e3e53a794f2694094b9b4338aa9ee5c40b930cb8063a1be392c54 HTTP/1.0" 401 154 "" "docker/19.03.13 go/go1.13.15 git-commit/4484c46d9d kernel/4.15.0-162-generic os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.13 \\(linux\\))"
time="2021-11-11T12:00:15.72710083Z" level=warning msg="error authorizing context: insufficient scope" go.version=go1.11.2 http.request.host="localhost:5000" http.request.id=f5642f07-2d63-4bde-8cad-d2f82877afad http.request.method=HEAD http.request.remoteaddr="172.28.0.1:56364" http.request.uri="/v2/hello-world/blobs/sha256:2db29710123e3e53a794f2694094b9b4338aa9ee5c40b930cb8063a1be392c54" http.request.useragent="docker/19.03.13 go/go1.13.15 git-commit/4484c46d9d kernel/4.15.0-162-generic os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.13 \(linux\))" vars.digest="sha256:2db29710123e3e53a794f2694094b9b4338aa9ee5c40b930cb8063a1be392c54" vars.name=hello-world
time="2021-11-11T12:00:16.450703541Z" level=warning msg="error authorizing context: insufficient scope" go.version=go1.11.2 http.request.host="localhost:5000" http.request.id=c17cea66-31ef-435d-9e7a-81e1e350428e http.request.method=POST http.request.remoteaddr="172.28.0.1:56368" http.request.uri="/v2/hello-world/blobs/uploads/" http.request.useragent="docker/19.03.13 go/go1.13.15 git-commit/4484c46d9d kernel/4.15.0-162-generic os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.13 \(linux\))" vars.name=hello-world
172.28.0.1 - - [11/Nov/2021:12:00:16 +0000] "POST /v2/hello-world/blobs/uploads/ HTTP/1.0" 401 224 "" "docker/19.03.13 go/go1.13.15 git-commit/4484c46d9d kernel/4.15.0-162-generic os/linux arch/amd64 UpstreamClient(Docker-Client/19.03.13 \\(linux\\))"

Network setup

Nginx proxies HTTPS requests to both registry and docker_auth

smedelyan avatar Nov 11 '21 12:11 smedelyan

Unrestricted push (wrong rule match?)

Config:

server:
  addr: ":5001"
  certificate: "/auth/cert.pem"
  key: "/auth/key.pem"

token:
  issuer: "Acme auth server"  # Must match issuer in the Registry config.
  expiration: 900

users:
  # Password is specified as a BCrypt hash. Use `htpasswd -nB USERNAME` to generate.
  "test":
    password: "$2y$05$mdG8K4U.5d4h7RYqVw7vWeN7F8qztzgn5CvzKQfFtOY8Fz7WD..9a" # 123
  "admin":
    password: "$2y$05$mdG8K4U.5d4h7RYqVw7vWeN7F8qztzgn5CvzKQfFtOY8Fz7WD..9a" # 123

acl:
        - match: {account:"admin"}
          actions: ["*"]
          comment: "Comment for admin" # NOTICE THIS COMMENT
        - match: {account:"test"}
          actions: ["pull"]
          comment: "Comment for test"
  # Access is denied by default.

Logs.

rm@NC-PH-0365-19:~/onpremise/registry$ docker-compose up docker-auth
Creating registry_docker-auth_1 ... done
Attaching to registry_docker-auth_1
docker-auth_1  | I1111 12:19:44.810827       1 main.go:213] docker_auth 20211004 build 20211004-192635/1.8.0@b7b9d6e0
docker-auth_1  | I1111 12:19:44.811980       1 main.go:61] Config from /config/auth_config.yml (2 users, 2 ACL static entries)
docker-auth_1  | I1111 12:19:44.812004       1 acl.go:109] Created ACL Authorizer with 2 entries
docker-auth_1  | I1111 12:19:44.812020       1 main.go:110] Cert file: /auth/cert.pem
docker-auth_1  | I1111 12:19:44.812029       1 main.go:111] Key file : /auth/key.pem
docker-auth_1  | I1111 12:19:44.812520       1 main.go:142] Serving on :5001
docker-auth_1  | I1111 12:19:51.034653       1 server.go:488] Auth request: {test:***@192.168.64.1:53466 []}
docker-auth_1  | I1111 12:19:51.039595       1 server.go:317] Authn static test -> true, map[], <nil>
docker-auth_1  | I1111 12:19:51.048264       1 server.go:429] New token for {test:***@192.168.64.1:53466 []} map[]: {"iss":"Acme auth server","sub":"test","aud":"Docker registry","exp":1636634091,"nbf":1636633181,"iat":1636633191,"jti":"2371071496851593163","access":[]}
docker-auth_1  | I1111 12:20:05.503673       1 server.go:488] Auth request: {test:***@192.168.64.1:53542 [{repository  hello-world [pull push]}]}
docker-auth_1  | I1111 12:20:05.510018       1 server.go:317] Authn static test -> true, map[], <nil>
docker-auth_1  | I1111 12:20:05.510076       1 acl.go:121] {test pull,push repository hello-world} matched {"Match":{},"Actions":["*"],"Comment":"Comment for admin"} (Comment: Comment for admin)
docker-auth_1  | I1111 12:20:05.510297       1 server.go:339] Authz static ACL {test pull,push repository hello-world} -> [pull push], %!s(<nil>)
docker-auth_1  | I1111 12:20:05.520118       1 server.go:429] New token for {test:***@192.168.64.1:53542 [{repository  hello-world [pull push]}]} map[]: {"iss":"Acme auth server","sub":"test","aud":"Docker registry","exp":1636634105,"nbf":1636633195,"iat":1636633205,"jti":"1280997329469189576","access":[{"type":"repository","name":"hello-world","actions":["pull","push"]}]}

Notice the following line in logs:

docker-auth_1  | I1111 12:20:05.510076       1 acl.go:121] {test pull,push repository hello-world} matched {"Match":{},"Actions":["*"],"Comment":"Comment for admin"} (Comment: Comment for admin)

Looks like the wrong rule matched?

smedelyan avatar Nov 11 '21 12:11 smedelyan

I'm getting worried when I see this 😑

tomaswarynyca avatar Dec 29 '21 23:12 tomaswarynyca

This issue looks critical to me, but looks like no one is maintaining this repository anymore?

smedelyan avatar Jan 19 '22 16:01 smedelyan

This issue looks critical to me, but looks like no one is maintaining this repository anymore?

I am, however I'm having trouble tracing this down.

techknowlogick avatar Jan 19 '22 16:01 techknowlogick

Wow! Do you at least confirm you can reproduce?

smedelyan avatar Jan 19 '22 16:01 smedelyan

Wow! Do you at least confirm you can reproduce?

I haven't yet, so I may need to start a virtual machine to test in to ensure that nothing else is affecting my debugging.

techknowlogick avatar Jan 28 '22 17:01 techknowlogick

Was this ever resolved / looked into? I am considering using docker_auth, but this seems like something of a dealbreaker

FlyveHest avatar May 17 '22 08:05 FlyveHest

@FlyveHest @tomaswarynyca I recently heard about https://github.com/goharbor/harbor . My colleagues have had some experience with it and they say that:

  • ACL is working and can be configured quite accurately
  • it is able not only to serve as Registry but also proxy the official registry as well
  • it is able to serve as artifacts storage not only for Docker, has lots of other features which may or may not be needed in your case

I'm going to try it out sooner or later, and if I don't forget I will let you know here if I succeeded

smedelyan avatar Aug 04 '22 13:08 smedelyan

For some reason, docker push seems to require both push and pull permissions from what I've tested, so the original issue might be normal.

I logged the content of accessItems here while running docker push some-registry/some-image: https://github.com/distribution/distribution/blob/26163d82560f4dda94bd7b87d587f94644c5af79/registry/auth/token/accesscontroller.go#L277-L283

and this is what I got:

[
  {
    "Type": "repository",
    "Class": "",
    "Name": "some-image",
    "Action": "pull"
  },
  {
    "Type": "repository",
    "Class": "",
    "Name": "some-image",
    "Action": "push"
  }
]

skwair avatar Aug 04 '22 22:08 skwair

@skwair Thanks to your investigation I was able to fix the issue by giving the user push AND pull permissions.

  - match: {account: "ci_bot"}
    actions: ["push", "pull"]
    comment: "Push images, but not allowed to delete."

Seams like my naive assumption that allowing anonymous pulling would always propagate to the logged in user didn't hold up 😅

  - match: {account: "", type: "registry", name: "catalog"}
    actions: ["*"]
    comment: "Anonymous can query the catalog"
  - match: {account: ""}
    actions: ["pull"]
    comment: "Anonymous can pull everything"

s-weigand avatar Feb 16 '24 13:02 s-weigand