Skip to content

Scope JobToken to authorized projects [RUN ALL RSPEC] [RUN AS-IF-FOSS]

Fabio Pitino requested to merge scope-job-token-to-authorized-projects into master

What does this MR do?

Related to #329550 (closed)

Background

When a user authenticates using CI_JOB_TOKEN should be considered untrusted because the user running the pipeline may not actually be the same user that triggered it. For example a scheduled pipeline always run in the context of the schedule owner even though the owner may not be the author of the commit the pipeline runs against.

Today, using CI_JOB_TOKEN gives it complete permissions to all resources that the job.user has access to. Instead we want to allow the CI_JOB_TOKEN to be used only within a list of authorized projects.

Solution

With this MR we are introducing a security layer on top of CI_JOB_TOKEN usage in the form of Ci::JobToken::Scope. The scope defines the list of projects that the job token can access to. By default the scope for a given job token includes only the project where the job token originates from.

This MR introduces also a concept of ScopeLink as a model that allows to indicate all projects that a given job token can access.

In a follow-up MR we will introduce operations that allow maintainers to add/remove projects to/from a scope, via project settings, as well as enabling this feature at project-level.

Example

Scenario: A CI_JOB_TOKEN, from a job in project A, is used to trigger a multi-project pipeline on project B.

Initially Project A's scope is a collection containing only itself: [A], meaning that the token can only be used for operations within A. Even if the user that runs the job that generated the CI_JOB_TOKEN is a member of project B. Project B must be added to Project A's scope for the token to access B.

If the multi-project pipeline in B wants to read some data from project C, project C must be present in B's scope in project settings.

Feature flag

This change is introduced behind a feature flag ci_scoped_job_token which is disabled by default. FF issue: #332272 (closed).

DB migrations

$ rails db:migrate:redo
== 20210527194558 CreateCiJobTokenProjectScopeLinks: reverting ================
-- drop_table(:ci_job_token_project_scope_links, {:if_exists=>true})
   -> 0.0018s
== 20210527194558 CreateCiJobTokenProjectScopeLinks: reverted (0.0119s) =======

== 20210527194558 CreateCiJobTokenProjectScopeLinks: migrating ================
-- create_table(:ci_job_token_project_scope_links, {:if_not_exists=>true})
   -> 0.0341s
== 20210527194558 CreateCiJobTokenProjectScopeLinks: migrated (0.0409s) =======

Relevant queries

Ci::JobToken::Scope#all_projects (plan executed locally):

SELECT "projects".* FROM ((SELECT "projects".* FROM "projects" WHERE "projects"."id" = 9)
UNION ALL
(SELECT "projects".* FROM "projects" 
  WHERE (EXISTS (
    SELECT 1 FROM "ci_job_token_project_scope_links" 
    WHERE "ci_job_token_project_scope_links"."source_project_id" = 9 
    AND (projects.id = ci_job_token_project_scope_links.target_project_id)))
)) projects;

 Append  (cost=0.14..16.51 rows=7 width=1412) (actual time=0.012..0.035 rows=2 loops=1)
   ->  Index Scan using idx_projects_on_repository_storage_last_repository_updated_at on projects projects_1  (cost=0.14..2.16 rows=1 width=1412) (actual time=0.012..0.013 rows=1 loops=1)
         Index Cond: (id = 9)
   ->  Nested Loop  (cost=0.29..14.25 rows=6 width=1412) (actual time=0.021..0.022 rows=1 loops=1)
         ->  Index Only Scan using i_ci_job_token_project_scope_links_on_source_and_target_project on ci_job_token_project_scope_links  (cost=0.15..6.26 rows=6 width=8) (actual time=0.010..0.010 rows=1 loops=1)
               Index Cond: (source_project_id = 9)
               Heap Fetches: 1
         ->  Index Scan using idx_projects_on_repository_storage_last_repository_updated_at on projects projects_2  (cost=0.14..1.32 rows=1 width=1412) (actual time=0.005..0.006 rows=1 loops=1)
               Index Cond: (id = ci_job_token_project_scope_links.target_project_id)
 Planning Time: 0.857 ms
 Execution Time: 0.116 ms
(11 rows)

Screenshots (strongly suggested)

Before / When target project is part of the CI job token scope

image

image

When job token scope does not include the target project

image

image

Does this MR meet the acceptance criteria?

Conformity

Availability and Testing

Security

Does this MR contain changes to processing or storing of credentials or tokens, authorization and authentication methods or other items described in the security review guidelines? If not, then delete this Security section.

  • Label as security and @ mention @gitlab-com/gl-security/appsec
  • The MR includes necessary changes to maintain consistency between UI, API, email, or other methods
  • Security reports checked/validated by a reviewer from the AppSec team
Edited by Mayra Cabrera

Merge request reports