canonical / canonical/documentation-workflows
Request for security hardening
- Dominant language
- Python
- Stars
- 5
- Forks
- 15
- PR merge metrics
- No merged PRs in 30d
Description
Hello!
Given the several recent GitHub Actions security incidents, our team is looking to harden the security of our workflows, as recommended by the Security team: https://discourse.canonical.com/t/security-update-action-requested/7120
Unfortunately, GitHub Actions does not have well-isolated security contexts, so all workflows carry a high degree of access/trust and can be used to compromise other workflows. Even if a workflow is run with `permissions: {}` (all GITHUB_TOKEN permission scopes set to none), it is still possible for an attacker to escalate privileges to workflows with broader permission scopes. For a real example of this, look at https://github.com/AdnaneKhan/Cacheract and the "But Wait, There’s More" section of https://www.praetorian.com/blog/codeqleaked-public-secrets-exposure-leads-to-supply-chain-attack-on-github-codeql/#:~:text=But%20Wait%2C%20There%E2%80%99s%20More. (Even when run with `permissions: {}`, a workflow has access to upload artifacts and to write to the repository's action cache. In that example, a compromised workflow without GITHUB_TOKEN permissions poisons the cache, which leads to arbitrary code execution in a workflow with secrets and with greater GITHUB_TOKEN permissions.)
For our team (Data), since we use `documentation-workflows` in our charm repositories (e.g. https://github.com/canonical/postgresql-operator/blob/6cbfaf9f1f26ac74f429813113dcb15b4449ec73/.github/workflows/automatic-doc-checks.yml#L21), a compromise of `documentation-workflows` could result in a compromise of our release artifacts—and thus, a compromise in Canonical's infrastructure or a customer's infrastructure. `documentation-workflows` has a high degree of trust and, though a privilege escalation attack, can affect our most important-to-secure artifacts. Therefore, we believe it's important to secure `documentation-workflows` to the same standard that our release pipeline is secured to.
After a quick review of the repository, here are some actionable suggestions to improve the security posture—from a combination of the recommendations in https://discourse.canonical.com/t/security-update-action-requested/7120 and from our team's experience maintaining workflows:
- Add the [zizmor](https://github.com/zizmorcore/zizmor) security linter
- In case it's useful, here's the zizmor configuration our team uses: https://github.com/canonical/data-platform-workflows/blob/main/.github/workflows/lint_workflows.md
- Replace `actions/setup-python` with the system Python that's included in Ubuntu, if possible
- setup-python downloads Python binaries maintained by GitHub. If the system Python is sufficient, using that instead would reduce the attack surface (since we [Canonical] maintain the Python binary in Ubuntu and already trust it in the OS).
- Use [Renovate](https://docs.renovatebot.com/) to keep actions up-to-date
The workflows currently use actions/checkout@v4, which has not been updated for 5 months. v6.0.2 is the latest version
https://github.com/canonical/documentation-workflows/blob/aaeaf091e8f55145184ad897cb9834f224bd31de/.github/workflows/spelling-check.yaml#L53
- Create versioned releases and enable [immutable releases](https://docs.github.com/en/code-security/concepts/supply-chain-security/immutable-releases). Also, ensure those versions are used in sub-calls. For example, even if a user of the `documentation-checks.yaml` reusable workflow pins it to a commit SHA, the reusable workflow references actions via `@main`, which will always grab the latest version of the action. e.g. https://github.com/canonical/documentation-workflows/blob/aaeaf091e8f55145184ad897cb9834f224bd31de/.github/workflows/link-check.yaml#L64
- Always getting the latest version means that if `documentation-workflows` is compromised, all its consumers will also be immediately compromised—this enables an attacker to escalate quickly, before we can identify and remedy the compromise
- To propagate versions to sub-calls, https://github.com/canonical/get-workflow-version-action can be used
- Immutable releases ensure that an attacker isn't able to modify existing release tags to point to a different commit SHA, such as what happened in the [recent trivy attacks](https://www.stepsecurity.io/blog/trivy-compromised-a-second-time---malicious-v0-69-4-release)
- Enable branch protection that requires at least 2 approvals before a PR is merged
- (So that compromise of a single person's GitHub account cannot compromise our workflows)
## A simpler alternative
Given that the workflows appear to mostly be calling make targets, it may be simpler (and safer) to not have a reusable workflow at all—and to instead provide a few lines of reference YAML that can be copy-pasted into repositories' workflows (which would remove the security and maintenance burden from your team).
For example:
### Usage
Add `documentation_checks.yaml` to `.github/workflows/`
```yaml
name: Automatic documentation checks
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches:
- main
paths:
- 'docs/**'
pull_request:
paths:
- 'docs/**'
jobs:
check-docs:
name: Check documentation
runs-on: ubuntu-latest
timeout-minutes: 30
defaults:
run:
working-directory: docs
steps:
- name: Checkout
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Set up environment
run: |
sudo apt-get install -y aspell aspell-en
sudo snap install woke
- run: make install
- name: Spell check
run: make spelling
- name: Inclusive language check
run: make woke
- name: Link check
run: make linkcheck
permissions:
contents: read
```
---
If you have any questions, please feel free to reach out to our team! Happy to share what we've learned from hardening our own workflows
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by reviewing the reusable workflows and the referenced .github/workflows/automatic-doc-checks.yml, spelling-check.yaml, and link-check.yaml examples. Compare the listed hardening options, including zizmor, action version pinning, immutable releases, and branch protection, and clarify whether to secure the reusable workflows or replace them with reference YAML. Done means the chosen approach is documented and the affected workflows are consistently updated and checked.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- github-actions
- Domain
- ci-cd, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 30/100