IAMOIDCTrust
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.
CI pipeline federation is being rolled out gradually.
apiVersion | iam.stackngo.io/v1alpha1 |
kind | IAMOIDCTrust |
| Applied by | sng tenant oidc create / delete (or PUT /v1/iam/oidc-trusts/{name}), not by sng apply |
| Related commands | sng tenant oidc list · get · create · delete, sng tenant policy attach --oidc-trust |
What it is for
An IAMOIDCTrust tells StackNGO to accept the OIDC token that a CI platform hands to each job (GitLab id_tokens, GitHub Actions): a token from this issuer, whose audience, sub, and claims match, is exchanged for a session in your tenant under the principal oidc:<tenant>/<nom>. No secret is stored in the CI. The trust grants no rights: attach a policy to it (sng tenant policy attach POLICY --oidc-trust <nom>). The full walkthrough is in Deploy without secrets (OIDC).
Minimal example
sng tenant oidc create gitlab-deploy --gitlab-project acme/infra --claim project_id=4242
sng tenant policy attach readwrite --oidc-trust gitlab-deploy
writes to the tenant's desired state:
apiVersion: iam.stackngo.io/v1alpha1
kind: IAMOIDCTrust
metadata:
name: gitlab-deploy
spec:
issuer: https://gitlab.com
audiences: [https://api.dev.stackngo.fr]
subject: "project_path:acme/infra:ref_type:branch:ref:main"
claims:
project_id: ["4242"]
The same manifest can be applied from a file: sng tenant oidc create gitlab-deploy -f gitlab-deploy.yml (without specifying a tenant).
Complete annotated example
apiVersion: iam.stackngo.io/v1alpha1
kind: IAMOIDCTrust
metadata:
name: github-release # DNS label, at most 63 characters
spec:
issuer: https://token.actions.githubusercontent.com # the token's iss, https, no query or fragment
audiences: # 1 to 8; the token's aud must contain one of them
- https://api.dev.stackngo.fr # CLI default: the API URL
subject: "repo:acme/infra:ref:refs/tags/v*" # sub pattern, * wildcard; 8 literal characters before the first *
claims: # 16 keys max: AND between keys, OR between values (wildcards allowed)
repository_id: ["123456789"] # stable identifier: protects against a repository rename
environment: ["production", "staging"] # either one
maxSessionDuration: 15m # lifetime of the issued sessions, 5m to 1h (default 1h)
sng tenant oidc create github-release -f github-release.yml
sng tenant policy attach ci-publisher --oidc-trust github-release
Equivalent without a file: sng tenant oidc create github-release --issuer https://token.actions.githubusercontent.com --subject 'repo:acme/infra:ref:refs/tags/v*' --claim repository_id=123456789 --claim environment=production,staging --max-session 15m.
Fields
Cross-field validation rules
Schema rules (CEL), also enforced by the API as early as sng tenant oidc create:
metadata.nameis a DNS label of at most 63 characters.issuerstarts withhttps://and contains neither?nor#(nor credentials in the URL); 9 to 255 characters.subjectdoes not start with*and has at least 8 literal characters before the first*: a trust cannot accept every project of an instance.claimskeys are 1 to 64 characters amongA-Z a-z 0-9 _ . : -and cannot beiss,aud,sub,exp,nbf, oriat(covered byissuer,audiences,subject).maxSessionDurationis a duration between5mand1h.
Additional API rules:
- 1 to 8
audiencesof at most 255 characters; withoutaudiences(field omitted or empty list), the API keeps the API URL (https://api.dev.stackngo.fr), the audience the CLI requests by default. 1 to 16 values per claim, of at most 255 characters. - At most 20 trusts per tenant.
- The issuer cannot be a platform issuer (its SSO, the API itself, the tokens it issues to your applications): the trust is refused as soon as it is requested,
400 issuer "…" is reserved to the platform, and nothing is recorded.
IAMOIDCTrust
IAMOIDCTrust déclare la confiance d'un tenant envers un émetteur OIDC externe (job CI GitLab id_tokens, GitHub Actions). Un jeton de cet émetteur dont l'audience, le sub et les claims correspondent ouvre une session sur la plateforme sous le principal oidc:<tenant>/<nom>. Le trust ne donne aucun droit : ils viennent d'un IAMPolicyBinding qui le cite (subjects.oidcTrusts). Le trust appartient à votre tenant.
| Champ | Description | Défaut | Validation |
|---|---|---|---|
apiVersion string | iam.stackngo.io/v1alpha1 | ||
kind string | IAMOIDCTrust | ||
metadata ObjectMeta | Seul metadata.name est à renseigner (nom de la ressource dans votre tenant). | ||
spec IAMOIDCTrustSpec | |||
status IAMOIDCTrustStatus |
IAMOIDCTrustSpec
IAMOIDCTrustSpec : émetteur, audiences et motifs (glob '*') que le jeton doit vérifier.
| Champ | Description | Défaut | Validation |
|---|---|---|---|
issuer string | Émetteur OIDC (iss du jeton), en https, sans query ni fragment. | MaxLength: 255 MinLength: 9 | |
audiences string array | Audiences acceptées : le aud du jeton doit en contenir une. | MaxItems: 8 MinItems: 1 items:MaxLength: 255 items:MinLength: 1 | |
subject string | Motif du sub du jeton (glob ''). Il ne commence pas par '' et compte au moins 8 caractères littéraux avant le premier ''. Un sujet GitLab (project_path:) ou GitHub (repo:) nomme le groupe ou l'organisation (jusqu'au '/') avant tout ''. | MaxLength: 512 MinLength: 1 | |
claims object (keys:string, values:OIDCClaimValues) | Claims supplémentaires : ET entre clés, OU entre valeurs (globs). Les valeurs booléennes ou numériques du jeton sont comparées en chaîne. | MaxProperties: 16 Optional: {} | |
maxSessionDuration Duration | Durée maximale de la session émise, entre 5m et 1h (défaut 1h). | MaxLength: 32 Type: string Optional: {} | |
deletionPolicy DeletionPolicy | Delete (défaut) : supprimer l'IAMOIDCTrust supprime le trust. Block : la suppression attend qu'aucun IAMPolicyBinding de votre tenant ne le cite (subjects.oidcTrusts), puis supprime le trust. Orphan : le trust reste en vigueur. | Delete | Enum: [Block Delete Orphan] Optional: {} |
IAMOIDCTrustStatus
IAMOIDCTrustStatus : conditions Ready (raisons Registered, Invalid ou IAMUnavailable) et Discovered (lecture réussie ou non de la configuration publique de l'émetteur).
| Champ | Description | Défaut | Validation |
|---|---|---|---|
observedGeneration integer | Dernière version du manifeste (metadata.generation) prise en compte par la plateforme. | ||
provider ProviderStatus | Identité de la ressource dans le service d'authentification. | ||
lastSyncedAt Time | Date de la dernière prise en compte réussie du manifeste. | Optional: {} | |
conditions Condition array | Conditions décrivant l'état actuel de la ressource (Ready…). | ||
tenant string | Tenant auquel le trust appartient. | ||
version integer | Version du trust après la dernière modification enregistrée. |
OIDCClaimValues
Type sous-jacent : string array
OIDCClaimValues : valeurs acceptées pour un claim (OU, globs).
Règles de validation :
MaxItems: 16MinItems: 1items:MaxLength: 255items:MinLength: 1
DeletionPolicy
Type sous-jacent : string
DeletionPolicy indique ce que devient la ressource quand vous supprimez son manifeste : Block bloque la suppression tant que la ressource est encore utilisée, Orphan la conserve, Delete la supprime.
Règles de validation :
Enum: [Block Orphan Delete]
| Valeur | Description |
|---|---|
Block | Bloque la suppression tant que la ressource est encore utilisée par une autre. |
Orphan | Supprime le manifeste mais conserve la ressource créée. |
Delete | Supprime la ressource avec le manifeste, sans vérifier son usage. |
ProviderStatus
ProviderStatus identifie la ressource dans le service d'authentification.
| Champ | Description | Défaut | Validation |
|---|---|---|---|
owner string | Organisation propriétaire de l'objet dans l'annuaire d'identité. Renseigné par la plateforme. | ||
name string | Nom de l'objet dans l'annuaire d'identité. Renseigné par la plateforme. | ||
resourceID string | Identifiant de l'objet dans l'annuaire d'identité. Renseigné par la plateforme. |
Boolean or numeric token values (ref_protected: true, project_id: 4242) are compared as strings: write ["true"], ["4242"].
Status and conditions
| Condition | Reason | Meaning | Corrective action |
|---|---|---|---|
Ready=True | Registered | the trust is registered | — |
Ready=False | Invalid | the spec is rejected (rule above) | sng tenant oidc delete then create with a fixed spec |
Ready=False | IAMUnavailable | identity service temporarily unreachable | automatic retry |
Ready=False | ScopeUnresolved | the trust's tenant could not be determined | automatic retry; if it persists, contact support |
Synced=True | Reconciled | the trust in effect matches the manifest | — |
Discovered=True | Discovered | discovery of the issuer (/.well-known/openid-configuration, public keys) succeeded | — |
Discovered=False | DiscoveryFailed | issuer unreachable or wrong URL; the message gives the cause | check issuer; discovery is retried on every token exchange |
status.tenant gives the trust's tenant, status.version its registered version. On the CLI side, sng tenant oidc list shows pending, applied, or deleting, and sng tenant oidc get shows the discovery result (discovery: ok, or discovery: failed: <cause> (retried on use)) as soon as the trust has been applied. In the API, it is the discovery field ({"ok": true} or {"ok": false, "error": "…"}) of GET /v1/iam/oidc-trusts.
What happens on creation and deletion
Creation. sng tenant oidc create → PUT /v1/iam/oidc-trusts/<nom> (validation, 202, status: "pending"). A few moments later, the trust is registered and the issuer probed. As soon as Ready=True, a CI job can open a session (POST /v1/iam/session with its token and {"tenant": "acme"}).
Update. sng tenant oidc create refuses an existing name: delete and recreate. The API, however, accepts replacing a trust with PUT /v1/iam/oidc-trusts/<nom>.
Opening a session. For a token presented with {"tenant": "acme"}, StackNGO selects the trusts of acme for the token's issuer (or the only one named by trust), checks its signature, then requires that exactly one trust matches (audience, sub, each claim). The session lasts at most maxSessionDuration, regardless of the token's lifetime.
Deletion. sng tenant oidc delete <nom>: no job can open a session with this trust anymore. Sessions already open stay valid until they expire. Policy attachments that mention the trust stay in place, with no effect.
Common errors
| Symptom | Cause | Fix |
|---|---|---|
subject "repo:*": at least 8 literal characters before the first '*' | pattern too broad | specify the repository: repo:acme/infra:* |
claims must not override iss, aud, sub, exp, nbf or iat | reserved claim in claims | use issuer, audiences, subject |
401 invalid identity token on exchange, even though the token is valid | no trust matches (audience, branch, claim), or several do | compare the token's iss, aud, sub (shown by the CLI) with sng tenant oidc get; name the trust (STNG_OIDC_TRUST) |
Session opened, then 403 forbidden | the trust has no policy, or a policy that is too narrow | sng tenant policy attach POLICY --oidc-trust <nom> |
Discovered=False, DiscoveryFailed | issuer unreachable from the platform, or wrong URL (trailing slash, path) | check the exact URL of the tokens' iss |
sng tenant oidc create refuses: issuer "…" is reserved to the platform (400) | platform issuer | a trust only targets the issuer of an external CI |
discovery: failed: … in sng tenant oidc get | issuer unreachable or wrong URL when the trust was applied | check issuer; discovery is retried at each token exchange |
400 policies: invalid request: at most 20 oidc trusts per tenant | limit reached | delete unused trusts; one trust can accept several branches (--ref '*', claims) |
See also
- Guides: Deploy without secrets (OIDC), GitLab CI, GitHub Actions, Attach a policy
- CLI:
sng tenant oidc,sng sso login --oidc - API: Authentication — CI job
- Other kinds: IAMPolicyBinding, IAMPolicy