Draft: Resolve "Allow protecting container repositories against writes [POC]"
What does this MR do and why?
This MR will not be merged. The intention is to discuss and document design decision and implemenation details.
This MR is just a proof of concept for the &9825 and #18984 (closed) .
A proposal for implementation plan can be found in #18984 (comment 1447200733)
Proposal for data model
- In the current implementation, the container registry uses the GitLab as the authentication service because GitLab knows about the users and their connections to projects, etc. => this means, that when sending
docker
commands to the container registry, GitLab receives auth-related requests, e.g.:- For the command
docker login registry.test:5000 -u gitlab-token"
, the container registry sends the following auth request:GET "/jwt/auth?account=gitlab-token&client_id=docker&offline_token=[FILTERED]&service=container_registry" with params {"account"=>"gitlab-token", "client_id"=>"docker", "offline_token"=>"[FILTERED]", "service"=>"container_registry"}
- For the command
docker push flightjs/flight:v1.0
, the container registry sends the following auth request:GET "/jwt/auth?account=gitlab-token&scope=repository%3Aflightjs%2Fflight%3Apush%2Cpull&service=container_registry" with params {"account"=>"gitlab-token", "scope"=>"repository:flightjs/flight:push,pull", "service"=>"container_registry"}
- For the command
- I propose to tap into this logic / sequence and check if the respective container repository is protected or not, see the following sequence
sequenceDiagram
actor User
User->>+registry.test : docker push registry.test:5000/flightjs/flight:v1.17
registry.test->>+GitLab: GET "/jwt/auth?account=gitlab-token&scope=repository%3Aflightjs%2Fflight%3Apush%2Cpull&service=container_registry"
GitLab->>GitLab: JwtController
GitLab->>GitLab: Auth::ContainerRegistryAuthenticationService
GitLab->>GitLab: Check ContainerRegistry::ContainerRepositoryProtectionRule
GitLab-->>-registry.test: HTTP response code 200 (SUCCESS)
registry.test->>-registry.test: Store container image flightjs/flight:v1.0
registry.test-->>User: SUCCESS MESSAGE
- The
ContainerRegistry::ContainerRepositoryProtectionRule
is a new model that contains the information about which container repository is protected or not.
classDiagram
class `ContainerRegistry::ContainerRepositoryProtectionRule`{
id bigint,
container_path string
matching_package_version string
push_protected_up_to_access_level Gitlab::Access
delete_protected_up_to_access_level Gitlab::Access
project_id bigint
namespace_id bigint
}
`ContainerRegistry::ContainerRepositoryProtectionRule` --> `Project`
`ContainerRepository` --> `Project`
`Project` --> `Group (Namespace)`
Screenshots or screen recordings
The following console output shows the expected behavior. In the first part, the protection rule is set to no access and all container images with the name flightjs/flight
are protected. In the second part, we protect only against developer roles and pushing the image is accepted because we push with the root user.
➜ gitlab git:(18984-feature-protected-containers-proof-of-concept) ✗ rails r 'ContainerRegistry::ContainerRepositoryProtectionRule.first.update(push_protected_up_to_access_level: Gitlab::Access::NO_ACCESS)'
➜ gitlab git:(18984-feature-protected-containers-proof-of-concept) ✗ docker push registry.test:5000/flightjs/flight:v1.17
The push refers to repository [registry.test:5000/flightjs/flight]
a7866053acac: Preparing
denied: container protected; you are not allowed to push new package
➜ gitlab git:(18984-feature-protected-containers-proof-of-concept) ✗ rails r 'ContainerRegistry::ContainerRepositoryProtectionRule.first.update(push_protected_up_to_access_level: Gitlab::Access::DEVELOPER)'
➜ gitlab git:(18984-feature-protected-containers-proof-of-concept) ✗ docker push registry.test:5000/flightjs/flight:v1.17
The push refers to repository [registry.test:5000/flightjs/flight]
a7866053acac: Layer already exists
v1.17: digest: sha256:efebf0f7aee69450f99deafe11121afa720abed733943e50581a9dc7540689c8 size: 525
How to set up and validate locally
- Enable the container registry, see https://gitlab.com/gitlab-org/gitlab-development-kit/blob/main/doc/howto/registry.md (<= setup as HTTP and not HTTPS)
-
rails db:migrate
to create the new table forContainerRepositoryProtectionRule
- Create a
ContainerRepositoryProtectionRule
to protect against pushing new container images with the name"flightjs/flight"
rails r 'ContainerRegistry::ContainerRepositoryProtectionRule.create(container_path: "flightjs/flight", push_protected_up_to_access_level: Gitlab::Access::NO_ACCESS, delete_protected_up_to_access_level: Gitlab::Access::NO_ACCESS, project: Project.find(7), namespace: Project.find(7).namespace)'
- In another terminal session, prepare your container image
docker login registry.test:5000 -u gitlab-token -p "ypCa3Dzb23o5nvsixwPA"
docker pull hello-world:latest
docker tag hello-world:latest registry.test:5000/flightjs/flight:v1.0
- Push the container image
docker push registry.test:5000/flightjs/flight:v1.0
=> will be blocked byContainerRepositoryProtectionRule
Personal Notes
- A good guide to setup the container registry with gdk: https://gitlab.com/gitlab-org/gitlab-development-kit/blob/main/doc/howto/registry.md
- Reference document for the jwt token used for authentication in the container registry: https://github.com/distribution/distribution/blob/main/docs/spec/auth/scope.md
-
ContainerRepository
is created when a user visits the page/container_registry
, see https://gitlab.com/gitlab-community/gitlab/-/blob/6446d14354571e6bf8f8baf3ef9828d953585aff/app/controllers/projects/registry/repositories_controller.rb#L45 - Initial design discussion related the container registry: gitlab-foss#3299 (comment 4265537)
MR acceptance checklist
This MR will not be merged. The intention is to discuss and document design decision and implemenation details.
Related to Allow protecting container repositories against... (#18984 - closed) and Container Registry: Granular protection for rep... (&9825)