CI/CD Fundamentals · Module 07 of 12

Secrets, token permissions and OIDC

Reduce credential exposure with least privilege, protected secrets and short-lived identity federation.

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 1

Create release tree

mkdir -p ~/gnu-cicd-labs/module07/release && cd ~/gnu-cicd-labs/module07
printf 'public-config=true\n' > release/app.conf
STEP 2

Create 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 3

Run guard

./secret-guard.sh
STEP 4

Document 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 connection

Short-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.

My Learning

Record your progress for this module.

Progress is saved in this browser immediately. When you sign in to My Learning, it can also sync across devices.

Not started

Attempt the core lab and knowledge checks before marking the module complete.