Per-organization federation

How Ringleader authenticates to your cloud with a signed, organization-scoped token instead of a shared identity or a stored key — and the end-to-end onboarding flow.

Per-organization federation is how a Ringleader control plane proves to your cloud that it is acting for your organization — without any Ringleader-owned identity that other customers also trust, and without storing a credential of yours.

Ringleader acts as an OpenID Connect issuer. It signs a short-lived token whose subject is your organization’s id, and your cloud’s own trust configuration decides whether to accept it. That decision is entirely yours: you write it, you can inspect it, and you can revoke it in one command.

Why it exists

The alternative — and the model Ringleader used before — is a shared intermediary: one Ringleader-owned cloud identity that every customer’s account grants impersonation rights to. It works, but the trust is coarse: the thing your account trusts is a single principal that is also trusted by everyone else’s.

Federation replaces that with a per-organization claim. The token Ringleader signs for your workstations says org:<your-org-id> and nothing else, and your trust configuration pins exactly that subject. A token minted for another organization is refused by your cloud, not merely by Ringleader.

The three claims that make it work

Every token Ringleader signs for your organization carries three values, and each one is derived server-side from the workstation’s namespace — never from anything a user can type on an object:

ClaimValuePinned by you as
Issuer (iss)<issuer-url>/org/<org-id>GCP: issuer_uri. Azure: the credential’s issuer. AWS: the IAM OIDC provider’s URL.
Subject (sub)org:<org-id>GCP: attribute_condition + the exact-subject IAM binding. Azure: the credential’s subject. AWS: the role trust’s sub condition.
Audience (aud)GCP: <issuer-url>/org/<org-id>/gcp. Azure: api://AzureADTokenExchange. AWS: <issuer-url>/org/<org-id>/aws.GCP: allowed_audiences. Azure: the credential’s audiences. AWS: the provider’s client-id list + the role trust’s aud condition.

Your cloud fetches Ringleader’s public signing keys from the issuer itself, at:

<issuer-url>/org/<org-id>/.well-known/openid-configuration
<issuer-url>/org/<org-id>/jwks

These are public documents containing public keys. Ringleader publishes a set of accepted keys, so a signing key can be rotated without you re-onboarding anything.

Two different 'audiences' on Google Cloud

Google Cloud’s token exchange takes an audience parameter — the resource name of your workload identity provider — which is not the same thing as the token’s aud claim. Both are pinned, independently. Confusing the two is the single most common Workload Identity Federation mistake; the onboarding assets get it right, and you never have to write the aud claim yourself.

The end-to-end onboarding flow

Four steps, three different people. Only step 1 happens in your cloud.

1. You configure the trust in your cloud

Run the onboarding assets for your cloud:

  • Google Cloud: a least-privilege service account, plus a workload identity pool and OIDC provider trusting Ringleader’s issuer for your organization’s subject only.
  • Microsoft Azure: an Entra app with a custom least-privilege role, plus a federated identity credential trusting the same three claims.
  • AWS: an IAM OIDC identity provider and a least-privilege IAM role whose trust policy pins the same subject and audience, assumed keyless with AssumeRoleWithWebIdentity.

Ringleader gives you two values up front — the issuer URL and your organization id — and you hand back a handful of identifiers. All of them are public: a service-account email, a provider resource name, an application client id, a tenant id, a subscription and a subnet.

The organization id is your organization’s uid, never its name — a name can be deleted and reused, a uid cannot:

rl org get acmecorp -o jsonpath='{.metadata.uid}'

2. An organization administrator creates the CloudAccount

The identifiers you handed back become a CloudAccount: a cluster-scoped object that records which cloud identity to use — which principal to become, and whose trust to present the token to. Only organization administrators can write one; a namespace member cannot.

apiVersion: core.ringleader.dev/v1
kind: CloudAccount
metadata:
  name: acme-gcp
spec:
  org: acme
  provider: gcp
  gcp:
    targetServiceAccount: ringleader-workstations@acme-dev.iam.gserviceaccount.com
    workloadIdentityProvider: projects/123456789/locations/global/workloadIdentityPools/ringleader/providers/oidc
rl apply -f acme-gcp-account.yaml
rl ca get

3. A namespace administrator points a CloudIdentity at it

The CloudIdentity lives in the team’s namespace and carries the VM’s size and placement — project, zone, machine type, subnet, labels — plus a reference to the account:

apiVersion: core.ringleader.dev/v1
kind: CloudIdentity
metadata:
  name: gcp
  namespace: dev
spec:
  provider: gcp
  selector:
    matchLabels:
      cloud: gcp
  cloudAccountRef: acme-gcp
  defaultProviderConfig:
    gcp:
      project: acme-dev
      zone: us-east4-c
      machineType: c4-standard-4
      subnetwork: ringleader-workstations
      networkTags: [ringleader-workstation]   # matches the firewall rule from step 1

The namespace must belong to the same organization as the account. If it does not, the apply is refused — an account belonging to another organization and an account that does not exist give the same message, so the error cannot be used to discover other organizations’ account names.

The networkTags line is what makes the workstation usable: it matches the SSH firewall rule the onboarding assets created. A workstation with no inbound path still finishes setting up and reports Ready — and nobody can open it. See reaching your workstations.

Confirm the identity resolves before anyone boots a workstation on it — this is the gate that tells you the trust you configured in step 1 actually works:

rl wait cloudidentity gcp -n dev --for Ready --timeout 2m   # waits for status.valid: true

4. A user labels their workstation

Nothing else. The label the CloudIdentity selects on is all a workstation needs:

apiVersion: workstations.ringleader.dev/v1
kind: Workstation
metadata:
  name: my-cloud-box
  namespace: dev
  labels:
    cloud: gcp
spec:
  requirements: [provider:gcp]
rl apply -f my-cloud-box.yaml
rl workstation wait my-cloud-box --for Ready --timeout 15m

Migrating an existing account

An identity already on the impersonation model moves over with a one-line edit: replace its spec.impersonation block with spec.cloudAccountRef, once the CloudAccount exists and your cloud carries the new trust.

 spec:
   provider: gcp
-  impersonation:
-    intermediaryPrincipal: ringleader-iam@example-iam.iam.gserviceaccount.com
-    targetPrincipal: ringleader-workstations@acme-dev.iam.gserviceaccount.com
+  cloudAccountRef: acme-gcp

Three things make this safe to do gradually:

  • It is per identity. Identities that still carry impersonation keep working exactly as before; there is no coordinated all-at-once switch.
  • It is reversible. Put the impersonation block back and the identity mints the old way again.
  • Both trusts can coexist. On both clouds the new federated trust sits beside the existing one, so you can add it, migrate, verify, and only then remove the old one.

Migrate one identity, confirm a workstation boots through it, then move the rest.

Failure is loud, never silent

A federated identity that cannot mint fails the workstation with a clear message. It never quietly falls back to another credential path, which would put a workstation back on a trust you thought you had left behind:

What is wrongWhat you see
cloudAccountRef names an account your organization does not own (or that does not exist)The apply is refused.
The workstation’s namespace belongs to no organizationThe apply is refused: no token can be signed for it.
Your cloud’s trust does not match what Ringleader signsThe workstation fails to create, with the cloud’s own rejection surfaced on its status.
The control plane has no federation configuredThe workstation fails with an explicit “cloud federation is not configured”.

If a mint is refused by your cloud, check the three pins first: the issuer string (byte-exact, no trailing slash), the subject, and the audience.

See also