Fix 403 error when using job token to access public or internal project
What does this MR do and why?
Problem
Users reported that they face a 403 forbidden
error when trying to reference a terraform module from a public or internal project.
After some investigation, it turned out that once a new public or internal project is created, its project_feature.package_registry_access_level
is set to ProjectFeature::ENABLED
. However, we grant the :read_package
ability only if the project's project_feature.package_registry_access_level
is ProjectFeature::PUBLIC
.
Why does this happen only when authenticating with a job token? because any user authenticated by a job token is restricted from public access unless it is explicitly allowed.
The flow can be summarized as follows:
- A job in
project 1
is trying to get the versions of a terraform module, and this module is in publicproject 2
:
stages:
- build
build:
stage: build
image: curlimages/curl:latest
script:
- curl -v "${CI_API_V4_URL}/packages/terraform/modules/v1/public-namespace/infra-registry/aws/versions" --header "Authorization:Bearer $CI_JOB_TOKEN"
only:
- main
- Since
project 2
is public & its package registry is enabled, the expected behavior is that the job will succeed and the request will return a200
response, however, a 403 forbidden response is returned in the above job. - When the request goes through GitLab, the authentication gate starts to grant/prevent abilities to the user.
-
This rule would prevent a couple of abilities because the user is authenticated using a job token. As mentioned above, GitLab should check if
project 1
can accessproject 2
using this job token. The prevented:public_access
ability is the one that's responsible for making the job token authenticated requests fail while the other token authenticated requests pass. Because in the case of the other token authenticated requests, the:public_access
ability is granted. - For the
:read_package
ability, the Packages::Policies::ProjectPolicy is responsible for figuring out if it's allowed or not. And as we can see in this condition, the policy allows it if the project'spackage_registry_access_level
isProjectFeature::PUBLIC
, which is not the case in the newly created public projects; theirpackage_registry_access_level
isProjectFeature::ENABLED
.
Solution
Add a new rule in the Packages::Policies::ProjectPolicy to grant the :read_package
ability if the project is public or internal and its job_token_package_registry
is true
.
MR acceptance checklist
Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Screenshots or screen recordings
Screenshots are required for UI changes, and strongly recommended for all other merge requests.
Before | After |
---|---|
How to set up and validate locally
- In rails console, create a terraform module in a public project & a ci build to test with its token:
# stub file upload
def fixture_file_upload(*args, **kwargs)
Rack::Test::UploadedFile.new(*args, **kwargs)
end
project = FactoryBot.create(:project, :public)
terraform_module = FactoryBot.create(:terraform_module_package, project: project)
# keep the namespace full path to use it below in the curl command in step 2
full_path = project.namespace.full_path
# keep the terraform_module name to use it below in the curl command in step 2
terraform_module_name = terraform_module.name
ci_build = FactoryBot.create(:ci_build, :running, user: User.first)
# keep the ci_build token to use it below in the curl command in step 2
ci_build_token = ci_build.token
- In the terminal, query the versions endpoint for the terraform module you just created:
curl "http://gdk.test:3000/api/v4/packages/terraform/modules/v1/<full_path>/<terraform_module_name>/versions" --header "Authorization:Bearer <ci_build_token>"
- On the
master
branch, the above curl query will result in{"message":"403 Forbidden"}
error. Switch to this MR branch and repeat the query: it should return 200 with the correct response:
{"modules":[{"versions":[{"version":"1.0.0","submodules":[],"root":{"dependencies":[],"providers":[{"name":"system","version":""}]}}],"source":"http://gdk.test:3000/namespace12/project-12"}]}
Related to #435647 (closed) & #438586 (closed)