How to Manage CI Secrets#
This guide covers creating, scoping, rotating, and replacing static secrets with federated identity (OIDC) in S-CORE CI workflows.
Secret scopes in GitHub Actions#
GitHub provides three secret scopes:
Scope |
Where set |
Who can use it |
|---|---|---|
Repository |
Repository → Settings → Secrets |
Workflows in that repository |
Organization |
Org → Settings → Secrets |
Workflows in repositories where the secret is granted |
Environment |
Repository → Settings → Environments |
Workflows that target that environment |
Default to the narrowest scope that works. Use environment secrets for deploy-time credentials that should be gated by environment protection rules (e.g. requiring a reviewer before a secret is accessible).
Create a new secret#
Decide on the scope (repository, organization, or environment).
Go to the appropriate Settings → Secrets page on GitHub.
Click New secret (or New repository secret / New organization secret).
Give the secret a clear, uppercase, underscore-separated name (e.g.
REGISTRY_TOKEN).Paste the secret value and save.
To reference it in a workflow:
env:
MY_TOKEN: ${{ secrets.MY_TOKEN }}
Or pass it directly to a step:
- name: Authenticate
run: some-tool login
env:
TOKEN: ${{ secrets.MY_TOKEN }}
Rotate a secret#
Generate the new credential in the upstream service (GitHub PAT, API key, etc.).
Update the secret value in GitHub Settings.
Verify that workflows using the secret still succeed after rotation.
Revoke the old credential.
Rotation should happen on a regular cadence (e.g. annually for long-lived PATs) or immediately when a credential is suspected to be compromised.
Replace a static secret with OIDC#
OIDC lets CI jobs request short-lived credentials from a cloud provider instead of storing long-lived static secrets. This is the preferred pattern for cloud access where the provider supports it.
How it works#
GitHub Actions job → requests OIDC token from GitHub
→ exchanges token at cloud provider
→ receives short-lived credential
→ calls cloud provider API
The cloud provider is configured to trust GitHub’s OIDC issuer and to grant access to tokens with specific claims (e.g. the repository name, branch, or environment).
Example: AWS#
In AWS IAM, create an OIDC identity provider for
token.actions.githubusercontent.com.Create an IAM role with a trust policy that allows GitHub tokens from your repository and branch.
In the workflow, use the
aws-actions/configure-aws-credentialsaction — no static secret needed:
permissions:
id-token: write
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/my-role
aws-region: eu-central-1
For other providers, the pattern is the same: create the OIDC trust on the provider side, then use the appropriate GitHub Action to request credentials.
Cross-repository token provisioning#
S-CORE workflows sometimes need to act across repositories (e.g. opening a PR in another repo). The cicd-actions repository provides a composite action for this:
- uses: eclipse-score/cicd-actions/get-app-token@<version>
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
This uses a GitHub App instead of a personal access token, which gives better audit trails and allows fine-grained repository-level permissions.
Least-privilege checklist#
Before creating a new secret or token, check:
[ ] Does this credential need write access, or is read-only sufficient?
[ ] Can OIDC replace this static credential?
[ ] Is the secret scoped to the narrowest necessary scope (repo vs. org)?
[ ] Is there a documented rotation cadence?
[ ] Is the credential usage visible in workflow logs (without leaking the value)?