diff --git a/jobs/uaa/spec b/jobs/uaa/spec index cc4ae2836..e9de2ae66 100644 --- a/jobs/uaa/spec +++ b/jobs/uaa/spec @@ -1304,8 +1304,12 @@ properties: BTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAL5j1JCN5EoXMOOBSBUL8KeVZFQD3Nfy YkYKBatFEKdBFlAKLBdG+5KzE7sTYesn7EzBISHXFz3DhdK2tg+IF1DeSFVmFl2n iVxQ1sYjo4kCugHBsWo+MpFH9VBLFzsMlP3eIDuVKe8aPXFKYCGhctZEJdQTKlja - lshe50nayKrT - -----END CERTIFICATE---- + lshe50nayKrT + -----END CERTIFICATE---- + + uaa.mtls_enabled: + description: "Enable RFC 8705 mTLS client authentication at /oauth/mtls/token. Requires Gorouter forwarded_client_cert: sanitize_set." + default: false login.saml.providers: description: | Contains a hash of SAML Identity Providers, diff --git a/jobs/uaa/templates/config/uaa.yml.erb b/jobs/uaa/templates/config/uaa.yml.erb index 2ef225569..958d776b8 100644 --- a/jobs/uaa/templates/config/uaa.yml.erb +++ b/jobs/uaa/templates/config/uaa.yml.erb @@ -369,6 +369,13 @@ client.each do |key,value| client_data[key] = value end + has_mtls_ca = client_data['tls-client-auth-ca'].is_a?(String) && + !client_data['tls-client-auth-ca'].strip.empty? + client_data.delete('tls-client-auth-trusted-proxy-ca') if + client_data['tls-client-auth-trusted-proxy-ca'].is_a?(String) && + client_data['tls-client-auth-trusted-proxy-ca'].strip.empty? + message = message + "\nInvalid property: uaa.clients.#{id}.token-endpoint-auth-method" if + client_data.key?('token-endpoint-auth-method') if !client['scopes'].nil? client_data.delete('scopes') if client['scopes'].is_a? Array @@ -380,7 +387,10 @@ if is_missing(client_data, 'authorized-grant-types') message = message + "\nMissing property: uaa.clients.#{id}.authorized-grant-types" else - message = message + "\nMissing property: uaa.clients.#{id}.secret" if client_data['secret'].nil? && client_data['authorized-grant-types'] != 'implicit' + message = message + "\nMissing property: uaa.clients.#{id}.secret" if + client_data['secret'].nil? && + client_data['authorized-grant-types'] != 'implicit' && + !has_mtls_ca if (client_data['redirect-uri'].nil? || client_data['redirect-uri'].empty?) message = message + "\nMissing property: uaa.clients.#{id}.redirect-uri" if client_data['authorized-grant-types'] =~ /implicit|authorization_code/ elsif client_data['redirect-uri'].split(',').any? { |uri| !(uri =~ /^http(\*|s)?:\/\/(.*:.*@)?(([a-zA-Z0-9\-\*]+\.)*[a-zA-Z0-9\-]+\.)?[a-zA-Z0-9\-]+(:[0-9]+)?(\/.*|$)/) } @@ -653,6 +663,7 @@ 'sleep' => p('uaa.shutdown.sleep') }, 'url' => uaa_base, + 'mtls-enabled' => p('uaa.mtls_enabled'), 'limitedFunctionality' => { 'statusFile' => p('uaa.limitedFunctionality.statusFile'), 'whitelist' => { diff --git a/spec/uaa-release.erb_spec.rb b/spec/uaa-release.erb_spec.rb index d04fe1547..270eb9c32 100755 --- a/spec/uaa-release.erb_spec.rb +++ b/spec/uaa-release.erb_spec.rb @@ -183,6 +183,19 @@ def str_compare(output, actual) end end + context 'when mTLS is enabled' do + let(:input) {'spec/input/test-defaults.yml'} + let(:erb_template) {'../jobs/uaa/templates/config/uaa.yml.erb'} + + before do + generated_cf_manifest['properties']['uaa']['mtls_enabled'] = true + end + + it 'does not render a configurable mtls endpoint block' do + expect(parsed_yaml).not_to have_key('mtls') + end + end + context 'for deprecated-properties-still-work.yml' do let(:input) {'spec/input/deprecated-properties-still-work.yml'} let(:output_uaa) {'spec/compare/deprecated-properties-still-work-uaa.yml'} @@ -1261,6 +1274,50 @@ def str_compare(output, actual) end end + context 'client_credentials mTLS clients' do + let(:erb_template) {'../jobs/uaa/templates/config/uaa.yml.erb'} + + before do + client = generated_cf_manifest['properties']['uaa']['clients']['app'] + client['authorized-grant-types'] = 'client_credentials' + client.delete('secret') + end + + it 'allows a secretless client with a nonblank tls-client-auth-ca' do + generated_cf_manifest['properties']['uaa']['clients']['app']['tls-client-auth-ca'] = "-----BEGIN CERTIFICATE-----\nCA\n-----END CERTIFICATE-----" + + expect { parsed_yaml }.not_to raise_error + end + + it 'omits a blank tls-client-auth-trusted-proxy-ca' do + generated_cf_manifest['properties']['uaa']['clients']['app']['tls-client-auth-trusted-proxy-ca'] = ' ' + + expect(parsed_yaml['oauth']['clients']['app']).not_to have_key('tls-client-auth-trusted-proxy-ca') + end + + it 'requires a secret when tls-client-auth-ca is absent' do + expect { + parsed_yaml + }.to raise_error(ArgumentError, /Missing property: uaa.clients.app.secret/) + end + + it 'requires a secret when tls-client-auth-ca is blank' do + generated_cf_manifest['properties']['uaa']['clients']['app']['tls-client-auth-ca'] = ' ' + + expect { + parsed_yaml + }.to raise_error(ArgumentError, /Missing property: uaa.clients.app.secret/) + end + + it 'does not exempt a client with private-key trust configuration from requiring a secret' do + generated_cf_manifest['properties']['uaa']['clients']['app']['client_jwt_config'] = '{}' + + expect { + parsed_yaml + }.to raise_error(ArgumentError, /Missing property: uaa.clients.app.secret/) + end + end + context 'redirect-uri is missing from required grant types' do let(:erb_template) {'../jobs/uaa/templates/config/uaa.yml.erb'} grant_types_requiring_secret = ['authorization_code', 'implicit'] diff --git a/src/uaa b/src/uaa index 5d42e16ae..aeb439805 160000 --- a/src/uaa +++ b/src/uaa @@ -1 +1 @@ -Subproject commit 5d42e16ae2771a80f5e8818125e971d3b56cd09f +Subproject commit aeb43980599ea9c4b4f1104d59e3661c53a2caff