-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add guide to secure GitHub workflows
Signed-off-by: Mihai Maruseac <[email protected]>
- Loading branch information
1 parent
91a6678
commit 8ead5f2
Showing
1 changed file
with
121 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
# How to secure GitHub repositories | ||
|
||
It is recommended to turn on 2FA for most repositories, especially if building | ||
a package that is a dependency of multiple other packages. | ||
|
||
At least for critical packages, administrators should enable branch | ||
protection. Require CI to pass before merging to the main branch. Allow only | ||
repository owners to merge PRs. | ||
|
||
It is ideal to require PR reviews before merging. For security critical | ||
packages, having at least two reviewers is ideal, as this alleviates the risk | ||
of sock-puppet accounts. | ||
|
||
To keep dependencies up to date, it is advisable to enable | ||
[Dependabot][dependabot] or [Renovate][renovate] or a similar tool which will | ||
create PRs to update dependencies and alert on vulnerabilities in | ||
dependencies. Both of these tools can be configured to send a PR at regular | ||
intervals (e.g., once a week). | ||
|
||
It is recommended to install [Scorecards action][scorecard] for public | ||
repositories and trying to improve the score as high as possible. This is a | ||
scanner for security best practices, most of which are already discussed in | ||
this document. | ||
|
||
## Securing GitHub Actions | ||
|
||
It is preferable to use the hosted runners as possible. The large runners | ||
supported by GitHub come with usage costs, but are better from the point of | ||
view of supply chain security than hosting your own infrastructure for CI. | ||
|
||
All GitHub Actions workflows should restrict [permissions][gha-permissions] to | ||
the minimum scope needed. Scope the permissions at job level, instead of at | ||
workflow level. | ||
|
||
``` | ||
check-tests: | ||
permissions: | ||
actions: read | ||
``` | ||
|
||
Minimize usage of actions that create PRs or push code to branch. Thoroughly | ||
inspect actions that can approve PRs and workflows that are triggered after a | ||
PR has been approved (term-of-check-vs-term-of-use type of concerns). | ||
|
||
If using actions outside of your organisation, these should be pinned by | ||
commit hash. Don't pin be version tag as these tags can be forced pushed. The | ||
[Scorecards Pinned Dependencies workflow][scorecard] can help identify which | ||
actions should still be pinned. [Dependabot][dependabot] is able to upgrade | ||
the versions of these actions periodically: | ||
|
||
``` | ||
- name: Upload sdist as an artifact for later jobs in workflow | ||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 | ||
with: | ||
... | ||
``` | ||
|
||
If secrets are used as arguments to commands for the workflows, pass them via | ||
environment variables. The action log can leak command line arguments! | ||
|
||
If using secrets, these should be scrubbed as soon as they are no longer | ||
needed. Make sure they don't show up in logs, including debug runs of the | ||
workflow! | ||
|
||
Be extra careful of GitHub context variables (e.g., | ||
`github.event.issue.title`) which are vulnerable to command injection attacks. | ||
Don't use these directly in commands. Prefer passing them via environment | ||
variables. Use [Scorecards][scorecard] to identify dangerous workflows. | ||
|
||
Audit carefully all workflows of type `pull_request_target` and | ||
`workflow_run`. For these, don't use secrets at all and separate the sensitive | ||
parts into separate runs. Validate all inputs and only run these workflows on | ||
protected branches. Never use `pull_request_head.ref` as the branch head | ||
reference since this can be updated between the time the PR gets approved and | ||
the time the CI workflow starts executing. | ||
|
||
### Using self-hosted runners | ||
|
||
If using self-hosted runners, create 2 separate pools: one pool to be used | ||
only for CI for PRs and another one to be used for releases and other | ||
sensitive jobs. Ideally, don't share these pools across multiple projects in | ||
the same organisation. | ||
|
||
> [!WARNING] | ||
> Repository-level self-hosted runners are accessible to any workflow in the | ||
> repository. Never trigger a CI job before the PR is reviewed as a PR can | ||
> maliciously alter the workflow to attack the hosted runner. | ||
Don't allow any secrets in jobs that run in the CI pool. Only use secrets on | ||
the release pool and never trigger jobs on the release pool on code that is | ||
not reviewed. | ||
|
||
Ideally, the self-hosted runners should be ephemeral. Destroy them as soon as | ||
the CI run finishes. If caching between jobs is required, periodically recycle | ||
all runners (e.g., destroy them every week). | ||
|
||
Do not allow writing to the repository from the workflow actions if using | ||
self-hosted runners. A compromise of the runner can result in compromise of | ||
the repository otherwise. | ||
|
||
All CI runners should have a timeout. All workflows that run on CI runners | ||
should not run on forks of the repository. Gate workflows run by checking the | ||
value of the `github.repository` variable: | ||
|
||
``` | ||
jobs: | ||
build: | ||
if: github.repository == 'haskell/haskell-language-server' # Don't do this in forks | ||
``` | ||
|
||
You can also use `pull_request.author_association` to differentiate between | ||
owners of the project and collaborators added to the project that also have | ||
write access. | ||
|
||
Monitor all activity on the runners and have separate infrastructure to scan | ||
for malicious activity. | ||
|
||
[scorecard]: https://github.com/ossf/scorecard-action | ||
[dependabot]: https://github.com/dependabot | ||
[renovate]: https://github.com/renovatebot/renovate | ||
[gha-permissions]: https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs |