Chapter 20Lesson 03~165 minutes

Secrets, Configuration Variables, GITHUB_TOKEN, Fine-Grained Permissions, and OIDC: Configuration, Design Choices, and Tradeoffs

Secure configuration is a design problem, not a menu choice. This lesson turns the mechanics from Lesson 2 into decision criteria: where each value belongs, which GitHub identity should act, where permissions should be declared, and when federation is safer than a long-lived cloud credential.

Design choicesGitHub AppsFine-grained PATOIDC trustDecision table

Learning objectives

  • Choose secret, variable, or repository file based on confidentiality, auditability, reviewability, and runtime ownership.
  • Choose GITHUB_TOKEN, GitHub App installation token, or fine-grained PAT based on identity and resource boundary.
  • Use job-level permissions to narrow authority below a workflow-level baseline.
  • Compare long-lived cloud secrets with OIDC federation and identify the controls the cloud provider must enforce.
  • Apply plan, repository-visibility, and deployment boundaries without making a paid feature mandatory.

Availability: The decision framework is GitHub.com-first and free-compatible. Repository-local secrets/variables and GITHUB_TOKEN do not need a paid plan. Organization-wide secret/variable sharing and private-repository environment behavior can depend on plan/policy. OIDC requires a compatible external verifier for a live exchange, so the mandatory exercises remain provider-neutral.

1. Secret versus variable versus repository file

Question Repository file Configuration variable Actions secret
Should reviewers see the value? Yes; that is a benefit. Yes; it is not confidential. No.
Should a change require a commit/PR? Usually yes. No; hosted config can change independently. No; credential lifecycle should be independent of source.
Should the value be visible in logs/UI/API? Yes, unless the file itself contains sensitive data—which it should not. Potentially yes. No; avoid printing and rely on secure use, not masking.
Good example Supported test matrix / policy fixture. Deployment region, feature mode, harmless endpoint name. API token, signing key, webhook secret.
Bad example Cloud private key committed to repo. Password stored as a variable. Public compiler version hidden in secrets.

Configuration drift is possible whenever a hosted variable can change without a commit. For critical non-secret policy inputs, decide whether repository review is more valuable than operator flexibility.

2. Choose the GitHub identity that actually owns the automation

Mechanism Identity/lifetime Reach Use it when
GITHUB_TOKEN Per-job repository GitHub App installation token Current workflow repository, constrained by policy/event/permissions. The workflow needs GitHub operations in its own repository.
GitHub App installation token App installation; short-lived tokens, durable app identity Installed repositories/org resources allowed by App permissions. A bot/integration must outlive individuals or operate across several repositories.
Fine-grained PAT Named user; explicit repositories/permissions/expiration Resources that user may access and the token selects. Personal automation or a short-lived compatibility gap where an App is not justified.

Do not choose a PAT merely because it “works everywhere.” Its human ownership and often-longer lifecycle are operational liabilities. Conversely, do not build an App for a five-minute personal API experiment if a narrow, expiring fine-grained PAT is appropriate.

3. Workflow-level versus job-level permissions

A workflow-level policy is convenient when every job needs the same GitHub authority. Job-level permissions are stronger isolation when only one job performs a write. A useful pattern is a deny-by-default workflow plus explicit job grants:

permissions: {}

jobs:
  test:
    runs-on: ubuntu-latest
    permissions:
      contents: read

  publish-report:
    needs: test
    runs-on: ubuntu-latest
    permissions:
      contents: read
      issues: write

The test job cannot inherit the write authority simply because another job has it. That matters when a build executes complex toolchains or third-party code but only a narrow reporting step needs to mutate GitHub state.

4. Long-lived cloud secret versus OIDC federation

Dimension Stored cloud key OIDC federation
GitHub storage A long-lived credential exists as a secret. No long-lived provider credential needs to be stored in GitHub.
Runtime credential Usually the same key until rotation. Provider issues a short-lived credential after validating GitHub's JWT.
Blast radius Depends on static key permissions and exposure duration. Bound by token lifetime plus trust-policy claims and provider role permissions.
Failure mode Leak/forgotten rotation/secret copied across repos. Over-broad audience/subject/claim trust can authorize unintended workloads.
Operational cost Simple initially; rotation/revocation burden later. More initial provider policy work; stronger workload identity when configured narrowly.

OIDC does not eliminate authorization policy. It moves the credential source from “stored key” to “verified workload assertion.” A permissive trust policy can still be dangerous.

5. Design a trust policy around immutable repository identity

For a newly created August 2026 GitHub.com repository, start from its actual immutable subject format—not a copied historical name-only subject. The default subject uses one context such as branch or environment. The following provider-neutral policy fixture intentionally shows multiple checks as policy intent; map them to the capabilities of your real provider.

{
  "issuer": "https://token.actions.githubusercontent.com",
  "audiences": ["https://cloud.example.invalid"],
  "subject": "repo:octo-org@123456/octo-repo@456789:environment:production",
  "required_claims": {
    "repository_owner_id": "123456",
    "repository_id": "456789",
    "ref": "refs/heads/main",
    "environment": "production",
    "workflow_ref": "octo-org/octo-repo/.github/workflows/deploy.yml@refs/heads/main"
  }
}

If the provider can bind only sub and aud, use the strongest supported subject and enforce the remaining branch/environment/workflow restrictions inside GitHub through environments, branch protections, and reusable-workflow governance. Never pretend a provider enforces claims it ignores.

6. Git checkout credentials are another persistence boundary

If a workflow checks out source but does not need authenticated Git commands afterward, do not leave credentials available longer than necessary. Current actions/checkout supports persist-credentials: false. This is separate from GITHUB_TOKEN issuance: the token may exist for the job, while checkout decides whether Git credential configuration is persisted for later Git operations.

- uses: actions/checkout@FULL_REVIEWED_COMMIT_SHA
  with:
    persist-credentials: false

Pin the action to a reviewed full commit SHA according to your supply-chain policy. Chapter 18 established why a mutable action tag and an immutable commit reference have different update and compromise tradeoffs.

7. Plan and deployment boundaries

  • Personal public lab: use repository secret/variable + explicit GITHUB_TOKEN permissions. This is the mandatory path.
  • Private environment workflows: verify the current plan and environment-feature availability before assuming reviewer/wait-timer behavior from Chapter 19.
  • Organization configuration: verify organization policy and repository-access selection. Do not assume a Free private repository can consume every organization-scoped value.
  • GHES: do not copy GitHub.com's post–July 15, 2026 immutable default subject rollout; verify the server version and OIDC documentation.

8. Worked production decision

A release pipeline needs to read repository metadata, publish one GitHub deployment status, and deploy to a cloud production account. The build job executes a large dependency tree; the deploy job is short.

Decision Choice Why
Build job token contents: read only Reduces impact if build tooling is compromised.
Deployment status Narrow deployments: write on deploy job Only the job that records deployment status receives the mutation permission.
Cloud auth OIDC with production environment subject No long-lived cloud key; trust binds workload context.
Non-secret target metadata Repository file or configuration variable Choose reviewable file for policy; variable for operator-tunable harmless value.
Cross-repo governance bot GitHub App Durable identity independent of a maintainer's personal token.

This design optimizes maintainability (explicit contracts), security (least privilege and no static cloud key), governance (reviewed workflow/environment), reliability (short-lived credentials tied to the run), compatibility (provider-neutral interface), and cost (no paid cloud required merely to learn the model).

Knowledge checks

A value is harmless but operators must change it without a commit. Which class is the default fit?

Why can a GitHub App be safer operationally than a PAT for a long-lived organization bot?

Why put issues: write only on a reporting job?

Does OIDC remove the need for least-privilege cloud IAM?

Why is persist-credentials: false relevant even with a short-lived GITHUB_TOKEN?

Lesson summary

Choose configuration by sensitivity and audit requirements, choose GitHub identity by ownership and resource boundary, scope token authority to the smallest job, and use OIDC when a provider can safely exchange verified workflow identity for short-lived credentials. The next lesson diagnoses what happens when any of those boundaries are wrong.

Next lesson

Secrets, Configuration Variables, GITHUB_TOKEN, Fine-Grained Permissions, and OIDC: Diagnostics, Failure Modes, Security, and Performance

Further reading

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.