How to disable users to perform push operation in a private docker-registry with basic authentication
I have configured in a given machine a private docker registry on CentOS 6.6 with basic authentication by using docker-registry 0.8.1-2 docker-io 1.2.0-3 nginx 1.6.2-1
I have included authorized users in the docker-registry.htpasswd.
Until now I have not found yet a way that allows me to disable part of the authorized users to perform docker push operation. Basically I would like to allow some users to only perform pull operation, whilst others also push.
Do you think it is possible such configuration without using docker hub?
I assume you can do that by restricting access to the PUT http method - but this is untested territories.
I can test it for you. Do you have any idea how to do the configuration? Or can you provide me with useful documentation? Basically only some users can run 'docker push ...' with success. All the others can just run pull.
I never went down that road myself, and I don't believe there is specific docs about it - but configuring nginx shouldn't be too hard.
+1
There is probably a nicer way to do that with nginx, but I have been succesfully using this for HTTPS termination and PUT/POST/DELETE basic auth protection:
server {
listen 80;
server_name myregisty.net;
location / {
rewrite ^(.*)$ https://myregisty.net$1 last;
}
}
server {
listen 443;
server_name myregisty.net;
error_page 588 = @readrequests;
ssl on;
ssl_certificate /etc/nginx/ssl/wildcard.myregisty.net.pem;
ssl_certificate_key /etc/nginx/ssl/wildcard.myregisty.net.pem;
ssl_dhparam /etc/nginx/ssl/wildcard.myregisty.net.pem;
if ($request_method = 'GET') {
return 588;
}
if ($request_method = 'HEAD') {
return 588;
}
if ($request_method = 'OPTIONS') {
return 588;
}
location / {
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
client_max_body_size 800M; # avoid HTTP 413 for large image uploads
chunked_transfer_encoding on; # required to avoid HTTP 411: see Issue #1486 (https://github.com/dotcloud/docker/issues/1486)
proxy_pass http://127.0.0.1:5000;
}
location @readrequests {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
client_max_body_size 800M; # avoid HTTP 413 for large image uploads
chunked_transfer_encoding on; # required to avoid HTTP 411: see Issue #1486 (https://github.com/dotcloud/docker/issues/1486)
proxy_pass http://127.0.0.1:5000;
}
}
Users can be added via htpasswd -c /etc/nginx/.htpasswd newuser
I am going to try what you suggested. Let you know as soon as possible.
However, do you think it is possible to support anonymous access to get images (so for pulling images) and register users that have to perform the push operation?
That's exactly what the snippet above achieves: no auth for pulls, but auth required (via docker login which just creates a ~/.dockercfg) for push/deletes.
Thanks.
@bjaglin I tried it and works perfect for me on pull/push/login commands !!! this shall be default configuration in nginx-registry, recommend to put under https://github.com/docker/docker-registry/tree/master/contrib/nginx
One extra questions for messages on unauthorized request (push without login)
docker@boot2docker:~$ docker push dokk.co/hello-world
The push refers to a repository [dokk.co/hello-world] (len: 1)
Sending image list
It doesn't clearly indicate the http 401 error, which I can see in nginx log, like
172.17.42.1 - - [01/Dec/2014:04:27:59 +0000] "PUT /v1/repositories/hello-world/ HTTP/1.1" 401 194 "-" "docker/1.3.2 go/go1.3.3 git-commit/39fa2fa kernel/3.16.7-tinycore64 os/linux arch/amd64"
or docker.log like
[debug] http.go:162 https://dokk.co/v1/repositories/hello-world/ -- HEADERS: map[User-Agent:
[docker/1.3.2 go/go1.3.3 git-commit/39fa2fa kernel/3.16.7-tinycore64 os/linux arch/amd64]
Authorization:[Basic Og==]]
Error: Status 401 trying to push repository hello-world: <html>
<head><title>401 Authorization Required</title></head>
<body bgcolor="white">
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx/1.6.2</center>
</body>
</html>
[998cd712] -job push(dokk.co/hello-world) = ERR (1)
Can we add extra configuration in nginx to response better warning
@bjaglin I have tried your solution. I confirm the same behaviour obtained by @larrycai. Where should I introduce the http check to obtain a clear error in case of unauthorized request?
@bjaglin for line
rewrite ^(.*)$ https://myregisty.net$1 last;
could be better for below ?
rewrite ^(.*)$ https://$host$1 last;
so if I test it locally, curl http://localhost it can redirect to https://localhost (I run it under nginx docker environment)
Have you found a way to return HTTP 401 error for users unregistered?
Hi,
I have tested a slightly shorter nginx configuration, which seems to allow pull for everyone but restrict push for authenticated users.
registry.conf:
server {
...
location /v1/_ping {
auth_basic off;
include registry.proxy;
}
location /v1 {
include registry.auth;
include registry.proxy;
}
}
registry.proxy:
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Authorization "";
proxy_pass http://127.0.0.1:5000;
registry.auth:
limit_except GET HEAD {
auth_basic "Docker Registry";
auth_basic_user_file registry.passwd;
}
I have split registry.auth in a different file so that I can change its content when I run the docker-registry container. The nginx startup script I use selects an appropriate registry.auth file depending on the value of an environment variable, so I can have one that completely disable authentication, for exemple.
@larrycai @eolamey thanks for the tips! My config is now down to:
server {
listen 80;
server_name myregisty.net;
location / {
rewrite ^(.*)$ https://$host$1 last;
}
}
server {
listen 443;
server_name myregisty.net;
ssl on;
ssl_certificate /etc/nginx/ssl/wildcard.myregisty.net.pem;
ssl_certificate_key /etc/nginx/ssl/wildcard.myregisty.net.pem;
ssl_dhparam /etc/nginx/ssl/wildcard.myregisty.net.pem;
location / {
limit_except GET HEAD OPTIONS {
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
}
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
client_max_body_size 800M; # avoid HTTP 413 for large image uploads
chunked_transfer_encoding on; # required to avoid HTTP 411: see Issue #1486 (https://github.com/dotcloud/docker/issues/1486)
proxy_pass http://127.0.0.1:5000;
}
}
@bjaglin did you solve the problem I mentioned above ? cc @joda70
not clear message in docker push without authentication, though it shows 401 error in daemon logs.
It looks there are code in docker daemon to handle this logic, don't know why it doesn't work. https://github.com/docker/docker/blob/master/api/client/commands.go (func CmdPush)
if err := push(authConfig); err != nil {
if strings.Contains(err.Error(), "Status 401") {
fmt.Fprintln(cli.out, "\nPlease login prior to push:")
@bjaglin like the new conf, quite clean, I also add simple LDAP certification as reference, see https://github.com/larrycai/nginx-registry , but above problem still there
Hi
This is how we're deal with push using apache 2.2 + ldap (centos6) insted of nginx
<AuthnProviderAlias ldap ldap_confi>
AuthLDAPURL 'ldap://xxx.yyy.zzz o=example,dc=com?uid?sub?(objectClass=*)'
</AuthnProviderAlias>
<Location />
AuthName "Registry Authentication"
AuthType basic
AuthBasicProvider ldap_config
AuthzLDAPAuthoritative on
<Limit PUT>
AuthLDAPURL "ldap://xxx.yyy.zzz o=example,dc=com?uid?sub?(objectClass=*)"
AuthLDAPGroupAttributeIsDN on
AuthLDAPGroupAttribute member
AuthLDAPRemoteUserAttribute uid
AuthLDAPRemoteUserIsDN on
Require ldap-group cn=admins,ou=metagroups,o=example,dc=com
</Limit>
<LimitExcept PUT>
Require valid-user
</LimitExcept>
</Location>
<Location /v1/_ping>
Satisfy any
Allow from all
</Location>
<Location /_ping>
Satisfy any
Allow from all
</Location>
Now I'm trying to add a new feature , a user can only push a img to it's own repository /v1/repositories/<USER>
@ssalvatori Are you running the apache + ldap on a container? Why have you decided to use apache instead of Nginx?
Have you run docker-registry and nginx in two different containers?
Have you tried to change the nginx.conf configuration for the registry:2?
Hm, meanwhile it seems that if
docker login
requests
GET /v2/
and doesn't get the request to authenticate it will not save the credentials.
If you later on docker push to that site it will break on the HTTP 401s its getting from the post requests and will not try to authenticate then.
@dothebart when using registry v2, nginx is useless.
you can use docker_auth image to realize this object, expecially more complex user ACLS. Please looking https://github.com/cesanta/docker_auth for more