Skip to content

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 public project 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 a 200 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 access project 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's package_registry_access_level is ProjectFeature::PUBLIC, which is not the case in the newly created public projects; their package_registry_access_level is ProjectFeature::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 its package_registry_access_level is ProjectFeature::ENABLED.
  • A root cause fix is to change the default of the newly created projects' package_registry_access_level from ProjectFeature::ENABLED to ProjectFeature::PUBLIC, and then add a data migration to update all existing public projects.