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 fromJwtController - 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
- Create a group access token for the group. Give it only the
read_registryscope. - Clear docker credentials:
docker logout http://gdk.test:3000 - 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) - Login:
docker login http://gdk.test:3000 -p <group_access_token>. The login should fail. - 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) - 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.
- 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
- Create another group access token, this time give it both
read_registryandwrite_registryscopes. - Login with the newer token. It should be successful.
- Pull an image:
docker pull gdk.test:3000/<group-namespace>/dependency_proxy/containers/alpine:latest. This should also be successful. - 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
- Revoke the token used for the successful pull in the previous step. From the Rails console, run
PersonalAccessToken.last.revoke! - Try the
docker pullagain: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. - Try
docker loginagain 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).
- Create a new group access token, this time give it only the
apiscope. -
docker loginanddocker pullshould 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.
-
I have evaluated the MR acceptance checklist for this MR.
Related to #431386 (closed)