From 9fd52ae7b42b888f1f0be03191d82fd9bfea5488 Mon Sep 17 00:00:00 2001 From: Graeme Gillies Date: Fri, 24 Jun 2016 15:23:40 +1000 Subject: [PATCH] Added federation support for OpenID Connect with mod_auth_openidc Change-Id: I710de4f38b899ab04cec8b3c5188e8a383bec18c --- manifests/federation/openidc.pp | 128 ++++++++++++++++++ .../federation/openidc_httpd_configuration.pp | 25 ++++ manifests/params.pp | 2 + spec/classes/keystone_federation_openidc.rb | 106 +++++++++++++++ templates/openidc.conf.erb | 15 ++ 5 files changed, 276 insertions(+) create mode 100644 manifests/federation/openidc.pp create mode 100644 manifests/federation/openidc_httpd_configuration.pp create mode 100644 spec/classes/keystone_federation_openidc.rb create mode 100644 templates/openidc.conf.erb diff --git a/manifests/federation/openidc.pp b/manifests/federation/openidc.pp new file mode 100644 index 000000000..1da48b5c7 --- /dev/null +++ b/manifests/federation/openidc.pp @@ -0,0 +1,128 @@ +# == class: keystone::federation::openidc [70/1473] +# +# == Parameters +# +# [*methods*] +# A list of methods used for authentication separated by comma or an array. +# The allowed values are: 'external', 'password', 'token', 'oauth1', 'saml2' +# (Required) (string or array value). +# Note: The external value should be dropped to avoid problems. +# +# [*idp_name*] +# The name name associated with the IdP in Keystone. +# (Required) String value. +# +# [*openidc_provider_metadata_url*] +# The url that points to your OpenID Connect metadata provider +# (Required) String value. +# +# [*openidc_client_id*] +# The client ID to use when handshaking with your OpenID Connect provider +# (Required) String value. +# +# [*openidc_client_secret*] +# The client secret to use when handshaking with your OpenID Connect provider +# (Required) String value. +# +# [*openidc_crypto_passphrase*] +# Secret passphrase to use when encrypting data for OpenID Connect handshake +# (Optional) String value. +# Defaults to 'openstack' +# +# [*admin_port*] +# A boolean value to ensure that you want to configure openidc Federation +# using Keystone VirtualHost on port 35357. +# (Optional) Defaults to false. +# +# [*main_port*] +# A boolean value to ensure that you want to configure openidc Federation +# using Keystone VirtualHost on port 5000. +# (Optional) Defaults to true. +# +# [*module_plugin*] +# The plugin for authentication acording to the choice made with protocol and +# module. +# (Optional) Defaults to 'keystone.auth.plugins.mapped.Mapped' (string value) +# +# [*template_order*] +# This number indicates the order for the concat::fragment that will apply +# the shibboleth configuration to Keystone VirtualHost. The value should +# The value should be greater than 330 an less then 999, according to: +# https://github.com/puppetlabs/puppetlabs-apache/blob/master/manifests/vhost.pp +# The value 330 corresponds to the order for concat::fragment "${name}-filters" +# and "${name}-limits". +# The value 999 corresponds to the order for concat::fragment "${name}-file_footer". +# (Optional) Defaults to 331. +# +# [*package_ensure*] +# (optional) Desired ensure state of packages. +# accepts latest or specific versions. +# Defaults to present. +# +class keystone::federation::openidc ( + $methods, + $idp_name, + $openidc_provider_metadata_url, + $openidc_client_id, + $openidc_client_secret, + $openidc_crypto_passphrase = 'openstack', + $admin_port = false, + $main_port = true, + $module_plugin = 'keystone.auth.plugins.mapped.Mapped', + $template_order = 331, + $package_ensure = present, +) { + + include ::apache + include ::keystone::deps + include ::keystone::params + + # Note: if puppet-apache modify these values, this needs to be updated + if $template_order <= 330 or $template_order >= 999 { + fail('The template order should be greater than 330 and less than 999.') + } + + if ('external' in $methods ) { + fail('The external method should be dropped to avoid any interference with openidc') + } + + if !('openidc' in $methods ) { + fail('Methods should contain openidc as one of the auth methods.') + } else { + if ($module_plugin != 'keystone.auth.plugins.mapped.Mapped') { + fail('Other plugins are not currently supported for openidc') + } + } + + validate_bool($admin_port) + validate_bool($main_port) + + if( !$admin_port and !$main_port){ + fail('No VirtualHost port to configure, please choose at least one.') + } + + keystone_config { + 'auth/methods': value => join(any2array($methods),','); + 'auth/openidc': value => $module_plugin; + } + + ensure_packages([$::keystone::params::openidc_package_name], { + ensure => $package_ensure, + tag => 'keystone-support-package', + }) + + if $admin_port { + keystone::federation::openidc_httpd_configuration{ 'admin': + port => $::keystone::admin_port, + keystone_endpoint => $::keystone::admin_endpoint, + } + } + + if $main_port { + keystone::federation::openidc_httpd_configuration{ 'main': + port => $::keystone::public_port, + keystone_endpoint => $::keystone::admin_endpoint, + } + } + +} diff --git a/manifests/federation/openidc_httpd_configuration.pp b/manifests/federation/openidc_httpd_configuration.pp new file mode 100644 index 000000000..6b8d8a3d3 --- /dev/null +++ b/manifests/federation/openidc_httpd_configuration.pp @@ -0,0 +1,25 @@ + +# == define: keystone::federation::openidc_httpd_configuration [70/1473] +# +# == Parameters +# +# [*port*] +# The port number to configure OpenIDC federated authentication on +# (Required) String value. +# +# [*keystone_endpoint*] +# The keystone endpoint to use when configuring the OpenIDC redirect back +# to keystone +# (Required) String value. +# +define keystone::federation::openidc_httpd_configuration ( + $port = undef, + $keystone_endpoint = undef +) { + $openidc_redirect_uri = "${keystone_endpoint}/v3/auth/OS-FEDERATION/websso/openidc/redirect" + concat::fragment { "configure_openidc_on_port_${port}": + target => "${keystone::wsgi::apache::priority}-keystone_wsgi_${title}.conf", + content => template('keystone/openidc.conf.erb'), + order => $keystone::federation::openidc::template_order, + } +} diff --git a/manifests/params.pp b/manifests/params.pp index f4f91b7eb..08a134eae 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -14,6 +14,7 @@ class keystone::params { $keystone_wsgi_script_path = '/usr/lib/cgi-bin/keystone' $python_memcache_package_name = 'python-memcache' $mellon_package_name = 'libapache2-mod-auth-mellon' + $openidc_package_name = 'libapache2-mod-auth-openidc' } 'RedHat': { $package_name = 'openstack-keystone' @@ -21,6 +22,7 @@ class keystone::params { $keystone_wsgi_script_path = '/var/www/cgi-bin/keystone' $python_memcache_package_name = 'python-memcached' $mellon_package_name = 'mod_auth_mellon' + $openidc_package_name = 'mod_auth_openidc' } default: { fail("Unsupported osfamily ${::osfamily}") diff --git a/spec/classes/keystone_federation_openidc.rb b/spec/classes/keystone_federation_openidc.rb new file mode 100644 index 000000000..4347c29f9 --- /dev/null +++ b/spec/classes/keystone_federation_openidc.rb @@ -0,0 +1,106 @@ +require 'spec_helper' + +describe 'keystone::federation::openidc' do + + describe 'with invalid params' do + before do + params.merge!(:methods => 'external, password, token, oauth1') + end + + it_raises 'a Puppet::Error', /The external method should be dropped to avoid any interference with openidc/ + + before do + params.merge!(:methods => 'password, token, oauth1') + end + + it_raises 'a Puppet::Error', /Methods should contain openidc as one of the auth methods./ + + before do + params.merge!(:methods => 'password, token, oauth1, openidc', + :module_plugin => 'keystone.auth.plugins') + end + + it_raises 'a Puppet:Error', /The plugin for openidc should be keystone.auth.plugins.mapped.Mapped/ + + before do + params.merge!(:admin_port => false, + :main_port => false) + end + + it_raises 'a Puppet:Error', /No VirtualHost port to configure, please choose at least one./ + + befode do + params.merge!(:template_port => 330) + end + + it_raises 'a Puppet:Error', /The template order should be greater than 330 and less than 999./ + + befode do + params.merge!(:template_port => 999) + end + + it_raises 'a Puppet:Error', /The template order should be greater than 330 and less than 999./ + end + + on_supported_os({ + }).each do |os,facts| + let (:facts) do + facts.merge!(OSDefaults.get_facts({})) + end + + let(:platform_parameters) do + case facts[:osfamily] + when 'Debian' + { + :openidc_package_name => 'libapache2-mod-auth-mellon', + } + when 'RedHat' + { + :openidc_package_name => 'mod_auth_openidc' + } + end + end + + context 'with only required parameters' do + let :params do + { :methods => 'password, token, openidc' } + end + + it 'should have basic params for mellon in Keystone configuration' do + is_expected.to contain_keystone_config('auth/methods').with_value('password, token, openidc') + is_expected.to contain_keystone_config('auth/openidc').with_value('keystone.auth.plugins.mapped.Mapped') + end + + it { is_expected.to contain_concat__fragment('configure_openidc_on_port_5000').with({ + :target => "${keystone::wsgi::apache::priority}-keystone_wsgi_main.conf", + :order => params[:template_order], + })} + end + + context 'with override default parameters' do + let :params do + { :methods => 'password, token, openidc', + :admin_port => true } + end + + it 'should have basic params for mellon in Keystone configuration' do + is_expected.to contain_keystone_config('auth/methods').with_value('password, token, openidc') + is_expected.to contain_keystone_config('auth/openidc').with_value('keystone.auth.plugins.mapped.Mapped') + end + + it { is_expected.to contain_concat__fragment('configure_openidc_on_port_5000').with({ + :target => "${keystone::wsgi::apache::priority}-keystone_wsgi_main.conf", + :order => params[:template_order], + })} + + it { is_expected.to contain_concat__fragment('configure_openidc_on_port_35357').with({ + :target => "${keystone::wsgi::apache::priority}-keystone_wsgi_admin.conf", + :order => params[:template_order], + })} + end + + it { is_expected.to contain_package(platform_parameters[:openidc_package_name]) } + + end + +end diff --git a/templates/openidc.conf.erb b/templates/openidc.conf.erb new file mode 100644 index 000000000..a1efbb059 --- /dev/null +++ b/templates/openidc.conf.erb @@ -0,0 +1,15 @@ + LoadModule auth_openidc_module modules/mod_auth_openidc.so + WSGIScriptAliasMatch ^(/v3/OS-FEDERATION/identity_providers/.*?/protocols/.*?/auth)$ <%= scope['keystone::params::keystone_wsgi_script_path'] -%>/$1 + OIDCClaimPrefix "OIDC-" + OIDCResponseType "id_token" + OIDCScope "openid email profile" + OIDCProviderMetadataURL "<%= scope['keystone::federation::openidc::openidc_provider_metadata_url']-%>" + OIDCClientID "<%= scope['keystone::federation::openidc::openidc_client_id']-%>" + OIDCClientSecret "<%= scope['keystone::federation::openidc::openidc_client_secret']-%>" + OIDCCryptoPassphrase "<%= scope['keystone::federation::openidc::openidc_crypto_passphrase']-%>" + OIDCRedirectURI "<%= @openidc_redirect_uri-%>" + + /protocols/openidc/auth> + AuthType "openid-connect" + Require valid-user +