From 6fd675a2fe288d227084a354da5884e9f3bb69f7 Mon Sep 17 00:00:00 2001 From: Matt Fischer Date: Tue, 25 Mar 2014 10:07:45 -0600 Subject: [PATCH] Full support for Keystone LDAP integration Adding full support for integrating Keystone via LDAP. Enables support for managing all LDAP related Keystone options. - Add two examples of LDAP configuration, although LDAP environments are highly variable, these will help get everyone started - Modify the keystone::ldap class to support all LDAP related options - Check sane defaults in the keystone::ldap class to hopefully reduce mistakes - Add a dependency on the python-ldap package - Modify the LDAP test to match the new class - Make the default-tenant optional since some LDAP backends do not support this Change-Id: Ie6879eb4816fd2b906f72cac8deb3b62bd4b2430 --- examples/ldap_full.pp | 66 +++++++ examples/ldap_identity.pp | 28 +++ lib/puppet/provider/keystone_user/keystone.rb | 1 + lib/puppet/type/keystone_user.rb | 5 + manifests/ldap.pp | 176 ++++++++++++++++-- spec/classes/keystone_ldap_spec.rb | 35 ++-- 6 files changed, 282 insertions(+), 29 deletions(-) create mode 100644 examples/ldap_full.pp create mode 100644 examples/ldap_identity.pp diff --git a/examples/ldap_full.pp b/examples/ldap_full.pp new file mode 100644 index 000000000..d8f1d4113 --- /dev/null +++ b/examples/ldap_full.pp @@ -0,0 +1,66 @@ +# A full example from a real deployment that allows Keystone to modify +# everything except users, uses enabled_emulation, and ldaps + +# Ensure this matches what is in LDAP or keystone will try to recreate +# the admin user +class { 'keystone::roles::admin': + email => 'test@example.com', + password => 'ChangeMe', +} + +# You can test this connection with ldapsearch first to ensure it works. +# LDAP configurations are *highly* dependent on your setup and this file +# will need to be tweaked. This sample talks to ldap.example.com, here is +# an example of ldapsearch that will search users on this box: +# ldapsearch -v -x -H 'ldap://69.134.70.154:389' -D \ +# "uid=bind,cn=users,cn=accounts,dc=example,dc=com" -w SecretPass \ +# -b cn=users,cn=accounts,dc=example,dc=com +class { 'keystone:ldap': + url => 'ldap://ldap.example.com:389', + user => 'uid=bind,cn=users,cn=accounts,dc=example,dc=com', + password => 'SecretPass', + suffix => 'dc=example,dc=com', + query_scope => 'sub', + user_tree_dn => 'cn=users,cn=accounts,dc=example,dc=com', + user_id_attribute => 'uid', + user_name_attribute => 'uid', + user_mail_attribute => 'mail', + user_allow_create => 'False', + user_allow_update => 'False', + user_allow_delete => 'False', + user_enabled_emulation => 'True', + user_enabled_emulation_dn => 'cn=openstack-enabled,cn=groups,cn=accounts,dc=example,dc=com', + group_tree_dn => 'ou=groups,ou=openstack,dc=example,dc=com', + group_objectclass => 'organizationalRole', + group_id_attribute => 'cn', + group_name_attribute => 'cn', + group_member_attribute => 'RoleOccupant', + group_desc_attribute => 'description', + group_allow_create => 'True', + group_allow_update => 'True', + group_allow_delete => 'True', + tenant_tree_dn => 'ou=projects,ou=openstack,dc=example,dc=com', + tenant_objectclass => 'organizationalUnit', + tenant_id_attribute => 'ou', + tenant_member_attribute => 'member', + tenant_name_attribute => 'ou', + tenant_desc_attribute => 'description', + tenant_allow_create => 'True', + tenant_allow_update => 'True', + tenant_allow_delete => 'True', + tenant_enabled_emulation => 'True', + tenant_enabled_emulation_dn => 'cn=enabled,ou=openstack,dc=example,dc=com', + role_tree_dn => 'ou=roles,ou=openstack,dc=example,dc=com', + role_objectclass => 'organizationalRole', + role_id_attribute => 'cn', + role_name_attribute => 'cn', + role_member_attribute => 'roleOccupant', + role_allow_create => 'True', + role_allow_update => 'True', + role_allow_delete => 'True', + identity_driver => 'keystone.identity.backends.ldap.Identity', + assignment_driver => 'keystone.assignment.backends.ldap.Assignment', + use_tls => 'True', + tls_cacertfile => '/etc/ssl/certs/ca-certificates.crt', + tls_req_cert => 'demand', +} diff --git a/examples/ldap_identity.pp b/examples/ldap_identity.pp new file mode 100644 index 000000000..41272c52f --- /dev/null +++ b/examples/ldap_identity.pp @@ -0,0 +1,28 @@ +# Example using LDAP to manage user identity only. +# This setup will not allow changes to users. + +# Ensure this matches what is in LDAP or keystone will try to recreate +# the admin user +class { 'keystone::roles::admin': + email => 'test@example.com', + password => 'ChangeMe', +} + +# You can test this connection with ldapsearch first to ensure it works. +# This was tested against a FreeIPA box, you will likely need to change the +# attributes to match your configuration. +class { 'keystone:ldap': + identity_driver => 'keystone.identity.backends.ldap.Identity', + url => 'ldap://ldap.example.com:389', + user => 'uid=bind,cn=users,cn=accounts,dc=example,dc=com', + password => 'SecretPass', + suffix => 'dc=example,dc=com', + query_scope => 'sub', + user_tree_dn => 'cn=users,cn=accounts,dc=example,dc=com', + user_id_attribute => 'uid', + user_name_attribute => 'uid', + user_mail_attribute => 'mail', + user_allow_create => 'False', + user_allow_update => 'False', + user_allow_delete => 'False' +} diff --git a/lib/puppet/provider/keystone_user/keystone.rb b/lib/puppet/provider/keystone_user/keystone.rb index d499c77e2..07842d0af 100644 --- a/lib/puppet/provider/keystone_user/keystone.rb +++ b/lib/puppet/provider/keystone_user/keystone.rb @@ -99,6 +99,7 @@ Puppet::Type.type(:keystone_user).provide( end def tenant + return resource[:tenant] if resource[:ignore_default_tenant] user_id = user_hash[resource[:name]][:id] begin tenantId = self.class.get_keystone_object('user', user_id, 'tenantId') diff --git a/lib/puppet/type/keystone_user.rb b/lib/puppet/type/keystone_user.rb index 0ee46e5e4..9bf459bc5 100644 --- a/lib/puppet/type/keystone_user.rb +++ b/lib/puppet/type/keystone_user.rb @@ -16,6 +16,11 @@ Puppet::Type.newtype(:keystone_user) do newvalues(/\S+/) end + newparam(:ignore_default_tenant, :boolean => true) do + newvalues(:true, :false) + defaultto false + end + newproperty(:enabled) do newvalues(/(t|T)rue/, /(f|F)alse/) defaultto('True') diff --git a/manifests/ldap.pp b/manifests/ldap.pp index 46f073b8f..623206fe0 100644 --- a/manifests/ldap.pp +++ b/manifests/ldap.pp @@ -6,29 +6,175 @@ # == Authors # # Dan Bode dan@puppetlabs.com +# Matt Fischer matt.fischer@twcable.com # # == Copyright # # Copyright 2012 Puppetlabs Inc, unless otherwise noted. # class keystone::ldap( - $url = 'ldap://localhost', - $user = 'dc=Manager,dc=example,dc=com', - $password = 'None', - $suffix = 'cn=example,cn=com', - $user_tree_dn = 'ou=Users,dc=example,dc=com', - $tenant_tree_dn = 'ou=Roles,dc=example,dc=com', - $role_tree_dn = 'dc=example,dc=com' + $url = undef, + $user = undef, + $password = undef, + $suffix = undef, + $query_scope = undef, + $page_size = undef, + $user_tree_dn = undef, + $user_filter = undef, + $user_objectclass = undef, + $user_id_attribute = undef, + $user_name_attribute = undef, + $user_mail_attribute = undef, + $user_enabled_attribute = undef, + $user_enabled_mask = undef, + $user_enabled_default = undef, + $user_attribute_ignore = undef, + $user_default_project_id_attribute = undef, + $user_allow_create = undef, + $user_allow_update = undef, + $user_allow_delete = undef, + $user_pass_attribute = undef, + $user_enabled_emulation = undef, + $user_enabled_emulation_dn = undef, + $user_additional_attribute_mapping = undef, + $tenant_tree_dn = undef, + $tenant_filter = undef, + $tenant_objectclass = undef, + $tenant_id_attribute = undef, + $tenant_name_attribute = undef, + $tenant_mail_attribute = undef, + $tenant_enabled_attribute = undef, + $tenant_domain_id_attribute = undef, + $tenant_attribute_ignore = undef, + $tenant_allow_create = undef, + $tenant_allow_update = undef, + $tenant_allow_delete = undef, + $tenant_enabled_emulation = undef, + $tenant_enabled_emulation_dn = undef, + $tenant_additional_attribute_mapping = undef, + $role_tree_dn = undef, + $role_filter = undef, + $role_objectclass = undef, + $role_id_attribute = undef, + $role_name_attribute = undef, + $role_member_attribute = undef, + $role_attribute_ignore = undef, + $role_allow_create = undef, + $role_allow_update = undef, + $role_allow_delete = undef, + $role_additional_attribute_mapping = undef, + $group_tree_dn = undef, + $group_filter = undef, + $group_objectclass = undef, + $group_id_attribute = undef, + $group_name_attribute = undef, + $group_member_attribute = undef, + $group_desc_attribute = undef, + $group_attribute_ignore = undef, + $group_allow_create = undef, + $group_allow_update = undef, + $group_allow_delete = undef, + $group_additional_attribute_mapping = undef, + $tenant_tree_dn = undef, + $role_tree_dn = undef, + $use_tls = undef, + $tls_cacertdir = undef, + $tls_cacertfile = undef, + $tls_req_cert = undef, + $identity_driver = undef, + $assignment_driver = undef, ) { + package { 'python-ldap': + ensure => present, + } + + # check for some common driver name mistakes + if ($assignment_driver != undef) { + if ! ($assignment_driver =~ /^keystone.assignment.backends.*Assignment$/) { + fail('assigment driver should be of the form \'keystone.assignment.backends.*Assignment\'') + } + } + + if ($identity_driver != undef) { + if ! ($identity_driver =~ /^keystone.identity.backends.*Identity$/) { + fail('identity driver should be of the form \'keystone.identity.backends.*Identity\'') + } + } + + if ($tls_cacertdir != undef) { + file { $tls_cacertdir: + ensure => directory + } + } + keystone_config { - 'ldap/url': value => $url; - 'ldap/user': value => $user; - 'ldap/password': value => $password; - 'ldap/suffix': value => $suffix; - 'ldap/user_tree_dn': value => $user_tree_dn; - 'ldap/tenant_tree_dn': value => $tenant_tree_dn; - 'ldap/role_tree_dn': value => $role_tree_dn; -# 'ldap/tree_dn': value => "dc=example,dc=com"; + 'ldap/url': value => $url; + 'ldap/user': value => $user; + 'ldap/password': value => $password, secret => true; + 'ldap/suffix': value => $suffix; + 'ldap/query_scope': value => $query_scope; + 'ldap/page_size': value => $page_size; + 'ldap/user_tree_dn': value => $user_tree_dn; + 'ldap/user_filter': value => $user_filter; + 'ldap/user_objectclass': value => $user_objectclass; + 'ldap/user_id_attribute': value => $user_id_attribute; + 'ldap/user_name_attribute': value => $user_name_attribute; + 'ldap/user_mail_attribute': value => $user_mail_attribute; + 'ldap/user_enabled_attribute': value => $user_enabled_attribute; + 'ldap/user_enabled_mask': value => $user_enabled_attribute; + 'ldap/user_enabled_default': value => $user_enabled_attribute; + 'ldap/user_attribute_ignore': value => $user_enabled_attribute; + 'ldap/user_default_project_id_attribute': value => $user_enabled_attribute; + 'ldap/user_allow_create': value => $user_allow_create; + 'ldap/user_allow_update': value => $user_allow_update; + 'ldap/user_allow_delete': value => $user_allow_delete; + 'ldap/user_pass_attribute': value => $user_pass_attribute; + 'ldap/user_enabled_emulation': value => $user_enabled_emulation; + 'ldap/user_enabled_emulation_dn': value => $user_enabled_emulation_dn; + 'ldap/tenant_tree_dn': value => $tenant_tree_dn; + 'ldap/tenant_filter': value => $tenant_filter; + 'ldap/tenant_objectclass': value => $tenant_objectclass; + 'ldap/tenant_id_attribute': value => $tenant_id_attribute; + 'ldap/tenant_name_attribute': value => $tenant_name_attribute; + 'ldap/tenant_mail_attribute': value => $tenant_mail_attribute; + 'ldap/tenant_enabled_attribute': value => $tenant_enabled_attribute; + 'ldap/tenant_attribute_ignore': value => $tenant_attribute_ignore; + 'ldap/tenant_domain_id_attribute': value => $tenant_domain_id_attribute; + 'ldap/tenant_allow_create': value => $tenant_allow_create; + 'ldap/tenant_allow_update': value => $tenant_allow_update; + 'ldap/tenant_allow_delete': value => $tenant_allow_delete; + 'ldap/tenant_enabled_emulation': value => $tenant_enabled_emulation; + 'ldap/tenant_enabled_emulation_dn': value => $tenant_enabled_emulation_dn; + 'ldap/tenant_additional_attribute_mapping': value => $tenant_additional_attribute_mapping; + 'ldap/role_tree_dn': value => $role_tree_dn; + 'ldap/role_filter': value => $role_filter; + 'ldap/role_objectclass': value => $role_objectclass; + 'ldap/role_id_attribute': value => $role_id_attribute; + 'ldap/role_name_attribute': value => $role_name_attribute; + 'ldap/role_member_attribute': value => $role_member_attribute; + 'ldap/role_attribute_ignore': value => $role_attribute_ignore; + 'ldap/role_allow_create': value => $role_allow_create; + 'ldap/role_allow_update': value => $role_allow_update; + 'ldap/role_allow_delete': value => $role_allow_delete; + 'ldap/role_additional_attribute_mapping': value => $role_additional_attribute_mapping; + 'ldap/group_tree_dn': value => $group_tree_dn; + 'ldap/group_filter': value => $group_filter; + 'ldap/group_objectclass': value => $group_objectclass; + 'ldap/group_id_attribute': value => $group_id_attribute; + 'ldap/group_name_attribute': value => $group_name_attribute; + 'ldap/group_member_attribute': value => $group_member_attribute; + 'ldap/group_desc_attribute': value => $group_desc_attribute; + 'ldap/group_attribute_ignore': value => $group_attribute_ignore; + 'ldap/group_allow_create': value => $group_allow_create; + 'ldap/group_allow_update': value => $group_allow_update; + 'ldap/group_allow_delete': value => $group_allow_delete; + 'ldap/group_additional_attribute_mapping': value => $group_additional_attribute_mapping; + 'ldap/use_tls': value => $use_tls; + 'ldap/tls_cacertdir': value => $tls_cacertdir; + 'ldap/tls_cacertfile': value => $tls_cacertfile; + 'ldap/tls_req_cert': value => $tls_req_cert; + 'identity/driver': value => $identity_driver; + 'assignment/driver': value => $assignment_driver; } } diff --git a/spec/classes/keystone_ldap_spec.rb b/spec/classes/keystone_ldap_spec.rb index 404f6d618..e2537ee6c 100644 --- a/spec/classes/keystone_ldap_spec.rb +++ b/spec/classes/keystone_ldap_spec.rb @@ -1,20 +1,27 @@ require 'spec_helper' describe 'keystone::ldap' do - - describe 'with default params' do - - it 'should contain default params' do - - should contain_keystone_config('ldap/url').with_value('ldap://localhost') - should contain_keystone_config('ldap/user').with_value('dc=Manager,dc=example,dc=com') - should contain_keystone_config('ldap/password').with_value('None') - should contain_keystone_config('ldap/suffix').with_value('cn=example,cn=com') - should contain_keystone_config('ldap/user_tree_dn').with_value('ou=Users,dc=example,dc=com') - should contain_keystone_config('ldap/tenant_tree_dn').with_value('ou=Roles,dc=example,dc=com') - should contain_keystone_config('ldap/role_tree_dn').with_value('dc=example,dc=com') + describe 'with basic params' do + let :params do + { + :url => 'ldap://foo', + :user => 'cn=foo,dc=example,dc=com', + :password => 'abcdefg', + :user_tree_dn => 'cn=users,dc=example,dc=com', + :user_allow_create => 'False', + :user_allow_update => 'False', + :user_allow_delete => 'False', + } + end + it { should contain_package('python-ldap') } + it 'should have basic params' do + should contain_keystone_config('ldap/url').with_value('ldap://foo') + should contain_keystone_config('ldap/user').with_value('cn=foo,dc=example,dc=com') + should contain_keystone_config('ldap/password').with_value('abcdefg').with_secret(true) + should contain_keystone_config('ldap/user_tree_dn').with_value('cn=users,dc=example,dc=com') + should contain_keystone_config('ldap/user_allow_create').with_value('False') + should contain_keystone_config('ldap/user_allow_update').with_value('False') + should contain_keystone_config('ldap/user_allow_delete').with_value('False') end - end - end