From e7231e01dc69fb35cd209dee915e8cd8b385fa59 Mon Sep 17 00:00:00 2001 From: rkoster Date: Mon, 5 Oct 2026 20:41:59 +0200 Subject: [PATCH 01/13] Test service account worker configuration rendering (red) --- .../cloud_controller_worker_spec.rb | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/spec/cloud_controller_worker/cloud_controller_worker_spec.rb b/spec/cloud_controller_worker/cloud_controller_worker_spec.rb index 7927ea28ab..c9229e6ec5 100644 --- a/spec/cloud_controller_worker/cloud_controller_worker_spec.rb +++ b/spec/cloud_controller_worker/cloud_controller_worker_spec.rb @@ -128,6 +128,30 @@ module Test let(:template) { job.template('config/cloud_controller_ng.yml') } + it 'renders dedicated provisioning credentials and instance trust only when explicitly enabled' do + manifest_properties['cc']['service_accounts'] = { + 'provisioning_enabled' => true, + 'runtime_enabled' => false, + 'management_client_id' => 'account-manager', + 'management_client_secret' => 'manager-secret', + 'identity_ca' => "-----BEGIN CERTIFICATE-----\nidentity-ca\n-----END CERTIFICATE-----", + 'token_endpoint' => 'https://uaa.example.test/oauth/token/mtls' + } + result = YAML.safe_load(template.render(manifest_properties, consumes: links)) + expect(result['service_account_provisioning']).to eq( + 'client_id' => 'account-manager', 'client_secret' => 'manager-secret', + 'identity_ca' => manifest_properties['cc']['service_accounts']['identity_ca'] + ) + expect(result['service_account_runtime_enabled']).to be(false) + expect(result['service_account_token_endpoint']).to eq('https://uaa.example.test/oauth/token/mtls') + end + + it 'keeps service account runtime and provisioning disabled by default' do + result = YAML.safe_load(template.render(manifest_properties, consumes: links)) + expect(result['service_account_runtime_enabled']).to be(false) + expect(result).not_to have_key('service_account_provisioning') + end + it 'creates the cloud_controller_ng.yml config file' do expect do YAML.safe_load(template.render(manifest_properties, consumes: links)) From 84b18957aa445453a4d4e8735bb2d13db18e126a Mon Sep 17 00:00:00 2001 From: rkoster Date: Mon, 5 Oct 2026 20:42:48 +0200 Subject: [PATCH 02/13] Render dedicated service account worker trust and credentials --- jobs/cloud_controller_worker/spec | 19 ++++++++++++++++++- .../templates/cloud_controller_ng.yml.erb | 9 +++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/jobs/cloud_controller_worker/spec b/jobs/cloud_controller_worker/spec index 855d6743db..5771682bd7 100644 --- a/jobs/cloud_controller_worker/spec +++ b/jobs/cloud_controller_worker/spec @@ -68,6 +68,24 @@ consumes: optional: true properties: + cc.service_accounts.provisioning_enabled: + description: Enable dedicated managed service account client reconciliation in workers. + default: false + cc.service_accounts.runtime_enabled: + description: Enable typed account runtime identity only after all BBS/rep/cells support it. + default: false + cc.service_accounts.token_endpoint: + description: HTTPS UAA certificate-authenticated token endpoint exposed to bound workloads. + default: "" + cc.service_accounts.management_client_id: + description: Dedicated UAA client with managed account namespace client-management permissions. + default: "" + cc.service_accounts.management_client_secret: + description: Secret of the dedicated UAA account-management client; never exposed to workloads. + default: "" + cc.service_accounts.identity_ca: + description: Instance identity CA PEM trusted for exact account DNS SAN authentication. + default: "" ssl.skip_cert_verify: description: "specifies that the job is allowed to skip ssl cert verification" default: false @@ -599,4 +617,3 @@ properties: when applying manifests. If a service broker fails to complete a service binding request before the specified duration, the manifest job will fail." default: 60 - diff --git a/jobs/cloud_controller_worker/templates/cloud_controller_ng.yml.erb b/jobs/cloud_controller_worker/templates/cloud_controller_ng.yml.erb index 01f66b3467..ef32fdd55d 100644 --- a/jobs/cloud_controller_worker/templates/cloud_controller_ng.yml.erb +++ b/jobs/cloud_controller_worker/templates/cloud_controller_ng.yml.erb @@ -167,6 +167,15 @@ uaa: client_timeout: <%= cc_internal.p("cc.uaa.client_timeout")%> <% end %> +service_account_runtime_enabled: <%= p('cc.service_accounts.runtime_enabled') %> +service_account_token_endpoint: <%= yaml_escape(p('cc.service_accounts.token_endpoint')) %> +<% if p('cc.service_accounts.provisioning_enabled') %> +service_account_provisioning: + client_id: <%= yaml_escape(p('cc.service_accounts.management_client_id')) %> + client_secret: <%= yaml_escape(p('cc.service_accounts.management_client_secret')) %> + identity_ca: <%= p('cc.service_accounts.identity_ca').to_json %> +<% end %> + # App staging parameters staging: timeout_in_seconds: <%= p("cc.staging_timeout_in_seconds") %> From bd7d8831d6cba02871a55e06a5d9c6858267d237 Mon Sep 17 00:00:00 2001 From: rkoster Date: Mon, 5 Oct 2026 20:43:46 +0200 Subject: [PATCH 03/13] Test service account API gate and discovery rendering (red) --- spec/cloud_controller_ng/cloud_controller_ng_spec.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spec/cloud_controller_ng/cloud_controller_ng_spec.rb b/spec/cloud_controller_ng/cloud_controller_ng_spec.rb index b1d749ef75..9ee8975f4e 100644 --- a/spec/cloud_controller_ng/cloud_controller_ng_spec.rb +++ b/spec/cloud_controller_ng/cloud_controller_ng_spec.rb @@ -142,6 +142,18 @@ module Test describe 'config/cloud_controller_ng.yml' do let(:template) { job.template('config/cloud_controller_ng.yml') } + it 'renders explicit service account gates and discovery without exposing management credentials' do + merged_manifest_properties['cc']['service_accounts'] = { + 'provisioning_enabled' => true, 'runtime_enabled' => true, + 'token_endpoint' => 'https://uaa.example.test/oauth/token/mtls' + } + result = YAML.safe_load(template.render(merged_manifest_properties, consumes: links)) + expect(result['service_account_provisioning_enabled']).to be(true) + expect(result['service_account_runtime_enabled']).to be(true) + expect(result['service_account_token_endpoint']).to eq('https://uaa.example.test/oauth/token/mtls') + expect(result).not_to have_key('service_account_provisioning') + end + it 'creates the cloud_controller_ng.yml config file' do expect do YAML.safe_load(template.render(merged_manifest_properties, consumes: links)) From 333cda02c4ed5c603909c724e53443e138049f31 Mon Sep 17 00:00:00 2001 From: rkoster Date: Mon, 5 Oct 2026 20:44:26 +0200 Subject: [PATCH 04/13] Render default-off service account API and runtime gates --- jobs/cloud_controller_ng/spec | 9 +++++++++ .../templates/cloud_controller_ng.yml.erb | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/jobs/cloud_controller_ng/spec b/jobs/cloud_controller_ng/spec index ba7e1b0d25..85d3c4c4b0 100644 --- a/jobs/cloud_controller_ng/spec +++ b/jobs/cloud_controller_ng/spec @@ -243,6 +243,15 @@ consumes: optional: true properties: + cc.service_accounts.provisioning_enabled: + description: Enable asynchronous service account client reconciliation requests. + default: false + cc.service_accounts.runtime_enabled: + description: Enable typed account runtime identity only after all BBS/rep/cells support it. + default: false + cc.service_accounts.token_endpoint: + description: HTTPS UAA certificate-authenticated token endpoint for account discovery. + default: "" ssl.skip_cert_verify: description: "specifies that the job is allowed to skip ssl cert verification" default: false diff --git a/jobs/cloud_controller_ng/templates/cloud_controller_ng.yml.erb b/jobs/cloud_controller_ng/templates/cloud_controller_ng.yml.erb index c7f69f1ca7..bfb0555b80 100644 --- a/jobs/cloud_controller_ng/templates/cloud_controller_ng.yml.erb +++ b/jobs/cloud_controller_ng/templates/cloud_controller_ng.yml.erb @@ -314,6 +314,10 @@ credential_references: interpolate_service_bindings: <%= p("cc.credential_references.interpolate_service_bindings") %> # App staging parameters +service_account_provisioning_enabled: <%= p('cc.service_accounts.provisioning_enabled') %> +service_account_runtime_enabled: <%= p('cc.service_accounts.runtime_enabled') %> +service_account_token_endpoint: <%= yaml_escape(p('cc.service_accounts.token_endpoint')) %> + staging: legacy_md5_buildpack_paths_enabled: <%= p("cc.legacy_md5_buildpack_paths_enabled") %> timeout_in_seconds: <%= p("cc.staging_timeout_in_seconds") %> From ed47d22184a6f7f21d2be8f317229306b0057265 Mon Sep 17 00:00:00 2001 From: rkoster Date: Mon, 5 Oct 2026 20:45:03 +0200 Subject: [PATCH 05/13] Test service account clock runtime rendering (red) --- spec/cloud_controller_clock/cloud_controller_clock_spec.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/spec/cloud_controller_clock/cloud_controller_clock_spec.rb b/spec/cloud_controller_clock/cloud_controller_clock_spec.rb index 12c9e24361..f3e6c0574d 100644 --- a/spec/cloud_controller_clock/cloud_controller_clock_spec.rb +++ b/spec/cloud_controller_clock/cloud_controller_clock_spec.rb @@ -118,6 +118,13 @@ module Test let(:template) { job.template('config/cloud_controller_ng.yml') } + it 'renders service account runtime discovery for clock-driven reconciliation' do + manifest_properties['cc']['service_accounts'] = { 'runtime_enabled' => true, 'token_endpoint' => 'https://uaa.example.test/oauth/token/mtls' } + result = YAML.safe_load(template.render(manifest_properties, consumes: links)) + expect(result['service_account_runtime_enabled']).to be(true) + expect(result['service_account_token_endpoint']).to eq('https://uaa.example.test/oauth/token/mtls') + end + it 'creates the cloud_controller_ng.yml config file' do expect do YAML.safe_load(template.render(manifest_properties, consumes: links)) From 7dbf40bb5a38ff7a254246037c7868053bb5a960 Mon Sep 17 00:00:00 2001 From: rkoster Date: Mon, 5 Oct 2026 20:45:43 +0200 Subject: [PATCH 06/13] Render service account runtime discovery in clock config --- jobs/cloud_controller_clock/spec | 6 ++++++ .../templates/cloud_controller_ng.yml.erb | 3 +++ 2 files changed, 9 insertions(+) diff --git a/jobs/cloud_controller_clock/spec b/jobs/cloud_controller_clock/spec index b5745d9c8e..cf13475d2b 100644 --- a/jobs/cloud_controller_clock/spec +++ b/jobs/cloud_controller_clock/spec @@ -60,6 +60,12 @@ consumes: optional: true properties: + cc.service_accounts.runtime_enabled: + description: Enable typed account identity in clock reconciliation after runtime rollout. + default: false + cc.service_accounts.token_endpoint: + description: HTTPS UAA certificate-authenticated token endpoint for account discovery. + default: "" ssl.skip_cert_verify: description: "specifies that the job is allowed to skip ssl cert verification" default: false diff --git a/jobs/cloud_controller_clock/templates/cloud_controller_ng.yml.erb b/jobs/cloud_controller_clock/templates/cloud_controller_ng.yml.erb index 650ef6a0b4..05153a299e 100644 --- a/jobs/cloud_controller_clock/templates/cloud_controller_ng.yml.erb +++ b/jobs/cloud_controller_clock/templates/cloud_controller_ng.yml.erb @@ -223,6 +223,9 @@ uaa: <% end %> # App staging parameters +service_account_runtime_enabled: <%= p('cc.service_accounts.runtime_enabled') %> +service_account_token_endpoint: <%= yaml_escape(p('cc.service_accounts.token_endpoint')) %> + staging: timeout_in_seconds: <%= p("cc.staging_timeout_in_seconds") %> auth: From d2f5bb7ef679c99e3493ef44a56a6e0c5fe74987 Mon Sep 17 00:00:00 2001 From: rkoster Date: Mon, 5 Oct 2026 22:41:58 +0200 Subject: [PATCH 07/13] Bump cloud_controller_ng for space-owned service account POC --- src/cloud_controller_ng | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cloud_controller_ng b/src/cloud_controller_ng index 3a4f0bc491..8af06c4402 160000 --- a/src/cloud_controller_ng +++ b/src/cloud_controller_ng @@ -1 +1 @@ -Subproject commit 3a4f0bc49185f08feea8d8f9f5e821eb262f8968 +Subproject commit 8af06c44022e64b1b5b4e081234c807f2747a6a9 From fefb8c4bcb966e41348ae30f78345545d17c67c8 Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 07:10:05 +0200 Subject: [PATCH 08/13] Make packaged application directories readable by runtime user --- packages/cloud_controller_ng/packaging | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/cloud_controller_ng/packaging b/packages/cloud_controller_ng/packaging index 9344859f97..0d5b9b8a14 100644 --- a/packages/cloud_controller_ng/packaging +++ b/packages/cloud_controller_ng/packaging @@ -3,6 +3,9 @@ set -e -x # shellcheck disable=1090 source "${BOSH_PACKAGES_DIR:-/var/vcap/packages}/ruby-4.0/bosh/compile.env" cp -a * ${BOSH_INSTALL_TARGET} +# Archive-created parent directories may inherit a private build umask. +# The application is read by the unprivileged vcap runtime user. +chmod -R a+rX "${BOSH_INSTALL_TARGET}/cloud_controller_ng" cd ${BOSH_INSTALL_TARGET}/cloud_controller_ng mariadb_dir=/var/vcap/packages/mariadb_connector_c From 9fa307b81fc1d5d8eb8d1b07f70d3318b6e3f970 Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 07:22:09 +0200 Subject: [PATCH 09/13] Bump cloud_controller_ng for MySQL service account migration recovery --- src/cloud_controller_ng | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cloud_controller_ng b/src/cloud_controller_ng index 8af06c4402..e4fdc2b67e 160000 --- a/src/cloud_controller_ng +++ b/src/cloud_controller_ng @@ -1 +1 @@ -Subproject commit 8af06c44022e64b1b5b4e081234c807f2747a6a9 +Subproject commit e4fdc2b67ee2b15a927cb9ea5f75a653f615191b From ca65001417c34285c1394b4c2b6b2679c014b5b2 Mon Sep 17 00:00:00 2001 From: rkoster Date: Tue, 6 Oct 2026 08:25:09 +0200 Subject: [PATCH 10/13] Bump cloud_controller_ng for normalized UAA client reconciliation --- src/cloud_controller_ng | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cloud_controller_ng b/src/cloud_controller_ng index e4fdc2b67e..8a4e504ae5 160000 --- a/src/cloud_controller_ng +++ b/src/cloud_controller_ng @@ -1 +1 @@ -Subproject commit e4fdc2b67ee2b15a927cb9ea5f75a653f615191b +Subproject commit 8a4e504ae54b3b90753ebeedb0215a0c553c3ad1 From fbd0c3ed3fcad4e8fbd25388227f453bed4d77c7 Mon Sep 17 00:00:00 2001 From: rkoster Date: Wed, 7 Oct 2026 09:40:18 +0200 Subject: [PATCH 11/13] Test configurable service account creation budget rendering (red) --- spec/cloud_controller_ng/cloud_controller_ng_spec.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/spec/cloud_controller_ng/cloud_controller_ng_spec.rb b/spec/cloud_controller_ng/cloud_controller_ng_spec.rb index 9ee8975f4e..24739efa13 100644 --- a/spec/cloud_controller_ng/cloud_controller_ng_spec.rb +++ b/spec/cloud_controller_ng/cloud_controller_ng_spec.rb @@ -142,6 +142,17 @@ module Test describe 'config/cloud_controller_ng.yml' do let(:template) { job.template('config/cloud_controller_ng.yml') } + it 'renders an unlimited service account creation budget by default' do + result = YAML.safe_load(template.render(merged_manifest_properties, consumes: links)) + expect(result['service_account_creation_limit']).to eq(-1) + end + + it 'renders the foundation-wide non-admin service account creation budget' do + merged_manifest_properties['cc']['service_accounts'] = { 'creation_limit' => 25 } + result = YAML.safe_load(template.render(merged_manifest_properties, consumes: links)) + expect(result['service_account_creation_limit']).to eq(25) + end + it 'renders explicit service account gates and discovery without exposing management credentials' do merged_manifest_properties['cc']['service_accounts'] = { 'provisioning_enabled' => true, 'runtime_enabled' => true, From e2a5bce25aa68d60820d3b4bd6649f4fdad0ace0 Mon Sep 17 00:00:00 2001 From: rkoster Date: Wed, 7 Oct 2026 09:40:57 +0200 Subject: [PATCH 12/13] Render configurable weekly service account creation limit --- jobs/cloud_controller_ng/spec | 3 +++ jobs/cloud_controller_ng/templates/cloud_controller_ng.yml.erb | 1 + 2 files changed, 4 insertions(+) diff --git a/jobs/cloud_controller_ng/spec b/jobs/cloud_controller_ng/spec index 85d3c4c4b0..5adc48970d 100644 --- a/jobs/cloud_controller_ng/spec +++ b/jobs/cloud_controller_ng/spec @@ -243,6 +243,9 @@ consumes: optional: true properties: + cc.service_accounts.creation_limit: + description: Foundation-wide maximum successful service account creations per non-admin authenticated principal over a rolling seven-day window. Platform admins are exempt; -1 means unlimited and 0 prevents non-admin creation. Deletion does not refund budget. + default: -1 cc.service_accounts.provisioning_enabled: description: Enable asynchronous service account client reconciliation requests. default: false diff --git a/jobs/cloud_controller_ng/templates/cloud_controller_ng.yml.erb b/jobs/cloud_controller_ng/templates/cloud_controller_ng.yml.erb index bfb0555b80..167d139942 100644 --- a/jobs/cloud_controller_ng/templates/cloud_controller_ng.yml.erb +++ b/jobs/cloud_controller_ng/templates/cloud_controller_ng.yml.erb @@ -315,6 +315,7 @@ credential_references: # App staging parameters service_account_provisioning_enabled: <%= p('cc.service_accounts.provisioning_enabled') %> +service_account_creation_limit: <%= p('cc.service_accounts.creation_limit') %> service_account_runtime_enabled: <%= p('cc.service_accounts.runtime_enabled') %> service_account_token_endpoint: <%= yaml_escape(p('cc.service_accounts.token_endpoint')) %> From a186d2d41732ffcee9c4736750117d431e9e7612 Mon Sep 17 00:00:00 2001 From: rkoster Date: Wed, 7 Oct 2026 09:43:00 +0200 Subject: [PATCH 13/13] Bump cloud_controller_ng for creation budget and recursive account cleanup --- src/cloud_controller_ng | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cloud_controller_ng b/src/cloud_controller_ng index 8a4e504ae5..ec582b703e 160000 --- a/src/cloud_controller_ng +++ b/src/cloud_controller_ng @@ -1 +1 @@ -Subproject commit 8a4e504ae54b3b90753ebeedb0215a0c553c3ad1 +Subproject commit ec582b703ef80fa9b9c5b3c34ca2798a29b4b801