New CI job permissions model

Introduced in GitLab 8.12.

GitLab 8.12 has a completely redesigned job permissions system. You can find all discussion and all our concerns when choosing the current approach in issue #18994.

Jobs permissions should be tightly integrated with the permissions of a user who is triggering a job.

The reasons to do it like that are:

With the new behavior, any job that is triggered by the user, is also marked with their read permissions. When a user does a git push or changes files through the web UI, a new pipeline will be usually created. This pipeline will be marked as created by the pusher (local push or via the UI) and any job created in this pipeline will have the read permissions of the pusher but not write permissions.

This allows us to make it really easy to evaluate the access for all projects that have Git submodules or are using container images that the pusher would have access too. The permission is granted only for the time that the job is running. The access is revoked after the job is finished.

Types of users

It is important to note that we have a few types of users:

This allows us to make the CI and permission system more trustworthy. Let's consider the following scenario:

  1. You are an employee of a company. Your company has a number of internal tools hosted in private repositories and you have multiple CI jobs that make use of these repositories.

  2. You invite a new external user. CI jobs created by that user do not have access to internal repositories, because the user also doesn't have the access from within GitLab. You as an employee have to grant explicit access for this user. This allows us to prevent from accidental data leakage.

Job token

A unique job token is generated for each job and provides the user read access all projects that would be normally accessible to the user creating that job. The unique job token does not have any write permissions, but there is a proposal to add support.

We try to make sure that this token doesn't leak by:

  1. Securing all API endpoints to not expose the job token.
  2. Masking the job token from job logs.
  3. Granting permissions to the job token only when the job is running.

However, this brings a question about the Runners security. To make sure that this token doesn't leak, you should also make sure that you configure your Runners in the most possible secure way, by avoiding the following:

  1. Any usage of Docker's privileged mode is risky if the machines are re-used.
  2. Using the shell executor since jobs run on the same machine.

By using an insecure GitLab Runner configuration, you allow the rogue developers to steal the tokens of other jobs.

Before GitLab 8.12

In versions before GitLab 8.12, all CI jobs would use the CI Runner's token to checkout project sources.

The project's Runner's token was a token that you could find under the project's Settings > Pipelines and was limited to access only that project. It could be used for registering new specific Runners assigned to the project and to checkout project sources. It could also be used with the GitLab Container Registry for that project, allowing pulling and pushing Docker images from within the CI job.

GitLab would create a special checkout URL like:

https://gitlab-ci-token:<project-runners-token>/gitlab.com/gitlab-org/gitlab-foss.git

And then the users could also use it in their CI jobs all Docker related commands to interact with GitLab Container Registry. For example:

docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.gitlab.com

Using single token had multiple security implications:

All the above led to a new permission model for jobs that was introduced with GitLab 8.12.

Making use of the new CI job permissions model

With the new job permissions model, there is now an easy way to access all dependent source code in a project. That way, we can:

  1. Access a project's dependent repositories
  2. Access a project's Git submodules
  3. Access private container images
  4. Access project's and submodule LFS objects

Below you can see the prerequisites needed to make use of the new permissions model and how that works with Git submodules and private Docker images hosted on the container registry.

Prerequisites to use the new permissions model

With the new permissions model in place, there may be times that your job will fail. This is most likely because your project tries to access other project's sources, and you don't have the appropriate permissions. In the job log look for information about 403 or forbidden access messages.

In short here's what you need to do should you encounter any issues.

As an administrator:

As a user:

Dependent repositories

The Job environment variable CI_JOB_TOKEN can be used to authenticate any clones of dependent repositories. For example:

git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/<user>/<mydependentrepo>.git

It can also be used for system-wide authentication (only do this in a docker container, it will overwrite ~/.netrc):

echo -e "machine gitlab.com\nlogin gitlab-ci-token\npassword ${CI_JOB_TOKEN}" > ~/.netrc

Git submodules

To properly configure submodules with GitLab CI, read the Git submodules documentation.

Container Registry

With the update permission model we also extended the support for accessing Container Registries for private projects.

Notes:

  • GitLab Runner versions prior to 1.8 don't incorporate the introduced changes for permissions. This makes the image: directive not work with private projects automatically and it needs to be configured manually on Runner's host with a predefined account (for example administrator's personal account with access token created explicitly for this purpose). This issue is resolved with latest changes in GitLab Runner 1.8 which receives GitLab credentials with build data.
  • Starting from GitLab 8.12, if you have 2FA enabled in your account, you need to pass a personal access token instead of your password in order to login to GitLab's Container Registry.

Your jobs can access all container images that you would normally have access to. The only implication is that you can push to the Container Registry of the project for which the job is triggered.

This is how an example usage can look like:

test:
  script:
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker pull $CI_REGISTRY/group/other-project:latest
    - docker run $CI_REGISTRY/group/other-project:latest

Pipeline triggers

Since 9.0 pipeline triggers do support the new permission model. The new triggers do impersonate their associated user including their access to projects and their project permissions.

API

GitLab API cannot be used via CI_JOB_TOKEN but there is a proposal to support it.