From 9021fdf8bba7728d77472b5a21b7b3a540f4a7f4 Mon Sep 17 00:00:00 2001 From: "James E. Blair" Date: Thu, 20 Jun 2019 14:13:44 -0700 Subject: [PATCH] Allow config projects to override allowed-projects To handle the case where an untrusted project defines a job with a secret which another project would like to run, allow a config project to attach that job to a project-pipeline and have it run regardless of the allowed-projects setting. Normally, untrusted jobs with secrets have an implicit and non-overridable allowed-projects setting of only that project, to avoid a situation where another project with a trusted post-review pipeline gains access to the secret by using a Depends-On to a change which lifts the allowed-projects restriction. This change allows a config project to bypass this, in effect saying that the projects involved trust each other sufficiently (or else, do not have access to a post-review pipeline which could be used to obtain secrets). Change-Id: I52ab193d0e39a37de64c8b3cb6953538e4073b43 --- doc/source/user/config.rst | 10 +++++-- ...fig-allowed-projects-e82586c215879f28.yaml | 7 +++++ .../git/common-config/playbooks/base.yaml | 2 ++ .../git/common-config/zuul.yaml | 30 +++++++++++++++++++ .../git/org_project1/zuul.yaml | 13 ++++++++ .../git/org_project2/zuul.yaml | 4 +++ .../config/allowed-projects-trusted/main.yaml | 10 +++++++ tests/unit/test_v3.py | 16 ++++++++++ zuul/configloader.py | 8 +++++ zuul/model.py | 5 +++- 10 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 releasenotes/notes/config-allowed-projects-e82586c215879f28.yaml create mode 100644 tests/fixtures/config/allowed-projects-trusted/git/common-config/playbooks/base.yaml create mode 100644 tests/fixtures/config/allowed-projects-trusted/git/common-config/zuul.yaml create mode 100644 tests/fixtures/config/allowed-projects-trusted/git/org_project1/zuul.yaml create mode 100644 tests/fixtures/config/allowed-projects-trusted/git/org_project2/zuul.yaml create mode 100644 tests/fixtures/config/allowed-projects-trusted/main.yaml diff --git a/doc/source/user/config.rst b/doc/source/user/config.rst index c0e63bd9fd..cdfb394e7b 100644 --- a/doc/source/user/config.rst +++ b/doc/source/user/config.rst @@ -1117,11 +1117,15 @@ Here is an example of two job definitions: all projects permitted to use the job. The current project (where the job is defined) is not automatically included, so if it should be able to run this job, then it must be explicitly - listed. By default, all projects may use the job. + listed. This setting is ignored by :term:`config projects + ` -- they may add any job to any project's + pipelines. By default, all projects may use the job. If a :attr:`job.secrets` is used in a job definition in an :term:`untrusted-project`, `allowed-projects` is automatically set to the current project only, and can not be overridden. + However, a :term:`config-project` may still add such a job to + any project's pipeline. .. warning:: @@ -1519,7 +1523,9 @@ If a job with secrets is unsafe to be used by other projects, the :attr:`job.allowed-projects` attribute can be used to restrict the projects which can invoke that job. If a job with secrets is defined in an `untrusted-project`, `allowed-projects` is automatically set to -that project only, and can not be overridden. +that project only, and can not be overridden (though a +:term:`config-project` may still add the job to any project's pipeline +regardless of this setting). Secrets, like most configuration items, are unique within a tenant, though a secret may be defined on multiple branches of the same diff --git a/releasenotes/notes/config-allowed-projects-e82586c215879f28.yaml b/releasenotes/notes/config-allowed-projects-e82586c215879f28.yaml new file mode 100644 index 0000000000..5e1f04c496 --- /dev/null +++ b/releasenotes/notes/config-allowed-projects-e82586c215879f28.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Config projects may now add any job to any project's pipelines, + regardless of the setting of allowed-projets (including the implicit + setting of allowed-projects on jobs with secrets in untrusted + projects). diff --git a/tests/fixtures/config/allowed-projects-trusted/git/common-config/playbooks/base.yaml b/tests/fixtures/config/allowed-projects-trusted/git/common-config/playbooks/base.yaml new file mode 100644 index 0000000000..f679dceaef --- /dev/null +++ b/tests/fixtures/config/allowed-projects-trusted/git/common-config/playbooks/base.yaml @@ -0,0 +1,2 @@ +- hosts: all + tasks: [] diff --git a/tests/fixtures/config/allowed-projects-trusted/git/common-config/zuul.yaml b/tests/fixtures/config/allowed-projects-trusted/git/common-config/zuul.yaml new file mode 100644 index 0000000000..fb879de5f9 --- /dev/null +++ b/tests/fixtures/config/allowed-projects-trusted/git/common-config/zuul.yaml @@ -0,0 +1,30 @@ +- pipeline: + name: check + manager: independent + post-review: True + trigger: + gerrit: + - event: patchset-created + success: + gerrit: + Verified: 1 + failure: + gerrit: + Verified: -1 + +- job: + name: base + run: playbooks/base.yaml + parent: null + +- project: + name: common-config + check: + jobs: [] + +- project: + name: org/project2 + check: + jobs: + - test-project1 + diff --git a/tests/fixtures/config/allowed-projects-trusted/git/org_project1/zuul.yaml b/tests/fixtures/config/allowed-projects-trusted/git/org_project1/zuul.yaml new file mode 100644 index 0000000000..b6e0a1f24a --- /dev/null +++ b/tests/fixtures/config/allowed-projects-trusted/git/org_project1/zuul.yaml @@ -0,0 +1,13 @@ +- secret: + name: project1_secret + data: {} + +- job: + name: test-project1 + secrets: project1_secret + +- project: + name: org/project1 + check: + jobs: + - test-project1 diff --git a/tests/fixtures/config/allowed-projects-trusted/git/org_project2/zuul.yaml b/tests/fixtures/config/allowed-projects-trusted/git/org_project2/zuul.yaml new file mode 100644 index 0000000000..f7220bd88f --- /dev/null +++ b/tests/fixtures/config/allowed-projects-trusted/git/org_project2/zuul.yaml @@ -0,0 +1,4 @@ +- project: + name: org/project2 + check: + jobs: [] diff --git a/tests/fixtures/config/allowed-projects-trusted/main.yaml b/tests/fixtures/config/allowed-projects-trusted/main.yaml new file mode 100644 index 0000000000..49ed83896c --- /dev/null +++ b/tests/fixtures/config/allowed-projects-trusted/main.yaml @@ -0,0 +1,10 @@ +- tenant: + name: tenant-one + source: + gerrit: + config-projects: + - common-config + untrusted-projects: + - org/project1 + - org/project2 + - org/project3 diff --git a/tests/unit/test_v3.py b/tests/unit/test_v3.py index afc1629000..a6811f12fc 100644 --- a/tests/unit/test_v3.py +++ b/tests/unit/test_v3.py @@ -801,6 +801,22 @@ class TestAllowedProjects(ZuulTestCase): 'to run job test-project2b', B.messages[0]) +class TestAllowedProjectsTrusted(ZuulTestCase): + tenant_config_file = 'config/allowed-projects-trusted/main.yaml' + + def test_allowed_projects_secret_trusted(self): + # Test that an untrusted job defined in project1 can be used + # in project2, but only if attached by a config project. + A = self.fake_gerrit.addFakeChange('org/project2', 'master', 'A') + self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1)) + self.waitUntilSettled() + self.assertEqual(A.reported, 1) + self.assertIn('Build succeeded', A.messages[0]) + self.assertHistory([ + dict(name='test-project1', result='SUCCESS', changes='1,1'), + ], ordered=False) + + class TestCentralJobs(ZuulTestCase): tenant_config_file = 'config/central-jobs/main.yaml' diff --git a/zuul/configloader.py b/zuul/configloader.py index 70775a2cce..eaf99b0b93 100644 --- a/zuul/configloader.py +++ b/zuul/configloader.py @@ -670,6 +670,14 @@ class JobParser(object): job.variant_description = conf.get( 'variant-description', " ".join(as_list(conf.get('branches')))) + if project_pipeline and conf['_source_context'].trusted: + # A config project has attached this job to a + # project-pipeline. In this case, we can ignore + # allowed-projects -- the superuser has stated they want + # it to run. This can be useful to allow untrusted jobs + # with secrets to be run in other untrusted projects. + job.ignore_allowed_projects = True + if 'parent' in conf: if conf['parent'] is not None: # Parent job is explicitly specified, so inherit from it. diff --git a/zuul/model.py b/zuul/model.py index 7917685c8c..c773395dca 100644 --- a/zuul/model.py +++ b/zuul/model.py @@ -1145,6 +1145,8 @@ class Job(ConfigObject): provides=frozenset(), requires=frozenset(), dependencies=frozenset(), + ignore_allowed_projects=None, # internal, but inherited + # in the usual manner ) # These attributes affect how the job is actually run and more @@ -3941,7 +3943,8 @@ class Layout(object): raise Exception("Job %s is abstract and may not be " "directly run" % (frozen_job.name,)) - if (frozen_job.allowed_projects is not None and + if (not frozen_job.ignore_allowed_projects and + frozen_job.allowed_projects is not None and change.project.name not in frozen_job.allowed_projects): raise Exception("Project %s is not allowed to run job %s" % (change.project.name, frozen_job.name))