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: diff --git a/jobs/cloud_controller_ng/spec b/jobs/cloud_controller_ng/spec index ba7e1b0d25..5adc48970d 100644 --- a/jobs/cloud_controller_ng/spec +++ b/jobs/cloud_controller_ng/spec @@ -243,6 +243,18 @@ 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 + 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..167d139942 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,11 @@ 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_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')) %> + staging: legacy_md5_buildpack_paths_enabled: <%= p("cc.legacy_md5_buildpack_paths_enabled") %> timeout_in_seconds: <%= p("cc.staging_timeout_in_seconds") %> 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") %> 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 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)) diff --git a/spec/cloud_controller_ng/cloud_controller_ng_spec.rb b/spec/cloud_controller_ng/cloud_controller_ng_spec.rb index b1d749ef75..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,29 @@ 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, + '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)) 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)) diff --git a/src/cloud_controller_ng b/src/cloud_controller_ng index 3a4f0bc491..ec582b703e 160000 --- a/src/cloud_controller_ng +++ b/src/cloud_controller_ng @@ -1 +1 @@ -Subproject commit 3a4f0bc49185f08feea8d8f9f5e821eb262f8968 +Subproject commit ec582b703ef80fa9b9c5b3c34ca2798a29b4b801