Skip to main content

IAMOIDCTrust

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.

CI pipeline federation is being rolled out gradually.

apiVersioniam.stackngo.io/v1alpha1
kindIAMOIDCTrust
Applied bysng tenant oidc create / delete (or PUT /v1/iam/oidc-trusts/{name}), not by sng apply
Related commandssng 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:

gitlab-deploy.yml (written by the API)
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​

github-release.yml
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.name is a DNS label of at most 63 characters.
  • issuer starts with https:// and contains neither ? nor # (nor credentials in the URL); 9 to 255 characters.
  • subject does not start with * and has at least 8 literal characters before the first *: a trust cannot accept every project of an instance.
  • claims keys are 1 to 64 characters among A-Z a-z 0-9 _ . : - and cannot be iss, aud, sub, exp, nbf, or iat (covered by issuer, audiences, subject).
  • maxSessionDuration is a duration between 5m and 1h.

Additional API rules:

  • 1 to 8 audiences of at most 255 characters; without audiences (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.

ChampDescriptionDéfautValidation
apiVersion stringiam.stackngo.io/v1alpha1
kind stringIAMOIDCTrust
metadata ObjectMetaSeul 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.

ChampDescriptionDéfautValidation
issuer stringÉmetteur OIDC (iss du jeton), en https, sans query ni fragment.MaxLength: 255
MinLength: 9
audiences string arrayAudiences acceptées : le aud du jeton doit en contenir une.MaxItems: 8
MinItems: 1
items:MaxLength: 255
items:MinLength: 1
subject stringMotif 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 DurationDurée maximale de la session émise, entre 5m et 1h (défaut 1h).MaxLength: 32
Type: string
Optional: {}
deletionPolicy DeletionPolicyDelete (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.
DeleteEnum: [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).

ChampDescriptionDéfautValidation
observedGeneration integerDernière version du manifeste (metadata.generation) prise en compte par la plateforme.
provider ProviderStatusIdentité de la ressource dans le service d'authentification.
lastSyncedAt TimeDate de la dernière prise en compte réussie du manifeste.Optional: {}
conditions Condition arrayConditions décrivant l'état actuel de la ressource (Ready…).
tenant stringTenant auquel le trust appartient.
version integerVersion 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: 16
  • MinItems: 1
  • items:MaxLength: 255
  • items: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]
ValeurDescription
BlockBloque la suppression tant que la ressource est encore utilisée par une autre.
OrphanSupprime le manifeste mais conserve la ressource créée.
DeleteSupprime la ressource avec le manifeste, sans vérifier son usage.

ProviderStatus​

ProviderStatus identifie la ressource dans le service d'authentification.

ChampDescriptionDéfautValidation
owner stringOrganisation propriétaire de l'objet dans l'annuaire d'identité. Renseigné par la plateforme.
name stringNom de l'objet dans l'annuaire d'identité. Renseigné par la plateforme.
resourceID stringIdentifiant 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​

ConditionReasonMeaningCorrective action
Ready=TrueRegisteredthe trust is registered—
Ready=FalseInvalidthe spec is rejected (rule above)sng tenant oidc delete then create with a fixed spec
Ready=FalseIAMUnavailableidentity service temporarily unreachableautomatic retry
Ready=FalseScopeUnresolvedthe trust's tenant could not be determinedautomatic retry; if it persists, contact support
Synced=TrueReconciledthe trust in effect matches the manifest—
Discovered=TrueDiscovereddiscovery of the issuer (/.well-known/openid-configuration, public keys) succeeded—
Discovered=FalseDiscoveryFailedissuer unreachable or wrong URL; the message gives the causecheck 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​

SymptomCauseFix
subject "repo:*": at least 8 literal characters before the first '*'pattern too broadspecify the repository: repo:acme/infra:*
claims must not override iss, aud, sub, exp, nbf or iatreserved claim in claimsuse issuer, audiences, subject
401 invalid identity token on exchange, even though the token is validno trust matches (audience, branch, claim), or several docompare 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 forbiddenthe trust has no policy, or a policy that is too narrowsng tenant policy attach POLICY --oidc-trust <nom>
Discovered=False, DiscoveryFailedissuer 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 issuera trust only targets the issuer of an external CI
discovery: failed: … in sng tenant oidc getissuer unreachable or wrong URL when the trust was appliedcheck issuer; discovery is retried at each token exchange
400 policies: invalid request: at most 20 oidc trusts per tenantlimit reacheddelete unused trusts; one trust can accept several branches (--ref '*', claims)

See also​