GitHub-Actions

GitHub-Actions is a managed CI/CD offering integrated into GitHub. For standardisation and re-use within Gardener-Project, we maintain some re-usable Actions and Workflows (kept in cc-utils repository). Those offer functionality similar to Concourse-Pipeline-Template. This includes integration with OCM (Open Component Model), as well as release (notes) handling.

To improve security posture, we make use of trustbased-authentication, and avoid usage of static credentials where possible.

Migration from Concourse-Pipelines

The default pipeline setup for Concourse-Pipelines consists of three pipelines:

  • head-update (run for certain branches - typically default + release-branches)

  • pull-request (run for pullrequests)

  • release (manually triggered to publish new releases)

This is migrated to workflows of the following layout:

.github/workflows/build.yaml       # shared; called by other workflows
.github/workflows/release.yaml     # for manually triggering releases
.github/workflows/non-release.yaml # for triggering upon head-updates / pullrequests

Branch-Protection -> Rulesets

Most repositories use branch-protections to forbid directly pushing to a branch. Some pipelines, most prominently release-pipelines (which push release- and “bump”-commits), need to circumvent those rules. As we want to avoid using static credentials (for Service-Accounts w/ owner-permissions), we use a GitHub-App for granting such permissions more fine-granular.

However, as discussed here, it is only possible to grant exceptions (or “bypassers”, as GitHub Rulesets call them) to branch protections if using the more modern Rulesets. Hence, as part of migration to GitHub-Actions, it is necessary to migrate branch-protections to rulesets.

Note

It is important to no longer use “classical” branch protections after migration of release-pipeline to GitHub-Actions. Configure any protection-rules using the more modern Rulesets, instead. The latter are a superset to branch-protections w.r.t. configuration options.

Pull-Requests from forked Repositories

To mitigate harm from malicious Pull-Requests, GitHub-Actions-Workflow-Runs are run with restricted privileges. If using pull_request as pipeline-trigger, corresponding workflow-runs will only have readonly-access to target-repository. This prevents such runs to push build artefacts, such as OCI-Images or Helmcharts (to OCI-Registries).

As an alternative, there is the pull_request_target trigger, which does not have this limitation. However, by default, thus-triggered runs will be based on the pull-request’s target repository, i.e. the actual changes proposed by a Pull-Request will not be visible to the pipeline-run.

For the latter case, there is the trusted-checkout action, which will circumvent this limitation, and explicitly checkout commits from trusted pullrequests. Pullrequests are considered to be trusted, if

  • the fork’s owner is the same as the target-repository (i.e. a fork within the same organisation)

OR - the pullrequest-author is either of:

  • COLLABORATOR

  • CONTRIBUTOR

  • MEMBER (org-member)

  • OWNER (repository-owner)

  • the pullrequest has a certain label (default: reviewed/ok-to-test) set

The preferred approach (because it will also work for first-time contributors) is using “label-based trust”.

Warning

For pullrequests that are not considered to be trusted, the workflow-run will still be executed. However, re-usable workflows from cc-utils will not attempt to push build-results, nor will the run be based on the changes from the pullrequest, which may be unintuitive.

In such cases, a warning is emitted into the pipeline-run’s summary.

Note

There are the following “autor-associations” a pullrequest author can have:

association

explanation

COLLABORATOR

Author has been invited to collaborate on the repository

CONTRIBUTOR

Author has previously committed to the repository

FIRST_TIMER

Author has not previously committed to GitHub

FIRST_TIME_CONTRIBUTOR

Author has not previously committed to the repository

MANNEQUIN

Author is a placeholder for an unclaimed user

MEMBER

Author is a member of the organization that owns the repository

NONE

Author has no association with the repository

OWNER

Author is the owner of the repository.

When to use what

If a workflow does not need to publish changes from pullrequests, use on.pull_request. Otherwise, use on.pull_request_target. In this case, consistently use trusted-checkout instead of actions/checkout.

Warning

If using pull_request_target, special care needs to be done to catch malicious changes, especially such changes that are done in buildscripts.

Example configuration for label-based trust

If privileged pipelines are needed, use the following event-trigger:

on:
   pull_request_target:
      types:
         - labeled

jobs:
   example:
      # the left condition (!= labeled) is only needed, if different triggers (e.g. push) are
      # used.
      # it is important to add the explicit check for label's name to prevent accidental
      # triggering (e.g. from gardener-robot setting initial set of labels)
      if: ${{ github.event.action != 'labeled' || github.event.label.name == 'revieved/ok-to-test' }}
      permissions:
         pull-requests: write # needed so trusted-checkout can remove trusted-label
                              # caveat: also needs to be set for all called workflows
                              # that use trusted-checkout (action)
      ...

The following workflow can be added for convenience:

# pullrequest-trust-helper.yaml
on:
   pull_request_target:
      types:
         - opened
         - edited
         - reopened
         - synchronize

jobs:
   pullrequest-trusted-helper:
      permissions:
         pull-requests: write
      secrets: inherit # access to `GitHub-Actions`-App is needed to read teams
      uses: gardener/cc-utils/.github/workflows/pullrequest-trust-helper@master
      with:
         # members will be trusted (-> get okay-to-test-label automatically)
         trusted-teams: 'first-team,second-team'

Caveats

Regardless which of on.pull_request or on.pull_request_target is used, workflow-runs will always be based on target-repository’s local workflow- and actions-definitions.

Note

Be sure to grant pull-requests: write-permission to all workflows called from pull_request_target-event (this is needed so trusted-checkout action is able to remove trusted-label).