Learning objectives
By the end of this module, you should be able to:
- Set explicit least-privilege GITHUB_TOKEN permissions.
- Explain why secrets must not be printed or embedded in source.
- Describe OIDC as a way to obtain short-lived cloud credentials without storing long-lived keys.
Start with least privilege
GitHub Actions permissions control what the automatically provided GITHUB_TOKEN can do. Explicitly request only the permissions required by the workflow or job; unspecified permissions become none when an explicit permissions map is used.
Secrets are not normal variables
Store sensitive values in supported secret stores and avoid echoing them, passing them through untrusted inputs or writing them into artifacts. A secret that reaches attacker-controlled code should be considered exposed.
OIDC avoids long-lived cloud keys
GitHub Actions can request an OIDC token when id-token: write is granted, then exchange it with a configured cloud identity provider for short-lived credentials. The permission allows requesting the OIDC token; it does not itself grant cloud-resource access.
Worked examples
See the idea in practice.
Minimal permissions for federated deployment
permissions:
contents: read
id-token: write
- contents: read supports source checkout.
- id-token: write lets the job request an OIDC token for an external identity exchange; the cloud-side trust policy still determines authorization.
Hands-on lab
Lab — Detect accidental secret exposure
Goal: Build a local guard that fails if secret-like material is placed in a release directory.
STEP 1Create release tree
mkdir -p ~/gnu-cicd-labs/module07/release && cd ~/gnu-cicd-labs/module07
printf 'public-config=true\n' > release/app.conf
STEP 2Create secret guard
cat > secret-guard.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
if grep -RIE '(BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY|password[=:]|token[=:])' release; then
echo 'secret-like material detected' >&2
exit 1
fi
echo 'SECRET GUARD PASS'
EOF
chmod +x secret-guard.sh
STEP 3Run guard
./secret-guard.sh
STEP 4Document workflow permissions
cat > permissions.yml <<'EOF'
permissions:
contents: read
id-token: write
EOF
grep -q 'id-token: write' permissions.yml
Verify
- The release tree passes the secret guard.
- The permission example grants read-only contents plus OIDC token request permission.
Expected outcome
- Sensitive credentials are treated as a separate security concern from normal configuration.
If it fails
- Pattern checks are only one control and can have false positives/negatives; use dedicated secret scanning in production.
- OIDC requires a matching trust configuration at the cloud provider.
Real-world connectionShort-lived identity and least privilege reduce the blast radius of a compromised pipeline compared with static administrator keys.
Avoid these traps
Common mistakes
- Giving write-all to every workflow.
- Printing secrets for debugging.
- Assuming id-token: write automatically grants cloud permissions.
Knowledge check
Can you explain it without looking back?
Open each answer only after you have tried to answer the question yourself.
1What should GITHUB_TOKEN permissions follow?
Least privilege: grant only what the workflow or job needs.
2What does id-token: write permit?
It allows the workflow to request a GitHub OIDC token; external authorization still depends on the provider trust policy.
3Why prefer OIDC over a long-lived cloud key where supported?
OIDC can exchange trusted workload identity for short-lived credentials instead of storing a reusable static secret.