Token issued with empty actions when "*" requested and "pull" allowed
Hello,
I'm trying to run this webUI for Docker Registry with docker_auth in anonymous read mode.
My acl contains this:
- actions: ['*']
match: {account: '', name: catalog, type: registry}
- actions: ['pull']
match: {ip: 172.16.0.0/12}
registry, docker_auth and webUI containers are in the same 172.16/12 network.
WebUI asks for token "registry:catalog:*" and receives it ok, but when it asks for "repository:testrepo/testrepo2:*", it receives a token with empty actions and then receives HTTP 401 for registry request GET /v2/testrepo/testrepo2/tags/list. When I change allowed actions to "*" in the second element of acl, eveything works.
Issue may be fixed on both ends, but I'm not sure witch way is better:
- in webUI, modify token request to ask only
"pull", not"*"scope - in docker_auth, modify action intersection logic to treat
"*"as "any action" and return"pull"action token for"*"request. Asterisks are treated that way in registry itself: /registry/auth/token/util.go#L46
can you post both requests, as logged by docker_auth, in here for reference? i'm afraid i still don't quite understand what the two requests look like.
Sure, here it is:
I0304 04:22:26.697024 1 server.go:217] Authn static -> true, map[], <nil>
I0304 04:22:26.698026 1 acl.go:116] { * registry catalog} matched {"Match":{"account":"","type":"registry","name":"catalog"},"Actions":["*"],"Comment":"access to image catalog (for web UI)"} (Comment: %!s(*string=0xc42011adc0))
I0304 04:22:26.698632 1 server.go:239] Authz static ACL { * registry catalog} -> [*], %!s(<nil>)
I0304 04:22:26.724245 1 server.go:329] New token for {:@172.17.0.4 [{registry catalog [*]}]} map[]: {"iss":"Test auth","sub":"","aud":"Docker Registry","exp":1551674246,"nbf":1551673336,"iat":1551673346,"jti":"8848343205205713016","access":[{"type":"registry","name":"catalog","actions":["*"]}]}
I0304 04:22:26.761559 1 server.go:371] Auth request: {:@172.17.0.4 [{repository testrepo/testrepo [*]}]}
I0304 04:22:26.762597 1 server.go:217] Authn static -> true, map[], <nil>
I0304 04:22:26.763542 1 acl.go:116] { * repository testrepo/testrepo} matched {"Match":{"ip":"172.16.0.0/12"},"Actions":["pull"],"Comment":"Allow docker internal network (for web UI)"} (Comment: %!s(*string=0xc42011af10))
I0304 04:22:26.764306 1 server.go:239] Authz static ACL { * repository testrepo/testrepo} -> [], %!s(<nil>)
I0304 04:22:26.776109 1 server.go:329] New token for {:@172.17.0.4 [{repository testrepo/testrepo [*]}]} map[]: {"iss":"Test auth","sub":"","aud":"Docker Registry","exp":1551674246,"nbf":1551673336,"iat":1551673346,"jti":"4247362744326423396","access":[{"type":"repository","name":"testrepo/testrepo","actions":[]}]}```
I have the same problem. I'll try to explain:
what I want to achieve
I want a registry which is read-only (no push) for the public (say any unauthenticated user) and read-write for any authenticated user.
the base problem
With the aforementioned configuration, when you curl your registry anonymously like so:
curl -i https://my.docker.registry.domain.com/v2/_catalog
you obtain a 401 result code with an extra header
HTTP/2 401
server: nginx
date: Tue, 30 Apr 2019 16:58:16 GMT
content-type: application/json; charset=utf-8
content-length: 145
docker-distribution-api-version: registry/2.0
www-authenticate: Bearer realm="https://my.docker.registry.domain.com/auth",service="The Docker Registry",scope="registry:catalog:*"
x-content-type-options: nosniff
strict-transport-security: max-age=31536000; preload
{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":[{"Type":"registry","Class":"","Name":"catalog","Action":"*"}]}]}
that you should follow to obtain a token. I completely understand that, from the user perspective, you are unauthenticated and so you should not do this fake-authentication in order to get to the content; but from the registry-container's perspective, every request should have a token, because ACLs management is delegated to the auth-container.
When you use a client like this, everything works ok, probably because this client is handling the authentication header.
possible solutions
I'm not sure that we can solve this in both ends. We should have a registry configuration that allows unauthenticated readonly access, without requesting the auth token. And this is, IMHO, the preferred solution. But I don't think that we can solve this issue from the auth side: if it's "unauthenticated access" an authenticator is useless...
A possible solution is from the client side: if the client receives the www-authenticate header, it should honor it.
I'm exploring a new solution: I have a proxy in front of these containers, so I'm trying to inject the token header if it's not present. I'll report on this when I'm done.
a note about my setup
In my case, I have 3 docker containers:
- registry which handles all /v2 requests
- auth which handles all /auth requests
- registry-webui which handles / and all other requests these containers are behind an nginx proxy, which terminates the SSL (and manages letsencrypt certs also)
The header-injection test wasn't a good idea: for every (anonymous) request, this was passed to a "middle" container that:
- forward the unauthenticated request to the registry
- get the www-authenticate header from the response
- do the authentication with the auth container
- forward the original request to the registry, with the injected token header
That was expensive.
So I simply found another registry-webUI that does the job.
More details here