From f1bb1d3f49b6b77092b6c47ab85b88af1a3a8c08 Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 11:48:59 +0200 Subject: [PATCH 01/21] Add draft RFC for space-owned workload service accounts --- toc/rfc/rfc-draft-service-accounts.md | 172 ++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 toc/rfc/rfc-draft-service-accounts.md diff --git a/toc/rfc/rfc-draft-service-accounts.md b/toc/rfc/rfc-draft-service-accounts.md new file mode 100644 index 000000000..b63267610 --- /dev/null +++ b/toc/rfc/rfc-draft-service-accounts.md @@ -0,0 +1,172 @@ +# Meta +[meta]: #meta +- Name: Service Accounts for Cloud Foundry Workload Identity +- Start Date: 2026-10-06 +- Author(s): @rkoster +- Status: Draft +- RFC Pull Request: To be added after submission +- Related RFCs: [RFC 0055: Identity-Aware Routing for GoRouter](rfc-0055-identity-aware-routing-for-gorouter.md) +- Affected Component(s): Cloud Controller, BBS, Diego, UAA, CF CLI; subsequently GoRouter and service brokers + +## Summary + +Introduce space-owned service accounts: stable identities that selected apps share +without sharing a private key or distributing client secrets. Cloud Controller +manages the account and its OAuth client; Diego adds its identity to each app's +instance certificate. Apps authenticate to UAA with that certificate and obtain +short-lived bearer JWTs for explicitly authorized resources. + +```sh +cf create-service-account payments-worker +cf bind-service-account payments-api payments-worker +cf bind-service-account payments-jobs payments-worker +``` + +This condenses the [original proposal][source] and deliberately revises its +org-owned model to **space ownership and same-space assignment**. Implementation +drafts provide a tested starting point, not a prerequisite for accepting their +exact API or configuration details. + +## Problem + +Instance GUIDs and app GUIDs identify individual workloads, but are unsuitable as +a durable identity shared across an API, background workers, replacements and +blue/green deployments. Applications commonly compensate with stored client +secrets or externally managed credentials. + +Cloud Foundry already issues per-instance certificates and authorizes OAuth client +principals. Connecting these mechanisms through an explicit account lifecycle +provides stable workload identity while preserving instance-level attribution. + +## Proposal + +### Identity and authorization boundary + +- An account has an immutable UUID, name and owning space. Names are + foundation-unique, lowercase DNS labels of 3–63 characters. Deletion permanently + reserves the name, preventing another owner from inheriting external grants. +- Each app has zero or one account; multiple apps in the owning space may share + it. Cross-space assignment is excluded, including within the same organization. +- The initial permission model uses space managers/platform admins for account + management and app writers for assignment in a writable owning space. +- Creating or binding an account grants no resource permissions. CAPI roles, + route rules and broker privileges are explicit and shared by all apps using it. + Anyone able to deploy code to those apps can exercise those permissions. + +| Identity | Example | +| --- | --- | +| Name | `payments-worker` | +| OAuth client ID / JWT subject | `cf:service-account:payments-worker` | +| Certificate DNS SAN | `payments-worker.svc.identity` | +| External identity | Trusted `(issuer, subject)` pair | + +The SAN suffix is identity-only: it creates no route or DNS record and is never +resolved to authenticate a caller. Separate foundations may reuse names; their +issuers and CA trust domains must remain distinct. + +### Control plane and developer experience + +Creation reserves the account. First authorized bind asynchronously provisions +one UAA client and roleless CAPI OAuth principal; further binds reuse them. Jobs +must survive retries/concurrency without adopting an unmanaged client-ID collision +or issuing credentials before provisioning is ready. + +CAPI exposes `/v3/service_accounts`, account app listing, and the app's +`/relationships/service_account` relationship. Resource relationships use UUIDs; +names are human-facing lookup keys. The CLI provides create/list/show/delete, +bind/unbind and enable/disable commands, waits for jobs, and reports identity, +provisioning state and restart guidance. Existing `/v3/roles` semantics and org +membership prerequisites apply to the managed principal. + +Accounts with no assigned apps retain their identity and roles until explicitly +disabled or deleted. Deletion requires removal of active workload references; +future route/broker references must also be resolved before teardown. Namespace +quotas and audit events must cover account creation, grants and assignment changes. + +### Credentials and token profile + +CAPI supplies a platform-owned account name through typed BBS certificate +properties. Diego derives exactly one account SAN in instance and C2C credentials, +preserving existing GUID/CN/IP, app/space/org OUs, route SANs and certificate renewal. +Every instance keeps its own key. Runtime tasks inherit their launch assignment; +staging receives no account identity. Other SAN-producing inputs cannot inject the +reserved suffix; malformed or ambiguous account identities fail closed. + +UAA uses [RFC 8705 PKI client authentication][mtls] with a validated chain and +exactly one registered DNS SAN binding. Managed clients are secretless, +`client_credentials`-only, provisioned in the default UAA zone. Operators must +protect `cf:service-account:` across client-management paths and zones; ordinary +client administrators cannot create, alter or take over managed registrations. + +Mutual-TLS client authentication is independent of certificate-bound access +tokens. Set [`tls_client_certificate_bound_access_tokens: false`][binding] on the +managed client: tokens are bearer JWTs **without `cnf`**, usable without presenting +the instance certificate to CAPI. Require trusted issuer, stable account subject, +authorized audience/scopes and expiry no later than five minutes or the issuing +leaf certificate's expiry. Platform-controlled caller claims should retain +app/space/org/instance attribution and must not be overridden by app templates. + +Non-secret `VCAP_SERVICE_ACCOUNT` metadata supplies the client ID and token +endpoint. Libraries reread rotated instance credential files when acquiring +tokens. UAA authenticates against its managed registration without a live CAPI +assignment lookup. Resource servers independently validate tokens and permissions. +The first delivery uses a fixed CAPI audience; additional targets require explicit +audience/scope policy rather than universal multi-audience tokens. + +### Lifecycle and rollout + +Bind/unbind changes desired configuration; running containers retain their +launch-time identity, including renewal, until replaced. **Restart is required**; +restaging solely for identity changes is unnecessary. New tasks use the desired +assignment. Unbind must precede assigning a different account. + +Restart does not revoke copied credentials. While the shared client is enabled, +an old certificate/key can obtain tokens until certificate expiry; each token is +also capped by that expiry. Without restart, an old running assignment can keep +renewing. Account disable stops new tokens when effective, not existing JWTs or +certificate-only access. Immediate per-app cryptographic revocation is out of scope. + +Features are operator-enabled only after compatible CAPI, BBS, cells and UAA are +deployed. Unsupported identity features must fail closed; silently dropping SANs +is not acceptable. Capability discovery and rollback must account for all cells +and consumers before enabling new assignments. + +### Delivery and remaining decisions + +1. Deliver the account lifecycle, native CLI, Diego identity, UAA bearer profile + and explicit CAPI roles. +2. Add curated token targets/external federation and account sources for RFC 0055 + route policies. Route matching requires verified SAN-bearing certificate data + and preserves caller org/space domain restrictions and default-deny behavior. +3. Negotiate broker/driver support for identity-based service bindings separately; + this RFC does not standardize new OSB fields or change legacy bindings. + +Maintainers should confirm ownership/permission defaults, name retention and +quotas, namespace protection, token/caller claim policy, API naming and mixed-version +capability signaling before finalizing the contract. + +### Implementation evidence + +Drafts: [CAPI][capi], [capi-release][release], [BBS contract][bbs], [Diego][diego], +[CLI][cli]. They preserve committed RED/GREEN tests and include verification notes. +The lab demonstrated two apps sharing a SAN with distinct keys, runtime-task +inheritance, roleless tokens seeing zero apps, explicit roles enabling access, +disable/enable, and unbind/restart/rebind through the native CLI. + +**Remaining acceptance requirements:** current [UAA mTLS work][uaa] emits `cnf`; +bearer-policy handling, leaf-expiry caps, managed-namespace enforcement and caller +claims require completion. BBS module publication and automatic rollout capability +signaling remain open. Timed live renewal, staging certificate inspection, Windows +execution and the full negative/rotation matrix need further evidence. Lab CAPI +calls used internal HTTP; production bearer use requires TLS. The POC therefore +does not yet meet the complete proposed profile. + +[source]: https://gist.github.com/rkoster/ee2ae127944943707c44f8f6f8b3f83d +[mtls]: https://www.rfc-editor.org/rfc/rfc8705.html#section-2.1 +[binding]: https://www.rfc-editor.org/rfc/rfc8705.html#section-3.4 +[capi]: https://github.com/cloudfoundry/cloud_controller_ng/pull/5520 +[release]: https://github.com/cloudfoundry/capi-release/pull/702 +[bbs]: https://github.com/cloudfoundry/bbs/pull/168 +[diego]: https://github.com/cloudfoundry/diego-release/pull/1216 +[cli]: https://github.com/cloudfoundry/cli/pull/3875 +[uaa]: https://github.com/cloudfoundry/uaa/pull/4076 From eb255d7e9df9ad900efc8305336f578f93da7bf0 Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 11:49:27 +0200 Subject: [PATCH 02/21] Link service-account RFC to its draft pull request --- toc/rfc/rfc-draft-service-accounts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toc/rfc/rfc-draft-service-accounts.md b/toc/rfc/rfc-draft-service-accounts.md index b63267610..affd6b66a 100644 --- a/toc/rfc/rfc-draft-service-accounts.md +++ b/toc/rfc/rfc-draft-service-accounts.md @@ -4,7 +4,7 @@ - Start Date: 2026-10-06 - Author(s): @rkoster - Status: Draft -- RFC Pull Request: To be added after submission +- RFC Pull Request: [community#1645](https://github.com/cloudfoundry/community/pull/1645) - Related RFCs: [RFC 0055: Identity-Aware Routing for GoRouter](rfc-0055-identity-aware-routing-for-gorouter.md) - Affected Component(s): Cloud Controller, BBS, Diego, UAA, CF CLI; subsequently GoRouter and service brokers From d468a2265f0f896eddf1b60c4aa7f2f5bc61f3f8 Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 14:09:15 +0200 Subject: [PATCH 03/21] Make service-account RFC self-contained and align creation permissions --- toc/rfc/rfc-draft-service-accounts.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/toc/rfc/rfc-draft-service-accounts.md b/toc/rfc/rfc-draft-service-accounts.md index affd6b66a..e1165addc 100644 --- a/toc/rfc/rfc-draft-service-accounts.md +++ b/toc/rfc/rfc-draft-service-accounts.md @@ -22,10 +22,8 @@ cf bind-service-account payments-api payments-worker cf bind-service-account payments-jobs payments-worker ``` -This condenses the [original proposal][source] and deliberately revises its -org-owned model to **space ownership and same-space assignment**. Implementation -drafts provide a tested starting point, not a prerequisite for accepting their -exact API or configuration details. +Implementation drafts provide a tested starting point, not a prerequisite for +accepting their exact API or configuration details. ## Problem @@ -47,8 +45,11 @@ provides stable workload identity while preserving instance-level attribution. reserves the name, preventing another owner from inheriting external grants. - Each app has zero or one account; multiple apps in the owning space may share it. Cross-space assignment is excluded, including within the same organization. -- The initial permission model uses space managers/platform admins for account - management and app writers for assignment in a writable owning space. +- Account creation follows service-instance creation permissions: Space Developers + and platform admins may create accounts, subject to readable/writable-space + checks and operator controls. Space Manager alone does not confer creation rights. + Other account lifecycle operations initially require space managers/platform + admins; assignment requires app-write permission in the writable owning space. - Creating or binding an account grants no resource permissions. CAPI roles, route rules and broker privileges are explicit and shared by all apps using it. Anyone able to deploy code to those apps can exercise those permissions. @@ -153,7 +154,8 @@ The lab demonstrated two apps sharing a SAN with distinct keys, runtime-task inheritance, roleless tokens seeing zero apps, explicit roles enabling access, disable/enable, and unbind/restart/rebind through the native CLI. -**Remaining acceptance requirements:** current [UAA mTLS work][uaa] emits `cnf`; +**Remaining acceptance requirements:** the prototype's manager-only creation check +must align with service-instance permissions. Current [UAA mTLS work][uaa] emits `cnf`; bearer-policy handling, leaf-expiry caps, managed-namespace enforcement and caller claims require completion. BBS module publication and automatic rollout capability signaling remain open. Timed live renewal, staging certificate inspection, Windows @@ -161,7 +163,6 @@ execution and the full negative/rotation matrix need further evidence. Lab CAPI calls used internal HTTP; production bearer use requires TLS. The POC therefore does not yet meet the complete proposed profile. -[source]: https://gist.github.com/rkoster/ee2ae127944943707c44f8f6f8b3f83d [mtls]: https://www.rfc-editor.org/rfc/rfc8705.html#section-2.1 [binding]: https://www.rfc-editor.org/rfc/rfc8705.html#section-3.4 [capi]: https://github.com/cloudfoundry/cloud_controller_ng/pull/5520 From bddccad58b2a79f696b4dab93064eed6126121f6 Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 14:16:00 +0200 Subject: [PATCH 04/21] Propose service account limits in space quotas --- toc/rfc/rfc-draft-service-accounts.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/toc/rfc/rfc-draft-service-accounts.md b/toc/rfc/rfc-draft-service-accounts.md index e1165addc..68a73ab92 100644 --- a/toc/rfc/rfc-draft-service-accounts.md +++ b/toc/rfc/rfc-draft-service-accounts.md @@ -81,8 +81,17 @@ membership prerequisites apply to the managed principal. Accounts with no assigned apps retain their identity and roles until explicitly disabled or deleted. Deletion requires removal of active workload references; -future route/broker references must also be resolved before teardown. Namespace -quotas and audit events must cover account creation, grants and assignment changes. +future route/broker references must also be resolved before teardown. Audit events +cover account creation, grants and assignment changes. + +Extend space quotas with a **maximum number of service accounts** (proposed V3 +field: `service_accounts.total_service_accounts`). Count every existing account +owned by the space, including disabled and unprovisioned accounts; app bindings +do not consume additional quota. Enforce the limit atomically during creation so +concurrent requests cannot exceed it. Deletion frees quota capacity, but its name +tombstone remains and does not count toward this resource limit. Lowering a quota +below current usage blocks further creation without deleting existing accounts. +Defaults and unlimited behavior should follow existing space-quota conventions. ### Credentials and token profile From 56880752bf65bc4c43c2647d35e5ca27a407bef8 Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 14:22:54 +0200 Subject: [PATCH 05/21] Propose admin-only recovery of tombstoned service account names --- toc/rfc/rfc-draft-service-accounts.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/toc/rfc/rfc-draft-service-accounts.md b/toc/rfc/rfc-draft-service-accounts.md index 68a73ab92..aa818c2ef 100644 --- a/toc/rfc/rfc-draft-service-accounts.md +++ b/toc/rfc/rfc-draft-service-accounts.md @@ -41,8 +41,9 @@ provides stable workload identity while preserving instance-level attribution. ### Identity and authorization boundary - An account has an immutable UUID, name and owning space. Names are - foundation-unique, lowercase DNS labels of 3–63 characters. Deletion permanently - reserves the name, preventing another owner from inheriting external grants. + foundation-unique, lowercase DNS labels of 3–63 characters. Deletion retains a + name tombstone; ordinary creation cannot reuse it. Only a platform admin may + explicitly override that reservation as described below. - Each app has zero or one account; multiple apps in the owning space may share it. Cross-space assignment is excluded, including within the same organization. - Account creation follows service-instance creation permissions: Space Developers @@ -79,6 +80,19 @@ bind/unbind and enable/disable commands, waits for jobs, and reports identity, provisioning state and restart guidance. Existing `/v3/roles` semantics and org membership prerequisites apply to the managed principal. +For recovery from accidental deletion, propose +`cf create-service-account NAME --reuse-name`. CAPI must authorize this explicit +tombstone override server-side for platform admins only and atomically prevent +conflicts with live accounts or concurrent creation. It creates a new account UUID +in the selected space, without restoring deleted bindings or CAPI roles; normal +creation checks and quotas still apply. Audit records retain the reservation's +history and identify the admin and replacement account. + +The CLI must warn that reuse restores the same SAN and `(issuer, subject)`: +external grants may authorize the replacement, and still-valid old certificates +may authenticate once its client is provisioned. This is an intentional admin +escape hatch, not revocation or isolation from the former identity. + Accounts with no assigned apps retain their identity and roles until explicitly disabled or deleted. Deletion requires removal of active workload references; future route/broker references must also be resolved before teardown. Audit events From e53872b667ef99ab9850b22f20571f2239017147 Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 14:30:40 +0200 Subject: [PATCH 06/21] Present service-account RFC through developer workflows and diagrams --- toc/rfc/rfc-draft-service-accounts.md | 351 ++++++++++++++------------ 1 file changed, 184 insertions(+), 167 deletions(-) diff --git a/toc/rfc/rfc-draft-service-accounts.md b/toc/rfc/rfc-draft-service-accounts.md index aa818c2ef..061cf6b45 100644 --- a/toc/rfc/rfc-draft-service-accounts.md +++ b/toc/rfc/rfc-draft-service-accounts.md @@ -6,188 +6,205 @@ - Status: Draft - RFC Pull Request: [community#1645](https://github.com/cloudfoundry/community/pull/1645) - Related RFCs: [RFC 0055: Identity-Aware Routing for GoRouter](rfc-0055-identity-aware-routing-for-gorouter.md) -- Affected Component(s): Cloud Controller, BBS, Diego, UAA, CF CLI; subsequently GoRouter and service brokers +- Affected Component(s): Cloud Controller, BBS, Diego, UAA, CF CLI; later GoRouter and service brokers ## Summary -Introduce space-owned service accounts: stable identities that selected apps share -without sharing a private key or distributing client secrets. Cloud Controller -manages the account and its OAuth client; Diego adds its identity to each app's -instance certificate. Apps authenticate to UAA with that certificate and obtain -short-lived bearer JWTs for explicitly authorized resources. +Give applications a stable identity they can share, without distributing a shared +secret. Developers assign a **service account**; Cloud Foundry manages its +credentials. Apps exchange their instance certificates for short-lived tokens and +use those tokens to access resources such as the Cloud Foundry API. + +**One account, multiple apps, independent keys, explicit permissions.** + +## Problem + +A payments team runs an API and a background worker. Both need the same access to +Cloud Foundry resources. Today, the team can distribute an OAuth client secret to +both apps—but then it must store, protect and rotate that secret. Scaling and +blue/green deployments add more places where the credential lives. + +Cloud Foundry already gives every instance its own certificate. However, instance +identities change on replacement, and app identities do not describe an intentional +group of apps. The team needs a durable **payments identity**, independent of which +instances or blue/green apps currently implement it. + +Service accounts connect existing instance credentials to that shared identity. +The platform handles keys; the team decides which apps use the identity and what +it may access. + +## Proposal + +### 1. Create once, assign to apps + +The account belongs to the targeted space. Each app can use one account, and +multiple apps in that space can share it. Cross-space assignment is excluded. ```sh +cf target -o acme -s payments +cf push payments-api --no-start +cf push payments-jobs --no-start + cf create-service-account payments-worker cf bind-service-account payments-api payments-worker cf bind-service-account payments-jobs payments-worker + +cf start payments-api +cf start payments-jobs +cf service-account payments-worker ``` -Implementation drafts provide a tested starting point, not a prerequisite for -accepting their exact API or configuration details. +```mermaid +flowchart LR + CLI["Developer: create and bind"] --> CAPI["CAPI: account and assignments"] + CAPI -->|"first bind: managed OAuth client"| UAA["UAA"] + CAPI -->|"typed account identity"| Diego["BBS / Diego"] + subgraph Space["Space: payments"] + API["payments-api
instance key A"] + Jobs["payments-jobs
instance key B"] + end + Diego -->|"issue certificate"| API + Diego -->|"issue certificate"| Jobs + API -.-> SAN["Shared DNS SAN:
payments-worker.svc.identity"] + Jobs -.-> SAN +``` -## Problem +First bind provisions one secretless UAA client and a roleless CAPI principal; +subsequent binds reuse them. The CLI waits for provisioning jobs, including retries. +No account certificate is issued before authorized provisioning is ready. -Instance GUIDs and app GUIDs identify individual workloads, but are unsuitable as -a durable identity shared across an API, background workers, replacements and -blue/green deployments. Applications commonly compensate with stored client -secrets or externally managed credentials. +### 2. Grant permissions explicitly -Cloud Foundry already issues per-instance certificates and authorizes OAuth client -principals. Connecting these mechanisms through an explicit account lifecycle -provides stable workload identity while preserving instance-level attribution. +Binding answers **“Who am I?”**, not **“What may I do?”** An authorized role manager +can grant the shared account access using existing role commands: -## Proposal +```sh +cf set-space-role cf:service-account:payments-worker acme payments SpaceAuditor --client +``` + +The CLI establishes required org membership before granting the space role. Both +apps can now read those resources. Without roles, their tokens grant no resource +access. Anyone who can deploy code to either app can exercise the shared grants. + +Creating accounts follows service-instance creation permissions: **Space Developer +or platform admin**, with readable/writable-space checks and operator controls. +Other lifecycle management initially requires space manager/admin; assignment +requires app-write permission. Ownership alone grants no account resource roles. + +### 3. From app certificate to API access + +The app discovers its client ID and token endpoint through non-secret +`VCAP_SERVICE_ACCOUNT` metadata. Its library reads `CF_INSTANCE_CERT` and +`CF_INSTANCE_KEY`, reloading them when acquiring tokens after credential rotation. + +```mermaid +sequenceDiagram + participant App as payments-api + participant UAA + participant CAPI as Cloud Foundry API + App->>UAA: HTTPS POST /oauth/mtls/token + instance certificate + Note over App,UAA: grant_type=client_credentials
client_id=cf:service-account:payments-worker + UAA->>UAA: Validate chain, key possession and exact account SAN + UAA-->>App: Signed, short-lived bearer JWT + App->>CAPI: HTTPS request with Authorization: Bearer JWT + CAPI->>CAPI: Validate JWT and check account roles + CAPI-->>App: Authorized resources, or access denied +``` + +Illustrative **decoded access-token payload** for the proposed CAPI profile: + +```json +{ + "iss": "https://uaa.example.org/oauth/token", + "sub": "cf:service-account:payments-worker", + "client_id": "cf:service-account:payments-worker", + "aud": ["cloud_controller"], + "scope": ["cloud_controller.read"], + "iat": 1791288000, + "exp": 1791288300 +} +``` + +Both apps have the same subject, but authenticate with different keys. The token +is limited to authorized audiences/scopes and expires within five minutes **and +no later than the certificate used to obtain it**. Scope permits API use; CAPI +roles determine which resources are accessible. Caller app/instance attribution +should also be retained in platform-controlled claims; claim names remain to be +agreed. + +There is deliberately **no `cnf`**: [RFC 8705][mtls] separates mTLS client +authentication from certificate-bound tokens. UAA must honor the client policy +`tls_client_certificate_bound_access_tokens: false`. The certificate is needed +to obtain the token, not to present it to CAPI. Token issuance needs no live CAPI +assignment lookup; consumers validate issuer, signature, audience, expiry and +permissions independently. + +### 4. Manage the account's lifecycle + +| Intent | Command | Effect | +| --- | --- | --- | +| Inspect | `cf service-accounts` | List accounts in the targeted space | +| Remove assignment | `cf unbind-service-account payments-api` | Changes desired identity; restart the app to apply | +| Stop new tokens | `cf disable-service-account payments-worker` | Disables issuance for all apps sharing it | +| Resume | `cf enable-service-account payments-worker` | Re-enables issuance, retaining roles | +| Delete | `cf delete-service-account payments-worker` | Requires workload references removed; retains a name tombstone | +| Recover a deleted name | `cf create-service-account payments-worker --reuse-name` | Explicit, audited platform-admin override | + +Running instances retain their launch identity—including renewal—until restart; +new tasks use the desired assignment. Staging never receives the account identity. +Unbind before assigning a different account. Zero-app accounts retain their client +and roles until explicitly disabled/deleted. + +**Unbind, restart and disable do not revoke existing tokens or certificates.** +Copied credentials can remain usable until expiry while the client is enabled; +without restart, a running old assignment can keep renewing. + +Names are immutable, foundation-unique lowercase DNS labels (3–63 characters). +The SAN suffix creates no DNS record or route; external identity is the trusted +`(issuer, subject)` pair. Admin `--reuse-name` creates a new UUID without restoring +bindings/roles, preserves audit history and cannot bypass live-name conflicts or +quotas. Its warning explains that external grants and valid old certificates may +still apply to the reused identity. + +### 5. Extend space quotas + +Add a maximum account count to space quotas, with this proposed V3 fragment: + +```json +{ + "service_accounts": { + "total_service_accounts": 10 + } +} +``` -### Identity and authorization boundary - -- An account has an immutable UUID, name and owning space. Names are - foundation-unique, lowercase DNS labels of 3–63 characters. Deletion retains a - name tombstone; ordinary creation cannot reuse it. Only a platform admin may - explicitly override that reservation as described below. -- Each app has zero or one account; multiple apps in the owning space may share - it. Cross-space assignment is excluded, including within the same organization. -- Account creation follows service-instance creation permissions: Space Developers - and platform admins may create accounts, subject to readable/writable-space - checks and operator controls. Space Manager alone does not confer creation rights. - Other account lifecycle operations initially require space managers/platform - admins; assignment requires app-write permission in the writable owning space. -- Creating or binding an account grants no resource permissions. CAPI roles, - route rules and broker privileges are explicit and shared by all apps using it. - Anyone able to deploy code to those apps can exercise those permissions. - -| Identity | Example | -| --- | --- | -| Name | `payments-worker` | -| OAuth client ID / JWT subject | `cf:service-account:payments-worker` | -| Certificate DNS SAN | `payments-worker.svc.identity` | -| External identity | Trusted `(issuer, subject)` pair | - -The SAN suffix is identity-only: it creates no route or DNS record and is never -resolved to authenticate a caller. Separate foundations may reuse names; their -issuers and CA trust domains must remain distinct. - -### Control plane and developer experience - -Creation reserves the account. First authorized bind asynchronously provisions -one UAA client and roleless CAPI OAuth principal; further binds reuse them. Jobs -must survive retries/concurrency without adopting an unmanaged client-ID collision -or issuing credentials before provisioning is ready. - -CAPI exposes `/v3/service_accounts`, account app listing, and the app's -`/relationships/service_account` relationship. Resource relationships use UUIDs; -names are human-facing lookup keys. The CLI provides create/list/show/delete, -bind/unbind and enable/disable commands, waits for jobs, and reports identity, -provisioning state and restart guidance. Existing `/v3/roles` semantics and org -membership prerequisites apply to the managed principal. - -For recovery from accidental deletion, propose -`cf create-service-account NAME --reuse-name`. CAPI must authorize this explicit -tombstone override server-side for platform admins only and atomically prevent -conflicts with live accounts or concurrent creation. It creates a new account UUID -in the selected space, without restoring deleted bindings or CAPI roles; normal -creation checks and quotas still apply. Audit records retain the reservation's -history and identify the admin and replacement account. - -The CLI must warn that reuse restores the same SAN and `(issuer, subject)`: -external grants may authorize the replacement, and still-valid old certificates -may authenticate once its client is provisioned. This is an intentional admin -escape hatch, not revocation or isolation from the former identity. - -Accounts with no assigned apps retain their identity and roles until explicitly -disabled or deleted. Deletion requires removal of active workload references; -future route/broker references must also be resolved before teardown. Audit events -cover account creation, grants and assignment changes. - -Extend space quotas with a **maximum number of service accounts** (proposed V3 -field: `service_accounts.total_service_accounts`). Count every existing account -owned by the space, including disabled and unprovisioned accounts; app bindings -do not consume additional quota. Enforce the limit atomically during creation so -concurrent requests cannot exceed it. Deletion frees quota capacity, but its name -tombstone remains and does not count toward this resource limit. Lowering a quota -below current usage blocks further creation without deleting existing accounts. -Defaults and unlimited behavior should follow existing space-quota conventions. - -### Credentials and token profile - -CAPI supplies a platform-owned account name through typed BBS certificate -properties. Diego derives exactly one account SAN in instance and C2C credentials, -preserving existing GUID/CN/IP, app/space/org OUs, route SANs and certificate renewal. -Every instance keeps its own key. Runtime tasks inherit their launch assignment; -staging receives no account identity. Other SAN-producing inputs cannot inject the -reserved suffix; malformed or ambiguous account identities fail closed. - -UAA uses [RFC 8705 PKI client authentication][mtls] with a validated chain and -exactly one registered DNS SAN binding. Managed clients are secretless, -`client_credentials`-only, provisioned in the default UAA zone. Operators must -protect `cf:service-account:` across client-management paths and zones; ordinary -client administrators cannot create, alter or take over managed registrations. - -Mutual-TLS client authentication is independent of certificate-bound access -tokens. Set [`tls_client_certificate_bound_access_tokens: false`][binding] on the -managed client: tokens are bearer JWTs **without `cnf`**, usable without presenting -the instance certificate to CAPI. Require trusted issuer, stable account subject, -authorized audience/scopes and expiry no later than five minutes or the issuing -leaf certificate's expiry. Platform-controlled caller claims should retain -app/space/org/instance attribution and must not be overridden by app templates. - -Non-secret `VCAP_SERVICE_ACCOUNT` metadata supplies the client ID and token -endpoint. Libraries reread rotated instance credential files when acquiring -tokens. UAA authenticates against its managed registration without a live CAPI -assignment lookup. Resource servers independently validate tokens and permissions. -The first delivery uses a fixed CAPI audience; additional targets require explicit -audience/scope policy rather than universal multi-audience tokens. - -### Lifecycle and rollout - -Bind/unbind changes desired configuration; running containers retain their -launch-time identity, including renewal, until replaced. **Restart is required**; -restaging solely for identity changes is unnecessary. New tasks use the desired -assignment. Unbind must precede assigning a different account. - -Restart does not revoke copied credentials. While the shared client is enabled, -an old certificate/key can obtain tokens until certificate expiry; each token is -also capped by that expiry. Without restart, an old running assignment can keep -renewing. Account disable stops new tokens when effective, not existing JWTs or -certificate-only access. Immediate per-app cryptographic revocation is out of scope. - -Features are operator-enabled only after compatible CAPI, BBS, cells and UAA are -deployed. Unsupported identity features must fail closed; silently dropping SANs -is not acceptable. Capability discovery and rollback must account for all cells -and consumers before enabling new assignments. - -### Delivery and remaining decisions - -1. Deliver the account lifecycle, native CLI, Diego identity, UAA bearer profile - and explicit CAPI roles. -2. Add curated token targets/external federation and account sources for RFC 0055 - route policies. Route matching requires verified SAN-bearing certificate data - and preserves caller org/space domain restrictions and default-deny behavior. -3. Negotiate broker/driver support for identity-based service bindings separately; - this RFC does not standardize new OSB fields or change legacy bindings. - -Maintainers should confirm ownership/permission defaults, name retention and -quotas, namespace protection, token/caller claim policy, API naming and mixed-version -capability signaling before finalizing the contract. - -### Implementation evidence - -Drafts: [CAPI][capi], [capi-release][release], [BBS contract][bbs], [Diego][diego], -[CLI][cli]. They preserve committed RED/GREEN tests and include verification notes. -The lab demonstrated two apps sharing a SAN with distinct keys, runtime-task -inheritance, roleless tokens seeing zero apps, explicit roles enabling access, -disable/enable, and unbind/restart/rebind through the native CLI. - -**Remaining acceptance requirements:** the prototype's manager-only creation check -must align with service-instance permissions. Current [UAA mTLS work][uaa] emits `cnf`; -bearer-policy handling, leaf-expiry caps, managed-namespace enforcement and caller -claims require completion. BBS module publication and automatic rollout capability -signaling remain open. Timed live renewal, staging certificate inspection, Windows -execution and the full negative/rotation matrix need further evidence. Lab CAPI -calls used internal HTTP; production bearer use requires TLS. The POC therefore -does not yet meet the complete proposed profile. - -[mtls]: https://www.rfc-editor.org/rfc/rfc8705.html#section-2.1 -[binding]: https://www.rfc-editor.org/rfc/rfc8705.html#section-3.4 +Count existing accounts, including disabled/unprovisioned ones, not bindings or +tombstones. Creation checks the limit atomically; deletion frees capacity. Lowering +the limit blocks further creation rather than deleting accounts. Default/unlimited +behavior follows existing space-quota conventions. + +### Delivery + +Start with CAPI account APIs/roles, native CLI, typed BBS/Diego identity and UAA +bearer issuance. Preserve existing instance fields, C2C route SANs and independent +keys. Protect the SAN namespace and managed client prefix from injection/takeover; +provision clients only in UAA's default zone. Unsupported identity features must +fail closed, with operator enablement only after compatible rollout. + +Later stages add curated external token targets/federation, account sources for +RFC 0055 route policies, and negotiated broker/driver integration. Route grants +must preserve verified-certificate checks, caller org/space restrictions and +default deny. Existing OSB bindings are unchanged. + +**Progress:** [CAPI][capi], [release wiring][release], [BBS][bbs], [Diego][diego] and +[CLI][cli] drafts demonstrate the two-app flow, explicit roles, disable/enable and +unbind/restart. Remaining work includes creation permissions, quotas/name reuse, +[UAA][uaa] bearer policy (the POC still emits `cnf`), leaf-expiry caps, namespace +protection, caller claims, module publication and rollout capability signaling. +Further renewal/staging/Windows/negative coverage is needed; lab CAPI HTTP access +must become HTTPS. Implementation details and test evidence live in those PRs. + +[mtls]: https://www.rfc-editor.org/rfc/rfc8705.html#section-3.4 [capi]: https://github.com/cloudfoundry/cloud_controller_ng/pull/5520 [release]: https://github.com/cloudfoundry/capi-release/pull/702 [bbs]: https://github.com/cloudfoundry/bbs/pull/168 From cd0ec877ea089575b03ce69cdc164e1132d664e9 Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 15:02:11 +0200 Subject: [PATCH 07/21] Lead service-account RFC with federation and broker use cases --- toc/rfc/rfc-draft-service-accounts.md | 117 ++++++++++++++++++-------- 1 file changed, 81 insertions(+), 36 deletions(-) diff --git a/toc/rfc/rfc-draft-service-accounts.md b/toc/rfc/rfc-draft-service-accounts.md index 061cf6b45..bd0c9425d 100644 --- a/toc/rfc/rfc-draft-service-accounts.md +++ b/toc/rfc/rfc-draft-service-accounts.md @@ -6,32 +6,39 @@ - Status: Draft - RFC Pull Request: [community#1645](https://github.com/cloudfoundry/community/pull/1645) - Related RFCs: [RFC 0055: Identity-Aware Routing for GoRouter](rfc-0055-identity-aware-routing-for-gorouter.md) -- Affected Component(s): Cloud Controller, BBS, Diego, UAA, CF CLI; later GoRouter and service brokers +- Affected Component(s): Cloud Controller, BBS, Diego, UAA, CF CLI, service brokers; later GoRouter ## Summary Give applications a stable identity they can share, without distributing a shared secret. Developers assign a **service account**; Cloud Foundry manages its -credentials. Apps exchange their instance certificates for short-lived tokens and -use those tokens to access resources such as the Cloud Foundry API. +credentials. Apps obtain short-lived JWTs for **workload identity federation +(WIF)** to off-platform services, especially services managed by service brokers. +The same identity can also authorize emerging workloads such as coding agents +that push applications through the Cloud Foundry API. **One account, multiple apps, independent keys, explicit permissions.** ## Problem -A payments team runs an API and a background worker. Both need the same access to -Cloud Foundry resources. Today, the team can distribute an OAuth client secret to -both apps—but then it must store, protect and rotate that secret. Scaling and -blue/green deployments add more places where the credential lives. +A payments API and background worker need an off-platform database, object store +or cloud API. Its identity provider supports WIF: exchange a JWT from a trusted +issuer for short-lived service credentials. The missing piece is a stable CF +workload identity that the provider can trust, without distributing another secret. + +**Broker-managed services are a natural fit.** A broker already provisions the +service and its access. An identity-aware binding could configure trust and grants +for the app's service account, returning federation/connection metadata instead +of a long-lived password. Developers would retain the familiar service-binding UX. Cloud Foundry already gives every instance its own certificate. However, instance identities change on replacement, and app identities do not describe an intentional group of apps. The team needs a durable **payments identity**, independent of which instances or blue/green apps currently implement it. -Service accounts connect existing instance credentials to that shared identity. -The platform handles keys; the team decides which apps use the identity and what -it may access. +**Next, coding agents running as CF apps** may need to push the applications they +generate. Explicit CAPI roles can give an agent that ability in a chosen space. +This is an emerging use case; off-platform federation is the primary motivation. ## Proposal @@ -73,25 +80,34 @@ First bind provisions one secretless UAA client and a roleless CAPI principal; subsequent binds reuse them. The CLI waits for provisioning jobs, including retries. No account certificate is issued before authorized provisioning is ready. -### 2. Grant permissions explicitly +### 2. Bind a broker-managed service using federation -Binding answers **“Who am I?”**, not **“What may I do?”** An authorized role manager -can grant the shared account access using existing role commands: +Binding an account answers **“Who am I?”**, not **“What may I do?”** For a service +whose broker supports identity federation, the proposed UX is: ```sh -cf set-space-role cf:service-account:payments-worker acme payments SpaceAuditor --client +# Proposed option; requires broker and client-library support +cf bind-service payments-api payments-store --authentication service-account +cf bind-service payments-jobs payments-store --authentication service-account ``` -The CLI establishes required org membership before granting the space role. Both -apps can now read those resources. Without roles, their tokens grant no resource -access. Anyone who can deploy code to either app can exercise the shared grants. +CAPI conveys the platform-owned account identity to the broker, which configures +the service's federation grants. The binding supplies the approved issuer, +audience and connection metadata; the app's library performs token acquisition +and exchange. Services may also be configured for federation outside a broker. + +This requires negotiated broker capability and audience/scope policy; no new OSB +fields are standardized here. Unsupported brokers reject the requested mode; +ordinary bindings retain their existing behavior. Grants are tracked per binding, +so removing one binding does not remove another's access. All apps sharing the +account can exercise its grants, as can anyone able to deploy code to those apps. Creating accounts follows service-instance creation permissions: **Space Developer or platform admin**, with readable/writable-space checks and operator controls. Other lifecycle management initially requires space manager/admin; assignment requires app-write permission. Ownership alone grants no account resource roles. -### 3. From app certificate to API access +### 3. From app certificate to off-platform access The app discovers its client ID and token endpoint through non-secret `VCAP_SERVICE_ACCOUNT` metadata. Its library reads `CF_INSTANCE_CERT` and @@ -101,25 +117,28 @@ The app discovers its client ID and token endpoint through non-secret sequenceDiagram participant App as payments-api participant UAA - participant CAPI as Cloud Foundry API + participant IdP as External federation provider + participant Service as Broker-managed service App->>UAA: HTTPS POST /oauth/mtls/token + instance certificate Note over App,UAA: grant_type=client_credentials
client_id=cf:service-account:payments-worker UAA->>UAA: Validate chain, key possession and exact account SAN - UAA-->>App: Signed, short-lived bearer JWT - App->>CAPI: HTTPS request with Authorization: Bearer JWT - CAPI->>CAPI: Validate JWT and check account roles - CAPI-->>App: Authorized resources, or access denied + UAA-->>App: Signed JWT for the approved federation audience + App->>IdP: HTTPS token exchange with JWT + IdP->>IdP: Verify issuer, signature, audience and subject grant + IdP-->>App: Short-lived service credentials + App->>Service: Request using exchanged credentials + Service-->>App: Authorized data, or access denied ``` -Illustrative **decoded access-token payload** for the proposed CAPI profile: +Illustrative **decoded UAA JWT** for an approved federation target (the provider's +required audience and exchange protocol must be configured and tested): ```json { "iss": "https://uaa.example.org/oauth/token", "sub": "cf:service-account:payments-worker", "client_id": "cf:service-account:payments-worker", - "aud": ["cloud_controller"], - "scope": ["cloud_controller.read"], + "aud": ["https://identity.example.org/federation/cf-payments"], "iat": 1791288000, "exp": 1791288300 } @@ -127,19 +146,42 @@ Illustrative **decoded access-token payload** for the proposed CAPI profile: Both apps have the same subject, but authenticate with different keys. The token is limited to authorized audiences/scopes and expires within five minutes **and -no later than the certificate used to obtain it**. Scope permits API use; CAPI -roles determine which resources are accessible. Caller app/instance attribution -should also be retained in platform-controlled claims; claim names remain to be -agreed. +no later than the certificate used to obtain it**. The external provider controls +the exchanged credentials' permissions and lifetime. Caller attribution should +also be retained in platform-controlled claims; claim names remain to be agreed. There is deliberately **no `cnf`**: [RFC 8705][mtls] separates mTLS client authentication from certificate-bound tokens. UAA must honor the client policy `tls_client_certificate_bound_access_tokens: false`. The certificate is needed -to obtain the token, not to present it to CAPI. Token issuance needs no live CAPI +to obtain the token, not to present it to the federation provider. Token issuance needs no live CAPI assignment lookup; consumers validate issuer, signature, audience, expiry and permissions independently. -### 4. Manage the account's lifecycle +### 4. Coding agents: the same identity, a CAPI target + +An agent app can instead request a CAPI-targeted token. An authorized role manager +grants its account `SpaceDeveloper` in the space where generated apps may be pushed: + +```sh +cf set-space-role cf:service-account:app-builder acme generated-apps SpaceDeveloper --client +``` + +```mermaid +sequenceDiagram + participant Agent as Coding agent app + participant UAA + participant CAPI as Cloud Foundry API + Agent->>UAA: mTLS token request for approved CAPI target + UAA-->>Agent: JWT with aud cloud_controller and CAPI scopes + Agent->>CAPI: Create and push generated app over HTTPS with bearer JWT + CAPI->>CAPI: Validate token and explicit SpaceDeveloper role + CAPI-->>Agent: Deployment result +``` + +This is a separate token target, not an all-purpose multi-audience token. CAPI +roles do not authorize external services, and federation grants confer no CAPI roles. + +### 5. Manage the account's lifecycle | Intent | Command | Effect | | --- | --- | --- | @@ -166,7 +208,7 @@ bindings/roles, preserves audit history and cannot bypass live-name conflicts or quotas. Its warning explains that external grants and valid old certificates may still apply to the reused identity. -### 5. Extend space quotas +### 6. Extend space quotas Add a maximum account count to space quotas, with this proposed V3 fragment: @@ -191,14 +233,17 @@ keys. Protect the SAN namespace and managed client prefix from injection/takeove provision clients only in UAA's default zone. Unsupported identity features must fail closed, with operator enablement only after compatible rollout. -Later stages add curated external token targets/federation, account sources for -RFC 0055 route policies, and negotiated broker/driver integration. Route grants +Build on that foundation with curated external token targets, provider-specific +WIF tests and negotiated broker/driver integration: these deliver the primary +user-facing goal. CAPI access is the initial validation path, not proof of WIF +compatibility. Later add account sources for RFC 0055 route policies. Route grants must preserve verified-certificate checks, caller org/space restrictions and default deny. Existing OSB bindings are unchanged. **Progress:** [CAPI][capi], [release wiring][release], [BBS][bbs], [Diego][diego] and [CLI][cli] drafts demonstrate the two-app flow, explicit roles, disable/enable and -unbind/restart. Remaining work includes creation permissions, quotas/name reuse, +unbind/restart against CAPI; external federation and broker integration are not +yet demonstrated. Remaining work includes creation permissions, quotas/name reuse, [UAA][uaa] bearer policy (the POC still emits `cnf`), leaf-expiry caps, namespace protection, caller claims, module publication and rollout capability signaling. Further renewal/staging/Windows/negative coverage is needed; lab CAPI HTTP access From 7fcc6b9916efc55b5e1209bac9d19b6a6084b527 Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 15:24:02 +0200 Subject: [PATCH 08/21] Clarify credential issuance and lead with external object storage --- toc/rfc/rfc-draft-service-accounts.md | 28 ++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/toc/rfc/rfc-draft-service-accounts.md b/toc/rfc/rfc-draft-service-accounts.md index bd0c9425d..271a4b4cc 100644 --- a/toc/rfc/rfc-draft-service-accounts.md +++ b/toc/rfc/rfc-draft-service-accounts.md @@ -11,20 +11,24 @@ ## Summary Give applications a stable identity they can share, without distributing a shared -secret. Developers assign a **service account**; Cloud Foundry manages its -credentials. Apps obtain short-lived JWTs for **workload identity federation -(WIF)** to off-platform services, especially services managed by service brokers. -The same identity can also authorize emerging workloads such as coding agents -that push applications through the Cloud Foundry API. +secret. Developers assign a **service account**. Cloud Foundry issues and rotates +each app instance's certificate, which the app uses to obtain short-lived JWTs +from UAA. External services can trust those JWTs directly or exchange them for +their own credentials. + +The primary use case is **workload identity federation (WIF)** to off-platform +services, especially services managed by service brokers. **One account, multiple apps, independent keys, explicit permissions.** ## Problem -A payments API and background worker need an off-platform database, object store -or cloud API. Its identity provider supports WIF: exchange a JWT from a trusted -issuer for short-lived service credentials. The missing piece is a stable CF -workload identity that the provider can trust, without distributing another secret. +A payments API stores uploaded invoices in a broker-managed cloud object store; +a background worker reads them for processing. Both need access to that external +service, without storing a long-lived access key in their service bindings. +The cloud provider supports WIF: exchange a JWT from a trusted issuer for +short-lived service credentials. The missing piece is a stable workload identity +that the provider can trust, without distributing another secret. **Broker-managed services are a natural fit.** A broker already provisions the service and its access. An identity-aware binding could configure trust and grants @@ -44,8 +48,10 @@ This is an emerging use case; off-platform federation is the primary motivation. ### 1. Create once, assign to apps -The account belongs to the targeted space. Each app can use one account, and -multiple apps in that space can share it. Cross-space assignment is excluded. +First, give the payments apps an identity that the object store's federation +provider can recognize across deployments. The account belongs to the targeted +space: each app can use one account, and multiple apps in that space can share +it. Cross-space assignment is excluded. Service access is granted in the next step. ```sh cf target -o acme -s payments From ccfe2e424b0989ebddbe6cd29d49eff3cf25ee8f Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 15:29:57 +0200 Subject: [PATCH 09/21] Describe OSBAPI and route-policy integration in service-account RFC --- toc/rfc/rfc-draft-service-accounts.md | 101 ++++++++++++++++++++++---- 1 file changed, 87 insertions(+), 14 deletions(-) diff --git a/toc/rfc/rfc-draft-service-accounts.md b/toc/rfc/rfc-draft-service-accounts.md index 271a4b4cc..3f8bfb951 100644 --- a/toc/rfc/rfc-draft-service-accounts.md +++ b/toc/rfc/rfc-draft-service-accounts.md @@ -98,15 +98,58 @@ cf bind-service payments-jobs payments-store --authentication service-account ``` CAPI conveys the platform-owned account identity to the broker, which configures -the service's federation grants. The binding supplies the approved issuer, -audience and connection metadata; the app's library performs token acquisition -and exchange. Services may also be configured for federation outside a broker. +the service's federation grants. Services may also be configured for federation +outside a broker. -This requires negotiated broker capability and audience/scope policy; no new OSB -fields are standardized here. Unsupported brokers reject the requested mode; -ordinary bindings retain their existing behavior. Grants are tracked per binding, -so removing one binding does not remove another's access. All apps sharing the -account can exercise its grants, as can anyone able to deploy code to those apps. +#### OSBAPI impact + +Propose a versioned service-plan capability, `service_account_binding`, and an +additive `bind_resource.service_account` object in the OSBAPI binding request. +These are **proposed extensions requiring OSBAPI agreement**, not existing fields. +An explicitly negotiated CF context extension can pilot the same semantics. + +Illustrative request fragment supplied by CAPI, never by app binding parameters: + +```json +{ + "bind_resource": { + "app_guid": "", + "service_account": { + "version": "1.0", + "id": "", + "issuer": "https://uaa.example.org/oauth/token", + "subject": "cf:service-account:payments-worker" + } + } +} +``` + +The broker accepts only configured issuer trust domains and returns non-secret +connection/federation metadata through the binding's `credentials` object: + +```json +{ + "credentials": { + "uri": "https://storage.example.org/payments", + "authentication": { + "type": "oauth2-bearer-jwt", + "issuer": "https://uaa.example.org/oauth/token", + "audience": "https://identity.example.org/federation/cf-payments" + } + } +} +``` + +CAPI approves and reconciles audience/scope targets before marking the binding +ready; libraries perform token acquisition and provider-specific exchange. +Unsupported brokers reject the requested mode; ordinary bindings stay unchanged. +Retries/async completion retain the binding's identity snapshot; failed setup or +cleanup remains visible and retryable. Grants and token targets are reference-counted +per binding so one unbind cannot remove another binding's access. + +Account reassignment requires removing identity-based service bindings first. +All apps sharing an account share its grants. The OSBAPI originating-identity header +continues to identify the operation's caller, not the assigned workload account. Creating accounts follows service-instance creation permissions: **Space Developer or platform admin**, with readable/writable-space checks and operator controls. @@ -187,7 +230,38 @@ sequenceDiagram This is a separate token target, not an all-purpose multi-audience token. CAPI roles do not authorize external services, and federation grants confer no CAPI roles. -### 5. Manage the account's lifecycle +### 5. Authorize app-to-app routes with the same identity + +Extend RFC 0055 route-policy sources with `cf:service-account:payments-worker`. +Proposed CLI syntax: + +```sh +cf add-route-policy apps.identity --hostname invoices --source-service-account payments-worker +``` + +```mermaid +flowchart LR + API["payments-api"] -->|"mTLS certificate"| Router["GoRouter"] + Jobs["payments-jobs"] -->|"mTLS certificate"| Router + Policy["Allow: cf:service-account:payments-worker"] -.-> Router + Router -->|"verified account SAN + domain scope match"| Backend["invoices.apps.identity"] + Other["Unbound app"] -->|"no account SAN: denied"| Router +``` + +CAPI resolves the account name to a UUID relationship and distributes a typed rule. +GoRouter matches exactly one canonical account DNS SAN from a verified certificate, +not a JWT or a caller-supplied header. Forwarded certificate data must carry the +verified SAN; XFCC `Hash`/`Subject` alone is insufficient. + +Preserve existing source OR semantics, `cf:any` exclusivity and default deny. +Domain org/space restrictions still apply using the **calling app's** OUs. Enable +account rules only when every enforcing router supports them; unknown rules must +fail closed. Referenced accounts cannot be deleted until route grants are removed. + +This path needs no UAA token or live CAPI lookup. Consequently, disabling token +issuance does not revoke route access: old certificates may match until expiry. + +### 6. Manage the account's lifecycle | Intent | Command | Effect | | --- | --- | --- | @@ -214,7 +288,7 @@ bindings/roles, preserves audit history and cannot bypass live-name conflicts or quotas. Its warning explains that external grants and valid old certificates may still apply to the reused identity. -### 6. Extend space quotas +### 7. Extend space quotas Add a maximum account count to space quotas, with this proposed V3 fragment: @@ -242,13 +316,12 @@ fail closed, with operator enablement only after compatible rollout. Build on that foundation with curated external token targets, provider-specific WIF tests and negotiated broker/driver integration: these deliver the primary user-facing goal. CAPI access is the initial validation path, not proof of WIF -compatibility. Later add account sources for RFC 0055 route policies. Route grants -must preserve verified-certificate checks, caller org/space restrictions and -default deny. Existing OSB bindings are unchanged. +compatibility. Deliver account-aware route policies as a separate integration; +existing OSB bindings and route source types remain supported. **Progress:** [CAPI][capi], [release wiring][release], [BBS][bbs], [Diego][diego] and [CLI][cli] drafts demonstrate the two-app flow, explicit roles, disable/enable and -unbind/restart against CAPI; external federation and broker integration are not +unbind/restart against CAPI; external federation, broker and account-route integration are not yet demonstrated. Remaining work includes creation permissions, quotas/name reuse, [UAA][uaa] bearer policy (the POC still emits `cnf`), leaf-expiry caps, namespace protection, caller claims, module publication and rollout capability signaling. From 165edb84e2298e17a2643a6aea3c502477850d96 Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 15:47:12 +0200 Subject: [PATCH 10/21] Use cf:svc prefix for service-account route-policy sources --- toc/rfc/rfc-draft-service-accounts.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/toc/rfc/rfc-draft-service-accounts.md b/toc/rfc/rfc-draft-service-accounts.md index 3f8bfb951..fbbe26cc5 100644 --- a/toc/rfc/rfc-draft-service-accounts.md +++ b/toc/rfc/rfc-draft-service-accounts.md @@ -232,8 +232,9 @@ roles do not authorize external services, and federation grants confer no CAPI r ### 5. Authorize app-to-app routes with the same identity -Extend RFC 0055 route-policy sources with `cf:service-account:payments-worker`. -Proposed CLI syntax: +Extend RFC 0055 route options with the source format +`cf:svc:`, for example `cf:svc:payments-worker`. +The proposed CLI resolves the following to that route-option value: ```sh cf add-route-policy apps.identity --hostname invoices --source-service-account payments-worker @@ -243,7 +244,7 @@ cf add-route-policy apps.identity --hostname invoices --source-service-account p flowchart LR API["payments-api"] -->|"mTLS certificate"| Router["GoRouter"] Jobs["payments-jobs"] -->|"mTLS certificate"| Router - Policy["Allow: cf:service-account:payments-worker"] -.-> Router + Policy["Allow: cf:svc:payments-worker"] -.-> Router Router -->|"verified account SAN + domain scope match"| Backend["invoices.apps.identity"] Other["Unbound app"] -->|"no account SAN: denied"| Router ``` From af452ded3883217fd6495888b9653ac044e23eb6 Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 15:55:23 +0200 Subject: [PATCH 11/21] Explain account route access in developer-facing terms --- toc/rfc/rfc-draft-service-accounts.md | 29 ++++++++++++++++----------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/toc/rfc/rfc-draft-service-accounts.md b/toc/rfc/rfc-draft-service-accounts.md index fbbe26cc5..443c9bfb5 100644 --- a/toc/rfc/rfc-draft-service-accounts.md +++ b/toc/rfc/rfc-draft-service-accounts.md @@ -249,18 +249,23 @@ flowchart LR Other["Unbound app"] -->|"no account SAN: denied"| Router ``` -CAPI resolves the account name to a UUID relationship and distributes a typed rule. -GoRouter matches exactly one canonical account DNS SAN from a verified certificate, -not a JWT or a caller-supplied header. Forwarded certificate data must carry the -verified SAN; XFCC `Hash`/`Subject` alone is insufficient. - -Preserve existing source OR semantics, `cf:any` exclusivity and default deny. -Domain org/space restrictions still apply using the **calling app's** OUs. Enable -account rules only when every enforcing router supports them; unknown rules must -fail closed. Referenced accounts cannot be deleted until route grants are removed. - -This path needs no UAA token or live CAPI lookup. Consequently, disabling token -issuance does not revoke route access: old certificates may match until expiry. +Both payments apps can now call the invoices route. GoRouter verifies the caller's +certificate and checks for `payments-worker.svc.identity`; an app without that +identity does not qualify for this grant. No JWT or token exchange is needed. + +Existing route policies keep working. An account grant does not bypass a domain's +org/space restriction: the **calling app** must still be in the allowed org or +space. Without a matching grant, access remains denied. + +CAPI records which account the policy refers to and sends the rule to GoRouter. +The rule can be enabled only after all routers support it. If TLS terminates at a +proxy, the router must receive trustworthy certificate data including the account +SAN; a caller-provided identity header is not proof of identity. + +**Disabling a service account stops new UAA tokens, not route access.** Route access +uses the certificate directly, so an already-issued certificate may still work +until expiry. Remove the route grant to withdraw that permission, and remove +account route grants before deleting the account. ### 6. Manage the account's lifecycle From 79837f040ffae88234ccbab8b7fa764533925297 Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 16:00:57 +0200 Subject: [PATCH 12/21] Remove routing implementation detail from service-account RFC --- toc/rfc/rfc-draft-service-accounts.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/toc/rfc/rfc-draft-service-accounts.md b/toc/rfc/rfc-draft-service-accounts.md index 443c9bfb5..655169042 100644 --- a/toc/rfc/rfc-draft-service-accounts.md +++ b/toc/rfc/rfc-draft-service-accounts.md @@ -257,11 +257,6 @@ Existing route policies keep working. An account grant does not bypass a domain' org/space restriction: the **calling app** must still be in the allowed org or space. Without a matching grant, access remains denied. -CAPI records which account the policy refers to and sends the rule to GoRouter. -The rule can be enabled only after all routers support it. If TLS terminates at a -proxy, the router must receive trustworthy certificate data including the account -SAN; a caller-provided identity header is not proof of identity. - **Disabling a service account stops new UAA tokens, not route access.** Route access uses the certificate directly, so an already-issued certificate may still work until expiry. Remove the route grant to withdraw that permission, and remove From 7a146cac2b19488fe63f8678d55241a60ab11523 Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 16:03:13 +0200 Subject: [PATCH 13/21] Distinguish agreed UAA authentication from remaining account profile work --- toc/rfc/rfc-draft-service-accounts.md | 41 ++++++++++++++------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/toc/rfc/rfc-draft-service-accounts.md b/toc/rfc/rfc-draft-service-accounts.md index 655169042..23a3df8de 100644 --- a/toc/rfc/rfc-draft-service-accounts.md +++ b/toc/rfc/rfc-draft-service-accounts.md @@ -308,26 +308,27 @@ behavior follows existing space-quota conventions. ### Delivery -Start with CAPI account APIs/roles, native CLI, typed BBS/Diego identity and UAA -bearer issuance. Preserve existing instance fields, C2C route SANs and independent -keys. Protect the SAN namespace and managed client prefix from injection/takeover; -provision clients only in UAA's default zone. Unsupported identity features must -fail closed, with operator enablement only after compatible rollout. - -Build on that foundation with curated external token targets, provider-specific -WIF tests and negotiated broker/driver integration: these deliver the primary -user-facing goal. CAPI access is the initial validation path, not proof of WIF -compatibility. Deliver account-aware route policies as a separate integration; -existing OSB bindings and route source types remain supported. - -**Progress:** [CAPI][capi], [release wiring][release], [BBS][bbs], [Diego][diego] and -[CLI][cli] drafts demonstrate the two-app flow, explicit roles, disable/enable and -unbind/restart against CAPI; external federation, broker and account-route integration are not -yet demonstrated. Remaining work includes creation permissions, quotas/name reuse, -[UAA][uaa] bearer policy (the POC still emits `cnf`), leaf-expiry caps, namespace -protection, caller claims, module publication and rollout capability signaling. -Further renewal/staging/Windows/negative coverage is needed; lab CAPI HTTP access -must become HTTPS. Implementation details and test evidence live in those PRs. +**The UAA authentication foundation is already implemented and agreed with the +UAA team, pending final review in [UAA #4076][uaa].** This covers certificate-based +client authentication and registered subject/SAN matching. This RFC builds on +that work; it does not propose starting UAA mTLS support from scratch. + +The remaining UAA work is the service-account profile: bearer issuance without +`cnf`, certificate-expiry token caps, protected managed-client registrations and +approved federation audiences/caller claims. These additions remain to be agreed +and implemented; they are not covered by the authentication team's agreement. + +[CAPI][capi], [release wiring][release], [BBS][bbs], [Diego][diego] and [CLI][cli] +drafts already demonstrate the shared two-app identity, explicit roles, +disable/enable and unbind/restart against CAPI. Finish creation permissions, +quotas/name reuse, contract publication and compatible rollout before release. + +Next, validate external federation targets and implement negotiated broker/driver +support to deliver the primary WIF use case. Account-aware route policies are a +separate integration. Neither has yet been demonstrated by the POC; existing OSB +bindings and route source types remain supported. Further renewal/staging/Windows +coverage and production HTTPS validation are also needed. Detailed implementation +and test evidence live in the linked PRs. [mtls]: https://www.rfc-editor.org/rfc/rfc8705.html#section-3.4 [capi]: https://github.com/cloudfoundry/cloud_controller_ng/pull/5520 From 05a59db52007d66c5be924f0ab927e84bd0af829 Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 16:04:23 +0200 Subject: [PATCH 14/21] Illustrate certificate SAN matching against account route policy --- toc/rfc/rfc-draft-service-accounts.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/toc/rfc/rfc-draft-service-accounts.md b/toc/rfc/rfc-draft-service-accounts.md index 23a3df8de..0bbd5fbbc 100644 --- a/toc/rfc/rfc-draft-service-accounts.md +++ b/toc/rfc/rfc-draft-service-accounts.md @@ -242,11 +242,18 @@ cf add-route-policy apps.identity --hostname invoices --source-service-account p ```mermaid flowchart LR - API["payments-api"] -->|"mTLS certificate"| Router["GoRouter"] - Jobs["payments-jobs"] -->|"mTLS certificate"| Router - Policy["Allow: cf:svc:payments-worker"] -.-> Router - Router -->|"verified account SAN + domain scope match"| Backend["invoices.apps.identity"] - Other["Unbound app"] -->|"no account SAN: denied"| Router + subgraph Cert["Verified caller certificate"] + Instance["CN / DNS SAN: instance GUID
OUs: app, space, org"] + SAN["Account DNS SAN:
payments-worker.svc.identity"] + end + subgraph Route["Policy for invoices.apps.identity"] + Rule["Source: cf:svc:payments-worker"] + Expected["Expected account DNS SAN:
payments-worker.svc.identity"] + Rule -->|"maps to"| Expected + end + SAN --> Match["Exact SAN match"] + Expected --> Match + Match --> Grant["Account rule satisfied
Domain restrictions still apply"] ``` Both payments apps can now call the invoices route. GoRouter verifies the caller's From 1d6707d264ff9b070581966ab123b8d3811afc70 Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 16:11:06 +0200 Subject: [PATCH 15/21] Remove distracting route-policy diagram from RFC --- toc/rfc/rfc-draft-service-accounts.md | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/toc/rfc/rfc-draft-service-accounts.md b/toc/rfc/rfc-draft-service-accounts.md index 0bbd5fbbc..f98edae4e 100644 --- a/toc/rfc/rfc-draft-service-accounts.md +++ b/toc/rfc/rfc-draft-service-accounts.md @@ -240,22 +240,6 @@ The proposed CLI resolves the following to that route-option value: cf add-route-policy apps.identity --hostname invoices --source-service-account payments-worker ``` -```mermaid -flowchart LR - subgraph Cert["Verified caller certificate"] - Instance["CN / DNS SAN: instance GUID
OUs: app, space, org"] - SAN["Account DNS SAN:
payments-worker.svc.identity"] - end - subgraph Route["Policy for invoices.apps.identity"] - Rule["Source: cf:svc:payments-worker"] - Expected["Expected account DNS SAN:
payments-worker.svc.identity"] - Rule -->|"maps to"| Expected - end - SAN --> Match["Exact SAN match"] - Expected --> Match - Match --> Grant["Account rule satisfied
Domain restrictions still apply"] -``` - Both payments apps can now call the invoices route. GoRouter verifies the caller's certificate and checks for `payments-worker.svc.identity`; an app without that identity does not qualify for this grant. No JWT or token exchange is needed. From 0cfb83dccec85bf91f22e8f6ed80fe184badfda7 Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 16:12:19 +0200 Subject: [PATCH 16/21] Keep UAA review follow-ups out of RFC delivery summary --- toc/rfc/rfc-draft-service-accounts.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/toc/rfc/rfc-draft-service-accounts.md b/toc/rfc/rfc-draft-service-accounts.md index f98edae4e..8e7b4854b 100644 --- a/toc/rfc/rfc-draft-service-accounts.md +++ b/toc/rfc/rfc-draft-service-accounts.md @@ -304,11 +304,6 @@ UAA team, pending final review in [UAA #4076][uaa].** This covers certificate-ba client authentication and registered subject/SAN matching. This RFC builds on that work; it does not propose starting UAA mTLS support from scratch. -The remaining UAA work is the service-account profile: bearer issuance without -`cnf`, certificate-expiry token caps, protected managed-client registrations and -approved federation audiences/caller claims. These additions remain to be agreed -and implemented; they are not covered by the authentication team's agreement. - [CAPI][capi], [release wiring][release], [BBS][bbs], [Diego][diego] and [CLI][cli] drafts already demonstrate the shared two-app identity, explicit roles, disable/enable and unbind/restart against CAPI. Finish creation permissions, From e73e3470d90eda5090b1a1e4c27ec4d07cf8b071 Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 16:13:20 +0200 Subject: [PATCH 17/21] Focus RFC next step on community review and approval --- toc/rfc/rfc-draft-service-accounts.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/toc/rfc/rfc-draft-service-accounts.md b/toc/rfc/rfc-draft-service-accounts.md index 8e7b4854b..97eb806df 100644 --- a/toc/rfc/rfc-draft-service-accounts.md +++ b/toc/rfc/rfc-draft-service-accounts.md @@ -309,12 +309,9 @@ drafts already demonstrate the shared two-app identity, explicit roles, disable/enable and unbind/restart against CAPI. Finish creation permissions, quotas/name reuse, contract publication and compatible rollout before release. -Next, validate external federation targets and implement negotiated broker/driver -support to deliver the primary WIF use case. Account-aware route policies are a -separate integration. Neither has yet been demonstrated by the POC; existing OSB -bindings and route source types remain supported. Further renewal/staging/Windows -coverage and production HTTPS validation are also needed. Detailed implementation -and test evidence live in the linked PRs. +The next step is community review and RFC approval of the proposed identity model +and developer experience. Detailed implementation and test evidence live in the +linked PRs. [mtls]: https://www.rfc-editor.org/rfc/rfc8705.html#section-3.4 [capi]: https://github.com/cloudfoundry/cloud_controller_ng/pull/5520 From cdf26200d6b91398c606422b460ad6803394654d Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 16:16:11 +0200 Subject: [PATCH 18/21] Explain account disable and enable lifecycle explicitly --- toc/rfc/rfc-draft-service-accounts.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/toc/rfc/rfc-draft-service-accounts.md b/toc/rfc/rfc-draft-service-accounts.md index 97eb806df..8dcde8eb7 100644 --- a/toc/rfc/rfc-draft-service-accounts.md +++ b/toc/rfc/rfc-draft-service-accounts.md @@ -264,6 +264,15 @@ account route grants before deleting the account. | Delete | `cf delete-service-account payments-worker` | Requires workload references removed; retains a name tombstone | | Recover a deleted name | `cf create-service-account payments-worker --reuse-name` | Explicit, audited platform-admin override | +**Disabling an account is a token-issuance switch.** +`cf disable-service-account payments-worker` disables its managed UAA client for +all apps sharing the account; the CLI waits until reconciliation completes. +The account, app assignments and CAPI roles remain, and apps keep running. +Already-issued JWTs remain valid until expiry, and certificates can still grant +route access. `cf enable-service-account payments-worker` restores token issuance +with the retained assignments and roles. In the POC, disabling deletes the UAA +client registration and enabling recreates it. + Running instances retain their launch identity—including renewal—until restart; new tasks use the desired assignment. Staging never receives the account identity. Unbind before assigning a different account. Zero-app accounts retain their client From 5c797fc9ff249ae8cc9c8503cb8c5ddf9808c978 Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 16:20:29 +0200 Subject: [PATCH 19/21] Reference RFC 1123 label syntax for account names --- toc/rfc/rfc-draft-service-accounts.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/toc/rfc/rfc-draft-service-accounts.md b/toc/rfc/rfc-draft-service-accounts.md index 8dcde8eb7..379652da6 100644 --- a/toc/rfc/rfc-draft-service-accounts.md +++ b/toc/rfc/rfc-draft-service-accounts.md @@ -282,7 +282,9 @@ and roles until explicitly disabled/deleted. Copied credentials can remain usable until expiry while the client is enabled; without restart, a running old assignment can keep renewing. -Names are immutable, foundation-unique lowercase DNS labels (3–63 characters). +Names are immutable and unique within a foundation. They follow +[RFC 1123 hostname-label syntax][names], restricted to lowercase ASCII and 3–63 +characters: letters, digits and hyphens, beginning and ending with a letter or digit. The SAN suffix creates no DNS record or route; external identity is the trusted `(issuer, subject)` pair. Admin `--reuse-name` creates a new UUID without restoring bindings/roles, preserves audit history and cannot bypass live-name conflicts or @@ -323,6 +325,7 @@ and developer experience. Detailed implementation and test evidence live in the linked PRs. [mtls]: https://www.rfc-editor.org/rfc/rfc8705.html#section-3.4 +[names]: https://www.rfc-editor.org/rfc/rfc1123.html#section-2.1 [capi]: https://github.com/cloudfoundry/cloud_controller_ng/pull/5520 [release]: https://github.com/cloudfoundry/capi-release/pull/702 [bbs]: https://github.com/cloudfoundry/bbs/pull/168 From 8d5aff39bd529932f2943f37e67da383ce80bc83 Mon Sep 17 00:00:00 2001 From: rkoster Date: Wed, 7 Oct 2026 09:14:54 +0200 Subject: [PATCH 20/21] Define configurable creation budget with platform admin exemption --- toc/rfc/rfc-draft-service-accounts.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/toc/rfc/rfc-draft-service-accounts.md b/toc/rfc/rfc-draft-service-accounts.md index 379652da6..7d6b40582 100644 --- a/toc/rfc/rfc-draft-service-accounts.md +++ b/toc/rfc/rfc-draft-service-accounts.md @@ -308,6 +308,17 @@ tombstones. Creation checks the limit atomically; deletion frees capacity. Lower the limit blocks further creation rather than deleting accounts. Default/unlimited behavior follows existing space-quota conventions. +Separately, operators configure a foundation-wide creation budget per authenticated +principal over a rolling seven-day window, with an explicit **unlimited** option. +This constrains name-reservation abuse on public platforms; internal platforms may +choose unlimited. The budget applies across all spaces to users and automation +clients, but **platform admins are exempt**, including automation authenticated with +platform-admin privileges. Each successful non-exempt creation consumes budget +atomically; failed requests do not. Deletion remains available and does not replenish +the budget. Admin-only `--reuse-name` remains subject to live-name and space-quota +checks, but is exempt from this creation budget. +This bounds per-principal creation rates, not total retained tombstones. + ### Delivery **The UAA authentication foundation is already implemented and agreed with the From a1647ffe20a12ddc03d60d0d8e0cc3570d2591de Mon Sep 17 00:00:00 2001 From: rkoster Date: Wed, 7 Oct 2026 09:19:45 +0200 Subject: [PATCH 21/21] Specify cascading service-account cleanup on space and org deletion --- toc/rfc/rfc-draft-service-accounts.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/toc/rfc/rfc-draft-service-accounts.md b/toc/rfc/rfc-draft-service-accounts.md index 7d6b40582..0b866acb8 100644 --- a/toc/rfc/rfc-draft-service-accounts.md +++ b/toc/rfc/rfc-draft-service-accounts.md @@ -264,6 +264,13 @@ account route grants before deleting the account. | Delete | `cf delete-service-account payments-worker` | Requires workload references removed; retains a name tombstone | | Recover a deleted name | `cf create-service-account payments-worker --reuse-name` | Explicit, audited platform-admin override | +**Space/org deletion cascades owned service accounts, as it does service instances.** +CAPI cleans up account-backed service bindings and route grants, deletes the +workloads and their account references, and removes managed UAA registrations and +account roles before deleting the accounts. Name tombstones remain. Pending or +failed cleanup prevents final space/org deletion and is visible through the +deletion job; users need not manually delete each account first. + **Disabling an account is a token-issuance switch.** `cf disable-service-account payments-worker` disables its managed UAA client for all apps sharing the account; the CLI waits until reconciliation completes. @@ -329,7 +336,8 @@ that work; it does not propose starting UAA mTLS support from scratch. [CAPI][capi], [release wiring][release], [BBS][bbs], [Diego][diego] and [CLI][cli] drafts already demonstrate the shared two-app identity, explicit roles, disable/enable and unbind/restart against CAPI. Finish creation permissions, -quotas/name reuse, contract publication and compatible rollout before release. +quotas/name reuse, cascading space/org cleanup, contract publication and compatible +rollout before release. The next step is community review and RFC approval of the proposed identity model and developer experience. Detailed implementation and test evidence live in the