summaryrefslogtreecommitdiff
path: root/doc/source/tenants.rst
blob: fbbb458a55758c6306c8160d413b39f2aaeec4f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
:title: Tenant Configuration

.. _tenant-config:

Tenant Configuration
====================

After ``zuul.conf`` is configured, Zuul component servers will be able
to start, but a tenant configuration is required in order for Zuul to
perform any actions.  The tenant configuration file specifies upon
which projects Zuul should operate.  These repositories are grouped
into tenants.  The configuration of each tenant is separate from the
rest (no pipelines, jobs, etc are shared between them).

A project may appear in more than one tenant; this may be useful if
you wish to use common job definitions across multiple tenants.

Actions normally available to the Zuul operator only can be performed by specific
users on Zuul's REST API, if admin rules are listed for the tenant. Admin rules
are also defined in the tenant configuration file.

The tenant configuration file is specified by the
:attr:`scheduler.tenant_config` setting in ``zuul.conf``.  It is a
YAML file which, like other Zuul configuration files, is a list of
configuration objects, though only two types of objects are supported:
``tenant`` and ``admin-rule``.

Alternatively the :attr:`scheduler.tenant_config_script`
can be the path to an executable that will be executed and its stdout
used as the tenant configuration. The executable must return a valid
tenant YAML formatted output.

Tenant configuration is checked for updates any time a scheduler is
started, and changes to it are read automatically. If the tenant
configuration is altered during operation, you can signal a scheduler
to read and apply the updated state in order to avoid restarting. See
the section on :ref:`reconfiguration` for instructions. Ideally,
tenant configuration deployment via configuration management should
also be made to trigger a smart-reconfigure once the file is replaced.

Tenant
------

A tenant is a collection of projects which share a Zuul
configuration. Some examples of tenant definitions are:

.. code-block:: yaml

   - tenant:
       name: my-tenant
       max-nodes-per-job: 5
       exclude-unprotected-branches: false
       source:
         gerrit:
           config-projects:
             - common-config
             - shared-jobs:
                 include: job
           untrusted-projects:
             - zuul/zuul-jobs:
                 shadow: common-config
             - project1
             - project2:
                 exclude-unprotected-branches: true

.. code-block:: yaml

   - tenant:
       name: my-tenant
       admin-rules:
         - acl1
         - acl2
       source:
         gerrit:
           config-projects:
             - common-config
           untrusted-projects:
             - exclude:
                 - job
                 - semaphore
                 - project
                 - project-template
                 - nodeset
                 - secret
               projects:
                 - project1
                 - project2:
                     exclude-unprotected-branches: true

.. attr:: tenant

   The following attributes are supported:

   .. attr:: name
      :required:

      The name of the tenant.  This may appear in URLs, paths, and
      monitoring fields, and so should be restricted to URL friendly
      characters (ASCII letters, numbers, hyphen and underscore) and
      you should avoid changing it unless necessary.

   .. attr:: source
      :required:

      A dictionary of sources to consult for projects.  A tenant may
      contain projects from multiple sources; each of those sources
      must be listed here, along with the projects it supports.  The
      name of a :ref:`connection<connections>` is used as the
      dictionary key (e.g. ``gerrit`` in the example above), and the
      value is a further dictionary containing the keys below.

   The next two attributes, **config-projects** and
   **untrusted-projects** provide the bulk of the information for
   tenant configuration.  They list all of the projects upon which
   Zuul will act.

   The order of the projects listed in a tenant is important.  A job
   which is defined in one project may not be redefined in another
   project; therefore, once a job appears in one project, a project
   listed later will be unable to define a job with that name.
   Further, some aspects of project configuration (such as the merge
   mode) may only be set on the first appearance of a project
   definition.

   Zuul loads the configuration from all **config-projects** in the
   order listed, followed by all **untrusted-projects** in order.

   .. attr:: config-projects

      A list of projects to be treated as :term:`config projects
      <config-project>` in this tenant.  The jobs in a config project
      are trusted, which means they run with extra privileges, do not
      have their configuration dynamically loaded for proposed
      changes, and Zuul config files are only searched for in the
      ``master`` branch.

      The items in the list follow the same format described in
      **untrusted-projects**.

      .. attr:: <project>

         The config-projects have an additional config option that
         may be specified optionally.

         .. attr:: load-branch
            :default: master

            Define which branch is loaded from a config project. By
            default config projects load Zuul configuration only
            from the master branch.

   .. attr:: untrusted-projects

      A list of projects to be treated as untrusted in this tenant.
      An :term:`untrusted-project` is the typical project operated on
      by Zuul.  Their jobs run in a more restrictive environment, they
      may not define pipelines, their configuration dynamically
      changes in response to proposed changes, and Zuul will read
      configuration files in all of their branches.

      .. attr:: <project>

         The items in the list may either be simple string values of
         the project names, or a dictionary with the project name as
         key and the following values:

         .. attr:: include

            Normally Zuul will load all of the :ref:`configuration-items`
            appropriate for the type of project (config or untrusted)
            in question.  However, if you only want to load some
            items, the **include** attribute can be used to specify
            that *only* the specified items should be loaded.
            Supplied as a string, or a list of strings.

            The following **configuration items** are recognized:

            * pipeline
            * job
            * semaphore
            * project
            * project-template
            * nodeset
            * secret

         .. attr:: exclude

            A list of **configuration items** that should not be loaded.

         .. attr:: shadow

            A list of projects which this project is permitted to
            shadow.  Normally, only one project in Zuul may contain
            definitions for a given job.  If a project earlier in the
            configuration defines a job which a later project
            redefines, the later definition is considered an error and
            is not permitted.  The **shadow** attribute of a project
            indicates that job definitions in this project which
            conflict with the named projects should be ignored, and
            those in the named project should be used instead.  The
            named projects must still appear earlier in the
            configuration.  In the example above, if a job definition
            appears in both the ``common-config`` and ``zuul-jobs``
            projects, the definition in ``common-config`` will be
            used.

         .. attr:: exclude-unprotected-branches

            Define if unprotected branches should be processed.
            Defaults to the tenant wide setting of
            exclude-unprotected-branches. This currently only affects
            GitHub and GitLab projects.

         .. attr:: include-branches

            A list of regexes matching branches which should be
            processed.  If omitted, all branches are included.
            Operates after *exclude-unprotected-branches* and so may
            be used to further reduce the set of branches (but not
            increase it).

            It has priority over *exclude-branches*.

         .. attr:: exclude-branches

            A list of regexes matching branches which should be
            processed.  If omitted, all branches are included.
            Operates after *exclude-unprotected-branches* and so may
            be used to further reduce the set of branches (but not
            increase it).

            It will not exclude a branch which already matched
            *include-branches*.

         .. attr:: always-dynamic-branches

            A list of regular expressions matching branches which
            should be treated as if every change newly proposes
            dynamic Zuul configuration.  In other words, the only time
            Zuul will realize any configuration related to these
            branches is during the time it is running jobs for a
            proposed change.

            This is potentially useful for situations with large
            numbers of rarely used feature branches, but comes at the
            cost of a significant reduction in Zuul features for these
            branches.

            Every regular expression listed here will also implicitly
            be included in *exclude-branches*, therefore Zuul will not
            load any static in-repo configuration from this branch.
            These branches will not be available for use in overriding
            checkouts of repos, nor will they be included in the git
            repos that Zuul prepares for *required-projects* (unless
            there is a change in the dependency tree for this branch).

            In particular, this means that the only jobs which can be
            specified for these branches are pre-merge and gating jobs
            (such as :term:`check` and :term:`gate`).  No post-merge
            or periodic jobs will run for these branches.

            Using this setting also incurs additional processing for
            each change submitted for these branches as Zuul must
            recalculate the configuration layout it uses for such a
            change as if it included a change to a ``zuul.yaml`` file,
            even if the change does not alter the configuration).

            With all these caveats in mind, this can be useful for
            repos with large numbers of rarely used branches as it
            allows Zuul to omit their configuration in most
            circumstances and only calculate the configuration of a
            single additional branch when it is used.

         .. attr:: extra-config-paths

            Normally Zuul loads in-repo configuration from the first
            of these paths:

            * zuul.yaml
            * zuul.d/*
            * .zuul.yaml
            * .zuul.d/*

            If this option is supplied then, after the normal process
            completes, Zuul will also load any configuration found in
            the files or paths supplied here.  This can be a string or
            a list.  If a list of multiple items, Zuul will load
            configuration from *all* of the items in the list (it will
            not stop at the first extra configuration found).
            Directories should be listed with a trailing ``/``.  Example:

            .. code-block:: yaml

               extra-config-paths:
                 - zuul-extra.yaml
                 - zuul-extra.d/

            This feature may be useful to allow a project that
            primarily holds shared jobs or roles to include additional
            in-repo configuration for its own testing (which may not
            be relevant to other users of the project).

      .. attr:: <project-group>

         The items in the list are dictionaries with the following
         attributes. A **configuration items** definition is applied
         to the list of projects.

         .. attr:: include

            A list of **configuration items** that should be loaded.

         .. attr:: exclude

            A list of **configuration items** that should not be loaded.

         .. attr:: projects

            A list of **project** items.

   .. attr:: max-nodes-per-job
      :default: 5

      The maximum number of nodes a job can request.  A value of
      '-1' value removes the limit.

   .. attr:: max-job-timeout
      :default: 10800

      The maximum timeout for jobs. A value of '-1' value removes the limit.

   .. attr:: exclude-unprotected-branches
      :default: false

      When using a branch and pull model on a shared repository
      there are usually one or more protected branches which are gated
      and a dynamic number of personal/feature branches which are the
      source for the pull requests. These branches can potentially
      include broken Zuul config and therefore break the global tenant
      wide configuration. In order to deal with this Zuul's operations
      can be limited to the protected branches which are gated. This
      is a tenant wide setting and can be overridden per project.
      This currently only affects GitHub and GitLab projects.

   .. attr:: default-parent
      :default: base

      If a job is defined without an explicit :attr:`job.parent`
      attribute, this job will be configured as the job's parent.
      This allows an administrator to configure a default base job to
      implement local policies such as node setup and artifact
      publishing.

   .. attr:: default-ansible-version

      Default ansible version to use for jobs that doesn't specify a version.
      See :attr:`job.ansible-version` for details.

   .. attr:: allowed-triggers
      :default: all connections

      The list of connections a tenant can trigger from. When set, this setting
      can be used to restrict what connections a tenant can use as trigger.
      Without this setting, the tenant can use any connection as a trigger.

   .. attr:: allowed-reporters
      :default: all connections

      The list of connections a tenant can report to. When set, this setting
      can be used to restrict what connections a tenant can use as reporter.
      Without this setting, the tenant can report to any connection.

   .. attr:: allowed-labels
      :default: []

      The list of labels (as strings or :ref:`regular expressions <regex>`)
      a tenant can use in a job's nodeset. When set, this setting can
      be used to restrict what labels a tenant can use.  Without this
      setting, the tenant can use any labels.

   .. attr:: disallowed-labels
      :default: []

      The list of labels (as strings or :ref:`regular expressions <regex>`)
      a tenant is forbidden to use in a job's nodeset. When set, this
      setting can be used to restrict what labels a tenant can use.
      Without this setting, the tenant can use any labels permitted by
      :attr:`tenant.allowed-labels`.  This check is applied after the
      check for `allowed-labels` and may therefore be used to further
      restrict the set of permitted labels.

   .. attr:: web-root

      If this tenant has a whitelabeled installation of zuul-web, set
      its externally visible URL here (e.g.,
      ``https://tenant.example.com/``).  This will override the
      :attr:`web.root` setting when constructing URLs for this tenant.

   .. attr:: admin-rules

      A list of access rules for the tenant. These rules are checked to grant
      privileged actions to users at the tenant level, through Zuul's REST API.

      At least one rule in the list must match for the user to be allowed the
      privileged action.

      More information on tenant-scoped actions can be found in
      :ref:`authentication`.

   .. attr:: authentication-realm

      Each authenticator defined in Zuul's configuration is associated to a realm.
      When authenticating through Zuul's Web User Interface under this tenant, the
      Web UI will redirect the user to this realm's authentication service. The
      authenticator must be of the type ``OpenIDConnect``.

      .. note::

         Defining a default realm for a tenant will not invalidate access tokens
         issued from other configured realms, especially if they match the tenant's
         admin rules. This is intended, so that an operator can for example issue
         an overriding access token manually. If this is an issue, it is advised
         to add finer filtering to admin rules, for example filtering by the ``iss``
         claim (generally equal to the issuer ID).

   .. attr:: semaphores

      A list of names of :attr:`global-semaphore` objects to allow
      jobs in this tenant to access.

.. _global_semaphore:

Global Semaphore
----------------

Semaphores are normally defined in in-repo configuration (see
:ref:`semaphore`), however to support use-cases where semaphores are
used to represent constrained global resources that may be used by
multiple Zuul tenants, semaphores may be defined within the main
tenant configuration file.

In order for a job to use a global semaphore, the semaphore must first
be defined in the tenant configuration file with
:attr:`global-semaphore` and then added to each tenant which should
have access to it with :attr:`tenant.semaphores`.  Once that is done,
Zuul jobs may use that semaphore in the same way they would use a
normal tenant-scoped semaphore.

If any tenant which is granted access to a global semaphore also has a
tenant-scoped semaphore defined with the same name, that definition
will be treated as a configuration error and subsequently ignored in
favor of the global semaphore.

An example definition looks similar to the normal semaphore object:

.. code-block:: yaml

   - global-semaphore:
       name: global-semaphore-foo
       max: 5

.. attr:: global-semaphore

   The following attributes are available:

   .. attr:: name
      :required:

      The name of the semaphore, referenced by jobs.

   .. attr:: max
      :default: 1

      The maximum number of running jobs which can use this semaphore.


.. _admin_rule_definition:

Access Rule
-----------

An access rule is a set of conditions the claims of a user's JWT must match
in order to be allowed to perform protected actions at a tenant's level.

The protected actions available at tenant level are **autohold**, **enqueue**,
**dequeue** or **promote**.

.. note::

   Rules can be overridden by the ``zuul.admin`` claim in a token if if matches
   an authenticator configuration where `allow_authz_override` is set to true.
   See :ref:`authentication` for more details.

Below are some examples of how access rules can be defined:

.. code-block:: yaml

   - admin-rule:
       name: affiliate_or_admin
       conditions:
         - resources_access:
             account:
               roles: "affiliate"
           iss: external_institution
         - resources_access.account.roles: "admin"
   - admin-rule:
       name: alice_or_bob
       conditions:
         - zuul_uid: alice
         - zuul_uid: bob


.. attr:: admin-rule

   The following attributes are supported:

   .. attr:: name
      :required:

      The name of the rule, so that it can be referenced in the ``admin-rules``
      attribute of a tenant's definition. It must be unique.

   .. attr:: conditions
      :required:

      This is the list of conditions that define a rule. A JWT must match **at
      least one** of the conditions for the rule to apply. A condition is a
      dictionary where keys are claims. **All** the associated values must
      match the claims in the user's token; in other words the condition dictionary
      must be a "sub-dictionary" of the user's JWT.

      Zuul's authorization engine will adapt matching tests depending on the
      nature of the claim in the token, eg:

      * if the claim is a JSON list, check that the condition value is in the
        claim
      * if the claim is a string, check that the condition value is equal to
        the claim's value

      The claim names can also be written in the XPath format for clarity: the
      condition

      .. code-block:: yaml

        resources_access:
          account:
            roles: "affiliate"

      is equivalent to the condition

      .. code-block:: yaml

        resources_access.account.roles: "affiliate"

      The special ``zuul_uid`` claim refers to the ``uid_claim`` setting in an
      authenticator's configuration. By default it refers to the ``sub`` claim
      of a token. For more details see the :ref:`authentication`.

      Under the above example, the following token would match rules
      ``affiliate_or_admin`` and ``alice_or_bob``:

      .. code-block:: javascript

        {
         'iss': 'external_institution',
         'aud': 'my_zuul_deployment',
         'exp': 1234567890,
         'iat': 1234556780,
         'sub': 'alice',
         'resources_access': {
             'account': {
                 'roles': ['affiliate', 'other_role']
             }
         },
        }

      And this token would only match rule ``affiliate_or_admin``:

      .. code-block:: javascript

        {
         'iss': 'some_other_institution',
         'aud': 'my_zuul_deployment',
         'exp': 1234567890,
         'sub': 'carol',
         'iat': 1234556780,
         'resources_access': {
             'account': {
                 'roles': ['admin', 'other_role']
             }
         },
        }

Access Rule Templating
----------------------

The special word "{tenant.name}" can be used in conditions' values. It will be automatically
substituted for the relevant tenant when evaluating authorizations for a given
set of claims. For example, consider the following rule:

.. code-block:: yaml

   - admin-rule:
       name: tenant_in_groups
       conditions:
         - groups: "{tenant.name}"

If applied to the following tenants:

.. code-block:: yaml

   - tenant:
       name: tenant-one
       admin-rules:
         - tenant_in_groups
   - tenant:
       name: tenant-two
       admin-rules:
         - tenant_in_groups

Then this set of claims will be allowed to perform protected actions on **tenant-one**:

.. code-block:: javascript

  {
   'iss': 'some_other_institution',
   'aud': 'my_zuul_deployment',
   'exp': 1234567890,
   'sub': 'carol',
   'iat': 1234556780,
   'groups': ['tenant-one', 'some-other-group'],
  }

And this set of claims will be allowed to perform protected actions on **tenant-one**
and **tenant-two**:

.. code-block:: javascript

    {
     'iss': 'some_other_institution',
     'aud': 'my_zuul_deployment',
     'exp': 1234567890,
     'sub': 'carol',
     'iat': 1234556780,
     'groups': ['tenant-one', 'tenant-two'],
    }