Skip to main content

Deploy without a secret (OIDC)

Beta

Being rolled out. The interface described here is the one of the next release: it may still change, and may not be enabled on your tenant yet.

GitLab CI and GitHub Actions hand each job a short-lived OIDC token, which says "this job belongs to this project, on this branch". StackNGO exchanges it for a tenant session. Result: no secret to store, nothing to rotate.

You declare which pipelines you trust with an OIDC trust (IAMOIDCTrust), then you attach a policy to it, exactly as for a user.

Prerequisites​

  • An administrator session on the tenant (sng tenant use acme).
  • The CI project and its deployment branch.

1. Declare the trust​

# GitLab: pipelines of the protected branch main of the project acme/infra
sng tenant oidc create gitlab-deploy \
--gitlab-project acme/infra --ref main \
--claim project_id=4242 --claim ref_protected=true

# GitHub: workflows of the repository acme/infra on main
sng tenant oidc create github-deploy \
--github-repo acme/infra --ref main \
--claim repository_id=123456789
OIDC trust "gitlab-deploy" created in tenant "acme" (pending: applied in a few minutes)
subject: project_path:acme/infra:ref_type:branch:ref:main
next: sng tenant policy attach POLICY --oidc-trust gitlab-deploy
FlagEffect
--gitlab-project GROUP/PROJECTIssuer https://gitlab.com (or --gitlab-url), subject project_path:GROUP/PROJECT:ref_type:branch:ref:REF
--github-repo ORG/REPOIssuer https://token.actions.githubusercontent.com, subject repo:ORG/REPO:ref:refs/heads/REF
--ref REFTrusted branch (main by default, * = all)
--claim KEY=V1,V2Additional required claims; AND between keys, OR between values
--max-session 15mDuration of the sessions opened by this trust, from 5m to 1h (default 1h)
--issuer, --audience, --subjectFor any issuer, without helpers
-f FILEAn IAMOIDCTrust manifest or its spec, in YAML or JSON (- = standard input)
Pin a stable identifier

A project path can be renamed and then reused by someone else. project_id (GitLab) and repository_id (GitHub) never change: always add them as --claim.

The trust is active a few minutes later (STATUS switches to applied):

sng tenant oidc list
NAME ISSUER SUBJECT STATUS
gitlab-deploy https://gitlab.com project_path:acme/infra:ref_type:branch:ref:main applied

sng tenant oidc get gitlab-deploy details the trust. Once the trust is applied, the discovery line says whether the issuer answered: ok, or failed: <cause> (check the issuer URL; the check is made again at each token exchange). An issuer of the platform itself is refused at creation (issuer "…" is reserved to the platform).

2. Give it rights​

A trust grants nothing by itself.

sng tenant policy attach readwrite --oidc-trust gitlab-deploy

readwrite is enough for sng apply. For a custom policy (for example deploy:* without ssm:read), see Policies. Give the pipeline the bare minimum.

3. Use it in the pipeline​

The CLI detects the job token and opens the session on its own, in memory. You just need to tell it which tenant.

VariableRole
STNG_TENANTTarget tenant (required)
STNG_OIDC_TOKENOIDC token of the job (GitLab id_tokens)
STNG_OIDC_TOKEN_FILESame, in a file
STNG_OIDC_AUDIENCEGitHub only: audience to request (default: the API URL)
STNG_OIDC_TRUSTName of the trust to use if several trusts of the tenant could match

Under GitHub Actions, the CLI directly uses ACTIONS_ID_TOKEN_REQUEST_URL and ACTIONS_ID_TOKEN_REQUEST_TOKEN provided by the runner (with permissions: id-token: write). An expired session is reopened by requesting a token from the CI again; nothing is written to disk.

Complete pipelines are in GitLab CI and GitHub Actions.

Persistent runner​

If several commands must share a stored session (persistent runner, scripts outside sng), sng sso login --oidc exchanges the token once and stores the session in the runner's configuration file:

OIDC session saved: tenant acme (token from STNG_OIDC_TOKEN, expires in 900s)

What the trust requires​

For a job to open a session, a trust of the target tenant must match on all points:

Trust fieldCondition
issuerThe token comes from this issuer (GitLab, GitHub or the one you specify)
audiencesThe token's aud is in the list (by default: the API URL)
subjectThe sub matches the pattern (* allowed, at least 8 literal characters before the first *)
claimsEach listed claim has one of the expected values
maxSessionDurationBounds the session, whatever the token duration

The session obtained has the principal oidc:<tenant>/<trust>: this is what you see in sng tenant policy principals and in the logs.

Troubleshooting​

SymptomCause and fix
no CI OIDC token: set STNG_OIDC_TOKEN …The job does not expose a token: add id_tokens (GitLab) or permissions: id-token: write (GitHub)
OIDC: tenant required: set STNG_TENANT …STNG_TENANT missing
invalid identity tokenNo trust matches, or several do: compare iss, aud, sub displayed by the CLI with sng tenant oidc get, or set STNG_OIDC_TRUST
jeton OIDC du CI expiréThe token provided by variable expired during the job: rerun, or lengthen its duration on the CI side
forbidden (no policy of this tenant allows it: see sng tenant policy list)The session is open, but the policy attached to the trust does not allow this action

Delete a trust​

sng tenant oidc delete gitlab-deploy

Already opened sessions remain valid until they expire (at most maxSessionDuration).

See also​