403 forbidden error when using job token to access public or internal project
Problem
Users reported that they face a 403 forbidden
error when trying to reference a terraform module from a public project.
After some investigation, it turned out that once a new public project is created, its project_feature.package_registry_access_level
is set to ProjectFeature::ENABLED
. However, we expect it to be ProjectFeature::PUBLIC
to grant the user the :read_package
permission.
Why does this happen only when authenticating with a job token? because by default the 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
- A quick fix is adding a new condition in the Packages::Policies::ProjectPolicy to grant the
:read_package
ability if the project is public and itspackage_registry_access_level
isProjectFeature::ENABLED
. - A root cause fix is to change the default of the newly created projects'
package_registry_access_level
fromProjectFeature::ENABLED
toProjectFeature::PUBLIC
, and then add a data migration to update all existing public projects.