Openstack Keystone与OpenLDAP的集成实践

作者: JAILBREAK(千里之外)

cloudsource版权所有,转载请注明

现有keystone后端对LDAP服务的需求

如果Keystone利用现有的LDAP后端进行账户验证时,LDAP服务需要满足keystone的一些较为严苛的需求,主要几点如下:

  • 用户和租户分别在不同的子树中存储
  • 角色必须作为用户及租户的一个属性记录在相应的子树中
  • 拥有LDAP服务的写权限,并且提前写入各个服务帐号(nova、glance、cinder、neutron、ceilometer等组件)以及各组件所在的服务租户(即service租户)。

实验环境

Openstack环境

用Fuel 5.1.1搭建的Openstack Icehouse 2014.3的标准环境。搭建方法参见博客:Fuel快速安装openstack的实践:第一部分小规模部署多节点,无HA

OpenLDAP环境

在CentOS 6.5上手动部署标准的OpenLDAP服务,并导入了Openstack的用户,租户,角色。

在Linux主机上安装OpenLDAP服务

#find / -name openldap*

/usr/share/doc/openldap-2.4.23

/usr/libexec/openldap

/etc/openldap

使用yum安装:

#yum install -y openldap openldap-servers openldap-clients

配置OpenLDAP服务

#cp /usr/share/openldap-servers/slapd.conf.obsolete /etc/openldap/slapd.conf

#cp /usr/share/openldap-servers/DB_CONFIG.example /var/lib/ldap/DB_CONFIG

#mv /etc/openldap/slapd.d{,.bak}

修改slapd.conf文件

#vim /etc/openldap/slapd.conf

找到:107         by dn.exact=”cn=Manager,dc=my-domain,dc=com” read

改为:107         by dn.exact=”cn=comp,dc=openstack,dc=local” read

设置目录树后缀

找到:115 suffix          “dc=my-domain,dc=com”

改为:115 suffix          “dc=openstack,dc=local”

设置LDAP管理员DN

找到:117 rootdn          “cn=Manager,dc=my-domain,dc=com”

改为:117 rootdn          “cn=cloudsource,dc=openstack,dc=local”

设置LDAP管理员口令(最好使用明文加密后的密码)

找到:121 # rootpw                secret

改为:121 rootpw          password //注意密码前不可以有空格,必须是tab键输入

修改相关文件权限

#chown ldap.ldap /etc/openldap/*

#chown ldap.ldap /var/lib/ldap/*

#mkdir /etc/openldap/cacerts

Reboot机器后,启动OpenLDAP服务

#/etc/init.d/slapd start

OpenLDAP默认网络服务端口为389,也可以修改

slapd -f /etc/openldap/slapd.conf -h ldap://389

新建初始化文件init.ldif

#cat init.ldif

dn:dc=openstack,dc=local

objectclass:dcObject

objectclass:organization

o:cloudsource, Inc.

dc:openstack

dn:cn=cloudsource,dc=openstack,dc=local

objectclass:organizationalRole

cn:comp

导入文件,初始化OpenLDAP服务器

#ldapadd -x -W -D “cn=cloudsource,dc=openstack,dc=cn” -f init.ldif

Enter LDAP Password: [password]

查看结果

#ldapsearch -x -LLL -H ldap:/// -b dc=openstack,dc=local dn

dn: dc=cloudsource,dc=cn

dn: cn=OpenStack,dc=cloudsource,dc=cn [后面会用到]

设置开机自启动,并开放389服务端口

#chkconfig slapd on

#iptables -A INPUT -p tcp –dport 389 -j ACCEPT

#/etc/rc.d/init.d/iptables save

OpenLDAP客户端

客户端有很多选择,这里用最简单的windows客户端软件Softerra LDAP Administrator:

http://softerra-downloads.com/ldapadmin/ldapadmin-4.11.14302.0-x64-eng.msi

实验目的

  1.  紧耦合模式:把Keystone的identity back-end 替换成OpenLDAP,所有的用户、租户、角色信息都由OpenLDAP管理。新用户从LDAP创建,并赋予角色和租户关系,keystone只转发认证。这种模式对LDAP的要求高。
  2.  松耦合模式:把Keystone的identity back-end 替换成OpenLDAP,只有用户信息由OpenLDAP管理,角色和租户信息还是在本地数据库管理。新用户一方面在LDAP创建用户名和密码,同时在keystone端创建角色和租户关系。keystone只转发用户密码认证到LDAP, 角色和租户关系在本地数据库认证。这种模式对LDAP要求低。

紧耦合模式实验步骤

导入Openstack系统用户到OpenLDAP

Openstack服务的帐号要提前在keystone中认证,否则Openstack服务之间不能够互相访问。用keystone命令可以查询出系统帐户:

1-1

导入这些用户到OpenLDAP本文使用了脚本生成ldif文件导入的方法。

#!/bin/shSUFFIX=’dc=openstack,dc=local’LDIF=’/tmp/ldap.ldif’

echo -n > $LDIF

# Make our OUs

echo “dn: ou=Roles,$SUFFIX” >> $LDIF

echo “objectclass:organizationalunit” >> $LDIF

echo “ou: Roles” >> $LDIF

echo “description: generic groups branch” >> $LDIF

echo -e “\n” >> $LDIF

echo “dn: ou=Users,$SUFFIX” >> $LDIF

echo “objectclass:organizationalunit” >> $LDIF

echo “ou: Users” >> $LDIF

echo “description: generic groups branch” >> $LDIF

echo -e “\n” >> $LDIF

echo “dn: ou=Groups,$SUFFIX” >> $LDIF

echo “objectclass:organizationalunit” >> $LDIF

echo “ou: Groups” >> $LDIF

echo “description: generic groups branch” >> $LDIF

echo -e “\n” >> $LDIF

for line in `keystone role-list | awk ‘($4 != “name”) && ($4 != “”) {print $4}’`

do

CN=$line

echo “dn: cn=$CN,ou=Roles,$SUFFIX” >> $LDIF

echo “objectClass: organizationalRole” >> $LDIF

echo “cn: $CN” >> $LDIF

echo -e “\n” >> $LDIF

done

for line in `keystone user-list | awk ‘($4 != “name”) && ($4 != “”) {print $4}’`

do

CN=$line

echo “dn: cn=$CN,ou=Users,$SUFFIX” >> $LDIF

echo “objectClass: inetOrgPerson” >> $LDIF

echo “cn: $CN” >> $LDIF

echo “sn: cookbook” >> $LDIF

echo -e “\n” >> $LDIF

done

for line in `keystone tenant-list | awk ‘($4 != “name”) && ($4 != “”) {print $4}’`

do

CN=$line

echo “dn: cn=$CN,ou=Groups,$SUFFIX” >> $LDIF

echo “objectClass: groupOfNames” >> $LDIF

echo “member: cn=admin,$SUFFIX” >> $LDIF

echo “cn: $CN” >> $LDIF

echo -e “\n” >> $LDIF

done

生成的ldap.ldif文件导入到OpenLDAP Server中

#ldapadd -x -W -D “cn=cloudsource,dc=OpenStack,dc=local” -f ldap.ldif

Enter LDAP Password: [password]

检查LDAP用户名,192.168.30.250 是OpenLDAP服务器地址:

ldapsearch -x -LLL -H ldap://192.168.30.250 -b dc=openstack,dc=local dn

在客户端检查用户

1-2

在OpenLDAP上验证系统用户密码

因为用户都有一个密码域,而以上脚本导入用户的时候没有取得密码。所以密码需要手动输入到用户属性中:

查找系统用的密码:

root@node-29:~# cat /etc/nova/nova.conf | grep password

rabbit_password=rFnSgEhs

neutron_admin_password=WxnyS4oB

admin_password=3jNRMT0t

root@node-29:~# cat /etc/glance/glance-glance-api.conf

glance-cache.conf          glance-registry-paste.ini

glance-api-paste.ini       glance-registry.conf       glance-scrubber.conf

root@node-29:~# cat /etc/glance/glance-api.conf | grep password

# Admin password

#admin_password = None

rabbit_password = rFnSgEhs

qpid_password =

# Server password (string value)

#vmware_server_password = <None>

admin_password = PqUHwb90

root@node-29:~# cat /etc/cinder/cinder.conf | grep password

rabbit_password=rFnSgEhs

admin_password=aPVwuQVg

root@node-29:~# cat /etc/heat/heat.conf | grep password

# Select deferred auth method, stored password or trusts.

#deferred_auth_method=password

# Keystone password for stack_domain_admin user. (string

#stack_domain_admin_password=<None>

# the RabbitMQ password (string value)

#rabbit_password=guest

rabbit_password=rFnSgEhs

#qpid_password=

[auth_password]

# Keystone account password (string value)

#admin_password=<None>

admin_password=fM2Ek2MX

#password=<None>

在OpenLDAP客户端手动更改用户的密码域

1-3

修改keystone.conf配置文件

driver=keystone.identity.backends.ldap.Identity

[ldap]

url=ldap://192.168.50.250

user=cn=cloudsource,dc=openstack,dc=local

password=password

suffix=dc=openstack,dc=local

use_dumb_member=True

dumb_member=cn=cloudsource,ou=Users,dc=openstack,dc=local

allow_subtree_delete=false

user_tree_dn=ou=Users,dc=openstack,dc=local

user_objectclass=organizationalPerson

user_id_attribute=cn

user_name_attribute=sn

user_mail_attribute=mail

user_pass_attribute=userPassword

user_enabled_attribute=userAccountControl

user_enabled_mask=2

user_enabled_default=512

user_attribute_ignore=password,tenant_id,tenants

user_allow_create=false

user_allow_update=false

user_allow_delete=false

tenant_tree_dn=ou=Projects,cn=cloudsource,dc=openstack,dc=local

tenant_objectclass=organizationalUnit

tenant_id_attribute=ou

tenant_member_attribute=member

tenant_name_attribute=ou

tenant_allow_create=true

tenant_allow_update=true

tenant_allow_delete=true

role_tree_dn=ou=Roles,cn=cloudsource,dc=openstack,dc=local

role_objectclass=organizationalRole

role_id_attribute=cn

role_name_attribute=cn

role_member_attribute=roleOccupant

测试Keystone-LDAP认证

1. 命令测试:使用keystone命令,

Keystone user-list

Keystone tenant-list

Keystone role-list

 

  1.  检查服务的权限

Nova list

Neutron net-list

Cinder list

Glance image-list

 

  1.  登录dashboard, 检查服务
  2.  创建新用户。注意LDAP对于keystone是只读的,新用户必须从LDAP端创建。并且,每一个新用户除了属于user组,还需要关联到role和tenant单元中。

松耦合模式实验步骤

对于目前已经拥有成熟LDAP服务的企业及组织,目前 keystone对LDAP的需求过于严苛。对于已有LDAP服务的企业组织,LDAP服务通常已经担负起整个企业组织内部的统一身份验证服务,所以很难 为满足keystone的需求进行大规模结构调整。因此对于这些组织来说,其目标要求,通常仅是需要通过已有LDAP验证,减少组织内部用户的系统验证数 量及复杂程度,并且要保持各个服务之间的独立性。

因此,这类需求的可简要概括如下:

  • 保持OpenStack服务的独立性,即OpenStack的系统服务帐号及某些特定管理员帐号不依赖于已有的LDAP服务
  • 使用只读权限与已有LDAP服务整合,只进行帐号验证

自定义LDAP后端的功能需求

如前面所述,已有LDAP服务的企业组织,对Keystone的需求可以简化如下:

  • Keystone所有的系统服务账号及特定管理员帐号存储于OpenStack的本地MySQL数据库中,不依赖于现有LDAP服务。
  • Keystone中所需要的用户角色及租户信息存储于OpenStack的本地MySQL数据库中,不依赖于现有LDAP。
  • Keystone中所有常规的用户帐号验证通过现有的LDAP服务进行验证。

上述简化的需求既可以保证OpenStack系统各组件服务的独立性,也可以通过现有的LDAP服务进行常规用户的验证,实现企业内部统一的身份验证机制。

具体实现的思路如下:

  • 以现有的SQL验证后端为基础,列出OpenStack各系统服务帐号及特定管理员帐号的列表,在该服务帐号列表中的服务帐号通过SQL验证后端进行本地验证。
  • 在服务帐号列表以外的其他帐号,以现有LDAP后端为基础,仅利用现有LDAP后端中的密码验证部分,实现仅通过LDAP服务进行帐号的密码验证功能。

自定义LDAP后端的验证流程如下:

1) 用户登陆时,自定义后端会通过现有的SQL后端查询账号是否存在。如果不存在直接返回,如果存在继续2);

2) 如果账户存在,查询该账号是否属于系统服务账号。如果是系统账号继续a);

a) 如果账号属于系统账号,则通过SQL后端进行密码验证。密码验证失败,则返回;密码验证成功,则继续4);

b) 如果不是系统账号,而是普通用户账号,继续3);

3) 如果账号存在,并且是普通用户账号,则通过LDAP后端进行密码验证。如果密码验证失败,则返回;如果密码验证成功,则继续4)

4) 继续通过SQL后端进行其他验证步骤。

注意:建议保留一个系统管理员帐号,用以进行必要的初始化配置。本文中保留的系统管理员帐号为“admin”。

具体自定义LDAP后端的示例:

Keystone的各个验证后端均是通过python语言实现,下面的具体的示例代码即是基于现有的SQL验证后端及LDAP后端,由python语言实现。具体示例代码,请参考如下清单:

#!/us/bin/env python

#”””CustomLDAP Identity backend for Keystone on top of the LDAP and SQL backends”””

import ldapfrom keystone

import configfrom keystone

import exceptionfrom keystone.common

import sql

from keystone.common import utils

from keystone.common import ldap as common_ldap

from keystone import identity

from keystone.identity.backends import ldap as ldap_backend

from keystone.identity.backends import sql

CONF = config.CONF

class Identity(sql.Identity):

def _check_password(self, password, user_ref):

username = user_ref.get(‘name’)

if (username in [‘admin’, ‘nova’, ‘glance’, ‘cinder’, ‘heat’, ‘heat-cfn’, ‘neutron’]):

return super(Identity, self)._check_password(password, user_ref)

return ldap_backend.UserApi(CONF).get_connection(ldap_backend.UserApi(CONF)._id_to_dn(username), password)

将自定义LDAP后端CustomLDAP.py复制到/usr/lib/python2.6/site-packages/keystone/identity/backends 目录下。并注意修改该文件的属主及权限信息,保持与其他该目录下的文件保持一致。可通过chown命令修改属主,通过chmod命令修改文件权限。

修改/etc/keystone/keystone.conf配置文件

修改验证后端为自定义LDAP后端CustomLDAP 。

  [identity]
driver = keystone.identity.backends.CustomLDAP.Identity

其余ldap配置的和紧耦合模式的keystone.conf一样。

同步LDAP用户帐号

Openstack系统用户在本地验证,所以我们只需要添加keystone的普通用户到LDAP中。同步帐号有多种方法,比如利用自定义后端中保留的本地admin帐号,可以通过keystone的Rest API利用脚本自动同步,或直接通过admin帐号手工创建。

测试LDAP用户

创建新用户的过程分为两步:

  1.  在LDAP端已存在用户或者创建了用户user1和密码
  2.  在keystone/horizon中创建新用户user1,并且分配tenant和role。注意:密码处可随意填写,在自定义后端中,仅需要MySQL中有该用户存在,并且配置有相应的项目及角色,实际验证时则是通过LDAP验证。因此该处的密码无效。

1-4

  1. 用户user1只能使用LDAP的密码登陆horizon。

总结

通过上面的简单示例,我们可以验证了keystone与LDAP结合的两种方式,以此展现OpenStack的灵活性及或扩展性。当然,本文仅是一个简化的可行性验证,在实际企业组织中,根据组织自身的需求不同,可以自定义一个功能丰富并且完整的验证后端,以实现和 满足企业内部自身的需求。

通过本文,仅希望能够以细微的需求来展现OpenStack作为一个云计算平台的开放性、灵活性以及易扩展性。以期能起到抛砖引玉的效果。

附录一:关于LDAP ldif文件的规范 

  1. 遇见的两个错误及其原因

ldap_add: Invalid syntax (21)

additional info: objectclass: value #0 invalid per syntax

原因:ldif文件中存在空格

ldap_add: Undefined attribute type (17)

additional info: dn: attribute type undefined

原因:should put an empty line in the ldif file between two entries

  1. 正确书写格式:

(1空行)

dn:(空格) dc=mail,dc=kaspersky,dc=com(结尾无空格)

objectclass: (空格)dcObject(结尾无空格)

objectclass: (空格)organization(结尾无空格)

o: (空格)kaspersky(结尾无空格)

dc:(空格) test(结尾无空格)

(1空行)

dn: (空格)cn=test,dc=mail,dc=kaspersky,dc=com(结尾无空格)

objectclass: (空格)organizationalRole(结尾无空格)

cn: (空格)test(结尾无空格)

(结尾无空行)

 

附录二:LDAP备份和导入

数据备份

ldap数据备份的方式有两种:一种是通过ldapsearch ,一种是通过slapcat命令。我这里更倾向于使用后者。因为后都使用不涉及到密码输出等问题。直接一条命令搞定

/usr/sbin/slapcat > /opt/ldap/ldapdbak.ldif

数据的导入

提到导出就不得不提下导入,ldap的导入命令也有两种:一种是通过ldapadd命令,一种是通过slapadd命令。同样,我倾向于使用后者。方法如下:

slapadd -l /opt/ldap/ldapdbak.ldif

 

附录常见错误及解决办法

Authorization Failed: An unexpected error prevented the server from fulfilling your request. ‘NoneType’ object has no attribute ‘lower’ (HTTP 500)

这是一个已知bug, 在Icehouse Keystone 2014.1.3中修复。如果是老版本,可以手动改源代码修复。

https://bugs.launchpad.net/keystone/+bug/1335437

ImportError: No module named ldap

Ubuntu的系统不会默认安装python-ldap,需要手动安装。

root@node-29:~# apt-get install python-ldap

 

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据