docker-registry-ui icon indicating copy to clipboard operation
docker-registry-ui copied to clipboard

Registry authentication only for pushing and deleting images

Open mjeco opened this issue 3 years ago • 1 comments

Is it possible to set up registry authentication in such a way where pulling images does not require any authentication but pushing and deleting images requires valid credentials? I have the following Nginx configuration where I try to limit the auth_basic to everything except for GET requests.

/etc/nginx/conf.d/default.conf

server {
  listen 443 ssl;
  server_name registry.kemptech.net;
  root /usr/share/nginx/html;

  ssl_certificate /etc/nginx/conf.d/domain.crt;
  ssl_certificate_key /etc/nginx/conf.d/domain.key;

  ssl_protocols TLSv1.1 TLSv1.2;
  ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
  ssl_prefer_server_ciphers on;
  ssl_session_cache shared:SSL:10m;

  client_max_body_size 0;
  chunked_transfer_encoding on;

  location /v2/ {
    if ($http_user_agent ~ "^(docker\/1\.(3|4|5(?!\.[0-9]-dev))|Go ).*$" ) {
      return 404;
    }
    
    limit_except GET {
      auth_basic "Registry realm";
      auth_basic_user_file /etc/nginx/auth/.htpasswd;
    }

    proxy_pass                          http://registry:5000;
    proxy_set_header  Host              $http_host;   # required for docker client's sake
    proxy_set_header  X-Real-IP         $remote_addr; # pass on real client's IP
    proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header  X-Forwarded-Proto $scheme;
    proxy_read_timeout                  900;
  }
}
  
server {
  listen 80;
  location / {
    return 301 https://$host$request_uri;
  }
}

Pulling images with docker pull works as expected, i.e. without any authentication.

$ docker pull localhost/alpine:3.12
3.12: Pulling from alpine
Digest: sha256:a9c28c813336ece5bb98b36af5b66209ed777a394f4f856c6e62267790883820
Status: Image is up to date for localhost/alpine:3.12
localhost/alpine:3.12

Deleting images from the UI also works as expected. I get prompted for credentials and the image is deleted only when a valid username and password is provided (the one from the .htaccess file).

The problem seems to be with pushing images with docker push. It is failing with authentication error even after a successful docker login localhost command.

$ docker push localhost/alpine:3.12
The push refers to repository [localhost/alpine]
32f366d666a5: Layer already exists
unauthorized: authentication required

I tried limit_except GET HEAD too but the same issue is seen.

Logs from the registry and UI when doing a docker push

registry_1  | time="2021-05-05T08:09:20.2269273Z" level=info msg="response completed" go.version=go1.11.2 http.request.host=registry.kemptech.net http.request.id=c925e1fa-7fd7-4340-9886-ed3665db41e4 http.request.method=GET http.request.remoteaddr=172.20.0.1 http.request.uri="/v2/" http.request.useragent="docker/20.10.5 go/go1.13.15 git-commit/363e9a8 kernel/4.19.84-microsoft-standard os/linux arch/amd64 UpstreamClient(Docker-Client/20.10.5 \(windows\))" http.response.contenttype="application/json; charset=utf-8" http.response.duration=1.1056ms http.response.status=200 http.response.written=2 
ui_1        | 172.20.0.1 - - [05/May/2021:08:09:20 +0000] "GET /v2/ HTTP/1.1" 200 2 "-" "docker/20.10.5 go/go1.13.15 git-commit/363e9a8 kernel/4.19.84-microsoft-standard os/linux arch/amd64 UpstreamClient(Docker-Client/20.10.5 \x5C(windows\x5C))" "-"
registry_1  | 172.20.0.3 - - [05/May/2021:08:09:20 +0000] "GET /v2/ HTTP/1.0" 200 2 "" "docker/20.10.5 go/go1.13.15 git-commit/363e9a8 kernel/4.19.84-microsoft-standard os/linux arch/amd64 UpstreamClient(Docker-Client/20.10.5 \\(windows\\))"
ui_1        | 172.20.0.1 - - [05/May/2021:08:09:20 +0000] "HEAD /v2/alpine/blobs/sha256:339de151aab4bc06eed8409daae147c408478cb538dacb90cc63f19ad4eba80b HTTP/1.1" 200 0 "-" "docker/20.10.5 go/go1.13.15 git-commit/363e9a8 kernel/4.19.84-microsoft-standard os/linux arch/amd64 UpstreamClient(Docker-Client/20.10.5 \x5C(windows\x5C))" "-"
registry_1  | 172.20.0.3 - - [05/May/2021:08:09:20 +0000] "HEAD /v2/alpine/blobs/sha256:339de151aab4bc06eed8409daae147c408478cb538dacb90cc63f19ad4eba80b HTTP/1.0" 200 0 "" "docker/20.10.5 go/go1.13.15 git-commit/363e9a8 kernel/4.19.84-microsoft-standard os/linux arch/amd64 UpstreamClient(Docker-Client/20.10.5 \\(windows\\))"
registry_1  | time="2021-05-05T08:09:20.2736337Z" level=info msg="response completed" go.version=go1.11.2 http.request.host=registry.kemptech.net http.request.id=4405b63d-3d7e-466f-93a9-ac891cf71037 http.request.method=HEAD http.request.remoteaddr=172.20.0.1 http.request.uri="/v2/alpine/blobs/sha256:339de151aab4bc06eed8409daae147c408478cb538dacb90cc63f19ad4eba80b" http.request.useragent="docker/20.10.5 go/go1.13.15 git-commit/363e9a8 kernel/4.19.84-microsoft-standard os/linux arch/amd64 UpstreamClient(Docker-Client/20.10.5 \(windows\))" http.response.contenttype="application/octet-stream" http.response.duration=1.4251ms http.response.status=200 http.response.written=0 
registry_1  | time="2021-05-05T08:09:20.2821575Z" level=info msg="response completed" go.version=go1.11.2 http.request.host=registry.kemptech.net http.request.id=06d27277-2a26-4581-afb9-984e38ca350f http.request.method=HEAD http.request.remoteaddr=172.20.0.1 http.request.uri="/v2/alpine/blobs/sha256:13621d1b12d4d0d4ad6699d220cd46f26764a9daf26a95f792ab2ce0715df366" http.request.useragent="docker/20.10.5 go/go1.13.15 git-commit/363e9a8 kernel/4.19.84-microsoft-standard os/linux arch/amd64 UpstreamClient(Docker-Client/20.10.5 \(windows\))" http.response.contenttype="application/octet-stream" http.response.duration=1.3505ms http.response.status=200 http.response.written=0 
registry_1  | 172.20.0.3 - - [05/May/2021:08:09:20 +0000] "HEAD /v2/alpine/blobs/sha256:13621d1b12d4d0d4ad6699d220cd46f26764a9daf26a95f792ab2ce0715df366 HTTP/1.0" 200 0 "" "docker/20.10.5 go/go1.13.15 git-commit/363e9a8 kernel/4.19.84-microsoft-standard os/linux arch/amd64 UpstreamClient(Docker-Client/20.10.5 \\(windows\\))"
ui_1        | 172.20.0.1 - - [05/May/2021:08:09:20 +0000] "HEAD /v2/alpine/blobs/sha256:13621d1b12d4d0d4ad6699d220cd46f26764a9daf26a95f792ab2ce0715df366 HTTP/1.1" 200 0 "-" "docker/20.10.5 go/go1.13.15 git-commit/363e9a8 kernel/4.19.84-microsoft-standard os/linux arch/amd64 UpstreamClient(Docker-Client/20.10.5 \x5C(windows\x5C))" "-"
ui_1        | 172.20.0.1 - - [05/May/2021:08:09:20 +0000] "PUT /v2/alpine/manifests/3.12 HTTP/1.1" 401 179 "-" "docker/20.10.5 go/go1.13.15 git-commit/363e9a8 kernel/4.19.84-microsoft-standard os/linux arch/amd64 UpstreamClient(Docker-Client/20.10.5 \x5C(windows\x5C))" "-"

Looks like the PUT request from UI is returning a 401 error.

When I remove the limit_except from the Nginx config then docker push works with valid credentials but authentication is also required for docker pull which I do not want.

mjeco avatar May 05 '21 08:05 mjeco

Hi there, thank you for your issue.

I tried your example and I'm facing the same issue.

I think what you want to do is impossible or complicated. To know if it should connect or not, docker engine makes a first GET request on /v2. If the response is 401 it will try to your credentials.

That means, for a PULL or PUSH, it will always do a GET request on /v2 first...

Maybe you can try keycloak auth, it uses scopes on every requests, maybe we can configure it ?

Joxit avatar May 27 '21 10:05 Joxit

Hi, I close this issue due to inactivity.

Joxit avatar Oct 21 '22 19:10 Joxit