Skip to content

Dependency Proxy: Add group access token scope checks

📓 History

!129697 (merged) added scope checks for group access tokens accessing the Dependency Proxy. This MR had to be reverted because it caused service account users to lose access to Dependency Proxy (incident).

This MR implements Add scope checks when using Dependency Proxy wi... (!129697 - merged) changes but keep user types access that previously had access to the dependency proxy (For example service_account)

To reduce the scope of this MR, some specs have been extracted into a separate MR Improve spec coverage for dependency proxy for ... (!136946 - merged).

🌱 Context

Dependency Proxy Authorization and Authentication Overview

🔬 Details

docker login sends these GET requests:

  • /v2/ -> Groups::DependencyProxyAuthController#authenticate
  • /jwt/auth -> JwtController#auth

docker pull sends these GET requests:

  • /v2/ -> Groups::DependencyProxyAuthController#authenticate
  • /jwt/auth -> JwtController#auth
  • /v2/<group_path>/dependency_proxy/containers/<image_name>/manifests/<image_tag> -> Groups::DependencyProxyForContainersController#manifest

Problem 💥

docker login and docker pull requests from a group access token with insufficient scopes for Dependency Proxy are not rejected.

We can add the scope checks, but where should we add it?

In policy code (in GroupPolicy, to be specific), we are passed only the token user and the dependency proxy group. We do not have a handle to the actual token. We considered doing bot_user.personal_access_tokens.first but decided it will not work, because it is possible to add tokens even to a bot user. As such, we cannot determine if a group access token has the correct scopes in a policy code.

In the controller code, when we authenticate the token, we have a handle to the token, but we do not know the dependency proxy group

Solution 🚑

  • We do group access token scope checking in Auth::DependencyProxyAuthenticationService, called from JwtController
  • We do group membership checking in GroupPolicy

Screenshots or screen recordings

No UI changes 🌈

How to set up and validate locally

Enable Dependency Proxy for a group

Testing new behavior

A group access token with insufficient scopes should fail docker login

  1. Create a group access token for the group. Give it only the read_registry scope.
  2. Clear docker credentials: docker logout http://gdk.test:3000
  3. Enable the feature flag for the token. Run in Rails console:
    token = PersonalAccessToken.find_by_name('<name_of_token>')
    Feature.enable(:packages_dependency_proxy_containers_scope_check, token.user)
  4. Login: docker login http://gdk.test:3000 -p <group_access_token>. The login should fail.
  5. Disable the feature flag for the token. Run in Rails console:
    token = PersonalAccessToken.find_by_name('<name_of_token>')
    Feature.disable(:packages_dependency_proxy_containers_scope_check, token.user)
  6. Feature flags are cached. It might be necessary to restart GDK before proceeding to the next step, to have the updated feature flag value in effect.
  7. Login: docker login http://gdk.test:3000 -p <group_access_token>. The login should succeed.

Verifying that existing behavior still works

A group access token with sufficient scopes should be able to docker pull

  1. Create another group access token, this time give it both read_registry and write_registry scopes.
  2. Login with the newer token. It should be successful.
  3. Pull an image: docker pull gdk.test:3000/<group-namespace>/dependency_proxy/containers/alpine:latest. This should also be successful.
  4. Open the group dependency proxy page (Group home -> Operate -> Dependency Proxy, or http://gdk.test:3000/groups/<group-namespace>/-/dependency_proxy), you should see the pulled image in the list of images

A revoked group access token, even with sufficient scopes, should fail docker login and docker pull

  1. Revoke the token used for the successful pull in the previous step. From the Rails console, run PersonalAccessToken.last.revoke!
  2. Try the docker pull again: docker pull gdk.test:3000/<group-namespace>/dependency_proxy/containers/alpine:latest. This time the operation should fail with a forbidden response. This confirms that we do our auth checks for every operation, even if the user is already logged in.
  3. Try docker login again with the token. This time the login should fail.

A group access token with api scope should be able to docker pull

The api scope grants read_registry and write_registry access (documentation).

  1. Create a new group access token, this time give it only the api scope.
  2. docker login and docker pull should be both successful just like A group access token with sufficient scopes

MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

Related to #431386

Edited by Radamanthus Batnag

Merge request reports