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:
| Claim | Value | Pinned 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>/jwksThese 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
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/oidcrl apply -f acme-gcp-account.yaml
rl ca get3. 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 1The 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: true4. 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 15mMigrating 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
impersonationkeep working exactly as before; there is no coordinated all-at-once switch. - It is reversible. Put the
impersonationblock 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 wrong | What 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 organization | The apply is refused: no token can be signed for it. |
| Your cloud’s trust does not match what Ringleader signs | The workstation fails to create, with the cloud’s own rejection surfaced on its status. |
| The control plane has no federation configured | The 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
- CloudAccount: the object that records which cloud identity to use.
- CloudIdentity: the credential paths and the VM’s size and placement.
- Google Cloud, Microsoft Azure and AWS: the runbooks.