From 2c51d6f4ccf4a473089c08857262e769f7a8fc3a Mon Sep 17 00:00:00 2001 From: Al Bailey Date: Thu, 7 Mar 2019 13:43:40 -0600 Subject: [PATCH 1/4] WRS: Patch1: 0001-pike-rebase-squash-titanium-patches.patch --- lib/puppet/provider/keystone.rb | 79 ++++++++++++++++++++++- manifests/db/sync.pp | 3 + manifests/init.pp | 76 +++++++++++++++++----- manifests/ldap.pp | 7 ++ manifests/logging.pp | 2 +- manifests/resource/service_identity.pp | 7 ++ manifests/security_compliance.pp | 45 +++++++++++++ spec/classes/keystone_security_compliance_spec.rb | 19 ++++++ 8 files changed, 220 insertions(+), 18 deletions(-) create mode 100644 manifests/security_compliance.pp create mode 100644 spec/classes/keystone_security_compliance_spec.rb diff --git a/lib/puppet/provider/keystone.rb b/lib/puppet/provider/keystone.rb index 3841418..0857ac1 100644 --- a/lib/puppet/provider/keystone.rb +++ b/lib/puppet/provider/keystone.rb @@ -3,6 +3,7 @@ require 'puppet/provider/openstack' require 'puppet/provider/openstack/auth' require 'puppet/provider/openstack/credentials' require File.join(File.dirname(__FILE__), '..','..', 'puppet/provider/keystone/util') +require 'hiera_puppet' class Puppet::Provider::Keystone < Puppet::Provider::Openstack @@ -230,12 +231,88 @@ class Puppet::Provider::Keystone < Puppet::Provider::Openstack end end + ### WRS Modifications (Start) ### + + def self.hiera_lookup(key) + HieraPuppet.lookup(key, :undef, self, nil, :priority) + end + + def self.initial_config_primary? + return true if ENV['INITIAL_CONFIG_PRIMARY'] == "true" + end + + + def self.upgrading? + return true if hiera_lookup('platform::params::controller_upgrade') == true + end + def self.request(service, action, properties=nil, options={}) super rescue Puppet::Error::OpenstackAuthInputError, Puppet::Error::OpenstackUnauthorizedError => error - request_by_service_token(service, action, error, properties, options=options) + if initial_config_primary? + # admin user account might not have been created + request_by_service_token(service, action, error, properties) + else + if upgrading? + # when running the Keystone manifest during an upgrade + # (on controller-1), we need to use an AUTH token and + # a bypass URL since using the default AUTL URL will + # send the Request to the service catalog URL (internalURL), + # running on the non-upgraded controller-0 which cannot + # service this request + request_by_upgrading_token(service, action, error, properties) + else + request_by_admin_credential(service, action, error, properties) + end + end end + def self.request_by_admin_credential(service, action, error, properties=nil) + properties ||= [] + @credentials.username = hiera_lookup('platform::client::params::admin_username') + @credentials.password = hiera_lookup('keystone::admin_password') + @credentials.project_name = 'admin' + @credentials.auth_url = service_url + @credentials.identity_api_version = @credentials.version + if @credentials.version == '3' + @credentials.user_domain_name = hiera_lookup('platform::client::params::admin_user_domain') + @credentials.project_domain_name = hiera_lookup('platform::client::params::admin_project_domain') + end + raise error unless @credentials.set? + Puppet::Provider::Openstack.request(service, action, properties, @credentials) + end + + def self.get_upgrade_token + upgrade_token_file = hiera_lookup('openstack::keystone::upgrade::upgrade_token_file') + # the upgrade token file may get refreshed by the same Puppet event + # that triggered this call, and therefore may not be available + # immediately. Try for timeout before quitting with error + timeout = 10 # 10 seconds + 1.upto(timeout) do |iter| + if File.exists?(upgrade_token_file) + upgrade_token = File.read(upgrade_token_file).strip + notice("Found #{upgrade_token_file} token file and upgrade token #{upgrade_token}.") + return upgrade_token + else + Puppet.debug("#{upgrade_token_file} not found. Retrying for #{iter} more seconds.") + sleep(1) + end + end + raise(Puppet::ExecutionFailure, "Can't retrieve #{upgrade_token_file} in #{timeout}s retry attempts.") + end + + + def self.request_by_upgrading_token(service, action, error, properties=nil, options={}) + properties ||= [] + @credentials.token = get_upgrade_token + @credentials.url = hiera_lookup('openstack::keystone::upgrade::url') + raise error unless @credentials.service_token_set? + Puppet::Provider::Openstack.request(service, action, properties, @credentials, options) + end + + ### WRS Additions (End) ### + + def self.request_by_service_token(service, action, error, properties=nil, options={}) properties ||= [] @credentials.token = admin_token diff --git a/manifests/db/sync.pp b/manifests/db/sync.pp index cee869b..cea217c 100644 --- a/manifests/db/sync.pp +++ b/manifests/db/sync.pp @@ -36,5 +36,8 @@ class keystone::db::sync( ], notify => Anchor['keystone::dbsync::end'], tag => 'keystone-exec', + # Only do the db sync if both controllers are running the same software + # version. Avoids impacting mate controller during an upgrade. + onlyif => "test $::controller_sw_versions_match = true", } } diff --git a/manifests/init.pp b/manifests/init.pp index 2adc685..4d79d30 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -28,6 +28,15 @@ # The admin_token has been deprecated by the Keystone service and this # will be deprecated in a future changeset. Required. # +# [*upgrade_token_cmd*] +# (optional) WRS - if we are in an upgrade scenario, an upgrade token +# will be required to bypass authentication. +# Defaults to undef +# +# [*upgrade_token_file*] +# (optional) WRS - the file where the upgrade token will be stowed +# Defaults to undef +# # [*admin_password*] # Keystone password for the admin user. This is not the admin_token. # This is the password that the admin user signs into keystone with. @@ -663,6 +672,8 @@ # class keystone( $admin_token, + $upgrade_token_cmd = undef, + $upgrade_token_file = undef, $admin_password = undef, $package_ensure = 'present', $client_package_ensure = 'present', @@ -857,10 +868,13 @@ admin_token will be removed in a later release") keystone_config { 'DEFAULT/admin_token': value => $admin_token, secret => true; + # WRS: the following options are deprecated for removal + # however public_bind_host and admin_bind_host are still required as long as + # keystone is running under eventlet 'DEFAULT/public_bind_host': value => $public_bind_host; 'DEFAULT/admin_bind_host': value => $admin_bind_host; - 'DEFAULT/public_port': value => $public_port; - 'DEFAULT/admin_port': value => $admin_port; + #'DEFAULT/public_port': value => $public_port; + #'DEFAULT/admin_port': value => $admin_port; 'DEFAULT/member_role_id': value => $member_role_id; 'DEFAULT/member_role_name': value => $member_role_name; 'paste_deploy/config_file': value => $paste_config; @@ -897,18 +911,21 @@ admin_token will be removed in a later release") # ssl config if ($enable_ssl) { keystone_config { - 'ssl/enable': value => true; + # WRS ssl/enable is deprecated for removal + #'ssl/enable': value => true; 'ssl/certfile': value => $ssl_certfile; 'ssl/keyfile': value => $ssl_keyfile; 'ssl/ca_certs': value => $ssl_ca_certs; 'ssl/ca_key': value => $ssl_ca_key; 'ssl/cert_subject': value => $ssl_cert_subject; } - } else { - keystone_config { - 'ssl/enable': value => false; - } } + # WRS ssl/enable is deprecated for removal + # else { + # keystone_config { + # 'ssl/enable': value => false; + # } + #} if !is_service_default($memcache_servers) or !is_service_default($cache_memcache_servers) { Service<| title == 'memcached' |> -> Anchor['keystone::service::begin'] @@ -1016,14 +1033,15 @@ Fernet or UUID tokens are recommended.") Fernet or UUID tokens are recommended.") } - keystone_config { - 'signing/certfile': value => $signing_certfile; - 'signing/keyfile': value => $signing_keyfile; - 'signing/ca_certs': value => $signing_ca_certs; - 'signing/ca_key': value => $signing_ca_key; - 'signing/cert_subject': value => $signing_cert_subject; - 'signing/key_size': value => $signing_key_size; - } + # WRS: the following signing options are deprecated for removal + #keystone_config { + # 'signing/certfile': value => $signing_certfile; + # 'signing/keyfile': value => $signing_keyfile; + # 'signing/ca_certs': value => $signing_ca_certs; + # 'signing/ca_key': value => $signing_ca_key; + # 'signing/cert_subject': value => $signing_cert_subject; + # 'signing/key_size': value => $signing_key_size; + #} # Only do pki_setup if we were asked to do so. This is needed # regardless of the token provider since token revocation lists @@ -1089,6 +1107,9 @@ Fernet or UUID tokens are recommended.") heartbeat_rate => $rabbit_heartbeat_rate, } + # WRS: The following options are deprecated for removal + # however they are still required as long as keystone + # is running under eventlet keystone_config { 'eventlet_server/admin_workers': value => $admin_workers; 'eventlet_server/public_workers': value => $public_workers; @@ -1135,7 +1156,8 @@ Fernet or UUID tokens are recommended.") validate => false, } } - warning("Keystone under Eventlet has been deprecated during the Kilo cycle. \ + # Drop this to info. + info("Keystone under Eventlet has been deprecated during the Kilo cycle. \ Support for deploying under eventlet will be dropped as of the M-release of OpenStack.") } elsif $service_name == 'httpd' { include ::apache::params @@ -1280,6 +1302,27 @@ running as a standalone service, or httpd for being run by a httpd server") } } + # WRS: Now that the keystone service has started, + # check if we are in an Upgrade scenario, and generate + # an upgrade token which will be used to bypass Keystone + # authentication (specifically the service catalog) for + # all operations during upgrades. + # This operation is similar to the keystone bootstrap + # operation (above) which would generate an admin + # token, and therefore also requires the database to + # be up and running and configured and is only run once, + # so we don't need to notify the service + if $upgrade_token_cmd and $upgrade_token_file { + exec { 'upgrade token issue': + command => "${upgrade_token_cmd} > ${upgrade_token_file}", + path => '/usr/bin', + creates => $upgrade_token_file, + subscribe => Service[$service_name], + notify => Anchor['keystone::service::end'], + tag => 'keystone-exec', + } + } + if $using_domain_config { validate_absolute_path($domain_config_directory) # Better than ensure resource. We don't want to conflict with any @@ -1311,4 +1354,5 @@ running as a standalone service, or httpd for being run by a httpd server") {'value' => $domain_config_directory} ) } + } diff --git a/manifests/ldap.pp b/manifests/ldap.pp index 11620bf..728ca40 100644 --- a/manifests/ldap.pp +++ b/manifests/ldap.pp @@ -4,6 +4,11 @@ # # === parameters: # +# [*debug_level*] +# LDAP debugging level for LDAP calls; a value of zero("0") disables +# debugging. (integer value) +# Defaults to 'undef' +# # [*url*] # URL for connecting to the LDAP server. (string value) # Defaults to 'undef' @@ -384,6 +389,7 @@ # Copyright 2012 Puppetlabs Inc, unless otherwise noted. # class keystone::ldap( + $debug_level = undef, $url = undef, $user = undef, $password = undef, @@ -494,6 +500,7 @@ class keystone::ldap( } keystone_config { + 'ldap/debug_level': value => $debug_level; 'ldap/url': value => $url; 'ldap/user': value => $user; 'ldap/password': value => $password, secret => true; diff --git a/manifests/logging.pp b/manifests/logging.pp index e737c4f..3d8df63 100644 --- a/manifests/logging.pp +++ b/manifests/logging.pp @@ -110,7 +110,7 @@ class keystone::logging( $log_file = $::os_service_default, $debug = $::os_service_default, $logging_context_format_string = $::os_service_default, - $logging_default_format_string = $::os_service_default, + $logging_default_format_string = 'keystone:log %(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s [-] %(instance)s%(message)s', $logging_debug_format_suffix = $::os_service_default, $logging_exception_prefix = $::os_service_default, $logging_user_identity_format = $::os_service_default, diff --git a/manifests/resource/service_identity.pp b/manifests/resource/service_identity.pp index 09e7d94..243c9ec 100644 --- a/manifests/resource/service_identity.pp +++ b/manifests/resource/service_identity.pp @@ -187,6 +187,8 @@ define keystone::resource::service_identity( if $service_type { ensure_resource('keystone_service', "${service_name_real}::${service_type}", { 'ensure' => $ensure, + 'name' => $service_name_real, + 'type' => $service_type, 'description' => $service_description, }) } else { @@ -199,6 +201,9 @@ define keystone::resource::service_identity( if $public_url and $admin_url and $internal_url { ensure_resource('keystone_endpoint', "${region}/${service_name_real}::${service_type}", { 'ensure' => $ensure, + 'name' => $service_name_real, + 'type' => $service_type, + 'region' => $region, 'public_url' => $public_url, 'admin_url' => $admin_url, 'internal_url' => $internal_url, @@ -210,6 +215,8 @@ define keystone::resource::service_identity( if $public_url and $admin_url and $internal_url { ensure_resource('keystone_endpoint', "${region}/${service_name_real}", { 'ensure' => $ensure, + 'name' => $service_name_real, + 'region' => $region, 'public_url' => $public_url, 'admin_url' => $admin_url, 'internal_url' => $internal_url, diff --git a/manifests/security_compliance.pp b/manifests/security_compliance.pp new file mode 100644 index 0000000..64830ec --- /dev/null +++ b/manifests/security_compliance.pp @@ -0,0 +1,45 @@ +# == class: keystone::security_compliance +# +# Implements security compliance configuration for keystone. +# +# === parameters: +# +# [*unique_last_password_count*] +# This controls the number of previous user password iterations +# to keep in history, in order to enforce that newly created passwords +# are unique. Setting the value to 1 (the default) disables this feature. +# (integer value) +# Defaults to 'undef' +# +# [*password_regex*] +# The regular expression used to validate password strength +# requirements. By default, the regular expression will match +# any password. (string value) +# Defaults to 'undef' +# +# [*password_regex_description*] +# If a password fails to match the regular expression (*password_regex*), +# the contents of this configuration will be returned to users to explain +# why their requested password was insufficient. (string value) +# Defaults to 'undef' +# +# === DEPRECATED group/name +# +# == Copyright +# +# Copyright 2017 Wind River Systems, unless otherwise noted. +# +class keystone::security_compliance( + $unique_last_password_count = undef, + $password_regex = undef, + $password_regex_description = undef, +) { + + include ::keystone::deps + + keystone_config { + 'security_compliance/unique_last_password_count': value => $unique_last_password_count; + 'security_compliance/password_regex': value => $password_regex; + 'security_compliance/password_regex_description': value => $password_regex_description; + } +} diff --git a/spec/classes/keystone_security_compliance_spec.rb b/spec/classes/keystone_security_compliance_spec.rb new file mode 100644 index 0000000..d0d4724 --- /dev/null +++ b/spec/classes/keystone_security_compliance_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe 'keystone::security_compliance' do + describe 'with basic params' do + let :params do + { + :unique_last_password_count => 2, + :password_regex => '^(?=.*\d)(?=.*[a-zA-Z]).{7,}$', + :password_regex_description => 'password must be at least 7 characters long and contain 1 digit', + } + end + it 'should have basic params' do + # basic params + is_expected.to contain_keystone_config('security_compliance/unique_last_password_count').with_value('2') + is_expected.to contain_keystone_config('security_compliance/password_regex').with_value('^(?=.*\d)(?=.*[a-zA-Z]).{7,}$') + is_expected.to contain_keystone_config('security_compliance/password_regex_description').with_value('password must be at least 7 characters long and contain 1 digit') + end + end +end -- 1.8.3.1