OpenLDAP高可用部署实践

达芬奇密码2018-07-19 12:45

背景
在运维Hadoop大数据平台的实践中,我们发现平台在用户和权限管理这两方面的自动化程度较低,对日常的运维效率造成不小的影响。 用户管理主要涉及:1. 新服务器上的用户和组同步;2. 新增用户和组在所有服务器上创建。 权限管理主要涉及权限组(用户组)的创建和频繁变更操作。在此之前平台的用户管理和权限管理都需要走OP工单来完成:SA根据不同的需求,配置Puppet来进行实现不同服务器之间的用户同步,或单独在服务器上新建和变更用户组。
  为了提高平台的运维效率和自动化水平,我们部署了一套基于OpenLDAP的高可用目录服务:作为一套独立于原操作系统账号系统之外的辅助账号系统,平台管理员可以独立完成相应的用户和组管理需求;同时通过定制程序访问该系统,实现目录服务自动化运维。

LDAP简介
目录服务(Directory Service)是一种用于搜索和浏览记录的第二代数据库。和我们经常接触的数据库(第三代)不同,它在特定的应用场景下具有的极佳的可靠性和性能,譬如我们常用的DNS服务,企业的账号系统等。这类服务通常要求系统对大量的查询或搜索操作能做出快速的相应并返回。
  LDAP(Lightweight Directory Access Protocl)
是一个访问目录服务的轻量级协议,其数据模型基于条目(Entry)来实现。条目是若干的属性的集合,其具有全局惟一性标识名(DN)。条目的属性一般有类型名(Type)和多个值(Value)。目录条目以树状的层次结构来进行组织。通过定义条目的特殊属性objectClass,用户可以控制哪些属性是该条目所必须和允许出现的,同时也决定了该条目需要遵循哪些特定的Schema规则。条目的惟一其标识名由本身的名称(相对标识名,RDN)和它的上级条目组合而成的。譬如某个条目的RDN叫"cn=zhangsan",其DN则是 "cn=zhangsan,ou=people,dc=dc=163,dc=org"。 下面是一个简单的账号系统的LDAP树状结构,用来进行说明。

LDAP复制原理
  LDAP通过使用多副本来实现高可用和负载均衡。主控节点和副本之间通过同步复制引擎(syncrepl)来实现:它是一个消费端(Consumer)的复制引擎,可使消费端的服务器持有一份DIT(目录信息树)片段的快照副本。这个syncrepl作为OpenLDAP的一个线程来执行,用于创建和维持一份消费者副本:在初始阶段完成DIT的全量加载,然后定期拉取数据,或在生产者(Provider)内容更新时及时更新本地数据。
  同步机制通过轮询和监听这两种方式来感知数据变更,分别定义了两种同步操作: refreshOnly和refreshAndPersist。 轮询通过refreshOnly操作来实现:消费者通过定期发起查询请求,轮询生产者的内容,其拷贝在轮询结果返回后和生产者的拷贝保持一致。监听通过refreshAndPersist操作来实现:消费者先发起一个查询,拿到返回的结果;与此同时生产者会将该查询请求持久化,后续的生产者中的数据更新,会转换成额外的条目更新操作,发送到消费者。
  Syncrepl支持Push和Pull两种同步机制。在默认的refreshOnly模式下,生产者使用Pull的方式进行同步,即消费者不需要与生产者保持连接且不需要保存历史信息:消费端周期性发起轮询请求,生产者从这些请求的cookie中获取相应的数据完成处理,并返回。
Syncrepl利用同步协议中的当前态和删除态,避免进行频繁的全量重载;同时生产者还维护了一个会话日志作为历史存储。这样可以使Pull方式同步更有效。
在refreshAndPersist模式的同步中,生产者使用Push方式进行同步。生产者需要和消费者保持通讯,当生产者的内容被更新后,就定期给消费者发送更新。

其复制结构图如下所示:



  OpenLDAP(http://www.openldap.org/)是LDAP的一个开源实现,在上面的复制机制之上提供了增量复制(Delta-Syncrel)特性。其在消费者上维护了一个数据库用来存储可选深度的日志变更信息。消费者检查变更日志确认那些信息需要更新,然后将其应用到自己的数据库。如果其与生产者的同步记录差距太大,则使用默认Syncrepl机制来同步,最后再切换到增量模式。
  关于LDAP的详细介绍,可以参考《OpenLDAP Admin Guide》
安装部署
  介绍完了LDAP的相关背景知识,我们下面开始进行环境的部署(以Debian系统为例)。执行以下命令,安装OpenLDAP的程序和相关脚本:

apt-get install slapd ldap-utils db-util

安装过程中,需要输入LDAP的Admin的密码以及BaseDN等信息,用于后续的配置。BaseDN用 dc=ldap,dc=163,dc=org 来代替。 安装完毕后,我们可以用以下的命令进行测试

ldapsearch -x -LLL -H ldap:/// -b dc=ldap,dc=163,dc=org dn


配置Provider
  实现LDAP的复制架构需要先选一台服务器作为主控节点或生产者。这里以 ldap01.163.org 为例配置Provider。
创建一个LDIF文件,文件名为 provider.ldif ,如下:

# Add indexes to the frontend db.
dn: olcDatabase={1}hdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: uid eq
-
add: olcDbIndex
olcDbIndex: uidNumber eq
-
add: olcDbIndex
olcDbIndex: memberUid eq
-
add: olcDbIndex
olcDbIndex: gidNumber eq
-
add: olcDbIndex
olcDbIndex: entryCSN eq
-
add: olcDbIndex
olcDbIndex: entryUUID eq
 
#Load the syncprov and accesslog modules.
dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: syncprov
-
add: olcModuleLoad
olcModuleLoad: accesslog
 
# Accesslog database definitions
dn: olcDatabase={2}hdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcHdbConfig
olcDatabase: {2}hdb
olcDbDirectory: /var/lib/ldap/accesslog
olcSuffix: cn=accesslog
olcRootDN: cn=admin,dc=ldap,dc=163,dc=org
olcDbIndex: default eq
olcDbIndex: entryCSN,objectClass,reqEnd,reqResult,reqStart
 
# Accesslog db syncprov.
dn: olcOverlay=syncprov,olcDatabase={2}hdb,cn=config
changetype: add
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
olcSpNoPresent: TRUE
olcSpReloadHint: TRUE
 
# syncrepl Provider for primary db
dn: olcOverlay=syncprov,olcDatabase={1}hdb,cn=config
changetype: add
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
olcSpNoPresent: TRUE
 
# accesslog overlay definitions for primary db
dn: olcOverlay=accesslog,olcDatabase={1}hdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcAccessLogConfig
olcOverlay: accesslog
olcAccessLogDB: cn=accesslog
olcAccessLogOps: writes
olcAccessLogSuccess: TRUE
# scan the accesslog DB every day, and purge entries older than 7 days
olcAccessLogPurge: 07+00:00 01+00:00

然后执行元数据的更新,执行以下命令:
sudo ldapadd -Q -Y EXTERNAL -H ldapi:/// -f provider.ldif
最后重启LDAP服务。
配置Consumer
配置Consumer的过程与Provider类似,也需要安装LDAP的相关包和工具。安装完毕后,新建一个 consumer.ldif 的文件,如下:

# Add indexes to the frontend db.
dn: olcDatabase={1}hdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: uid eq
-
add: olcDbIndex
olcDbIndex: uidNumber eq
-
add: olcDbIndex
olcDbIndex: memberUid eq
-
add: olcDbIndex
olcDbIndex: gidNumber eq
-
add: olcDbIndex
olcDbIndex: entryCSN eq
-
add: olcDbIndex
olcDbIndex: entryUUID eq

dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: syncprov
 
dn: olcDatabase={1}hdb,cn=config
changetype: modify
add: olcSyncRepl
olcSyncRepl: rid=0 provider=ldap://ldap01.163.org bindmethod=simple binddn="cn=admin,dc=ldap,dc=163,dc=org"
 credentials=secret searchbase="dc=ldap,dc=163,dc=org" logbase="cn=accesslog"
 logfilter="(&(object schemachecking=on
 type=refreshAndPersist retry="60 +" syncdata=accesslog
-
add: olcUpdateRef
olcUpdateRef: ldap://ldap01.163.org

再执行以下命令更新元数据配置,不需要进行重启。

sudo ldapadd -Q -Y EXTERNAL -H ldapi:/// -f consumer.ldif

更新后进行执行以下命令验证:

ldapsearch -z1 -LLLQY EXTERNAL -H ldapi:/// -s base -b dc=ldap,dc=163.org,dc=org contextCSN
dn: dc=ldap,dc=163,dc=org
contextCSN: 20120201193408.178454Z#000000#000#000000


TLS认证
  当所有的LDAP服务器位于同一个机房且访问者也来自内网时,可以使用默认的安全级别(无加密验证)。但现实情况是我们需要将LDAP的数据备份或复制到异地机房,然后由这这些LDAP服务器来提供异地机房的目录访问需求。OpenLDAP提供了强认证和数据安全服务(Simple Authentication and Security Layer), 其实现基于Cyrus SASL,支持DIGEST-MD5,EXTERNAL和GSSAPI等加密方式,同时使用TLS提供基于证书的认证和数据安全(完整性和机密性)服务。
  因为SSL仅在公司内部系统使用,所以使用自建的证书系统来完成证书创建和签名配置。下面是自建证书的具体步骤:
1. 使用openssl自建,也可以从默认的/etc/ssl/openssl.cnf拷贝一份,做以下修改:

[ CA_default ]
dir           = ./myCA              # Where everything is kept
certs          = $dir/certs            # Where the issued certs are kept
database        = $dir/index.txt        # database index file.
new_certs_dir     = $dir/newcerts         # default place for new certs.
serial        = $dir/serial           # The current serial number
certificate     = $dir/ca/cacert.pem    # The CA certificate
private_key     = $dir/ca/cakey.pem     # The private key
string_mask     = default                # support printable


2. 生成rootCA
openssl req -x509 -new -newkey rsa:2048 -config ./openssl.cnf -days 3650 -subj "/C=CN/ST=Zhejiang/L=Hangzhou/O=NetEase/OU=DevOps/CN=Hadoop" \
-out myCA/ca/cacert.pem -keyout myCA/ca/cakey.pem

这条命令会创建一个自签名的X.509证书,有效期是10年。同时生成证书的秘钥文件cakey.pem,证书文件是cacert.pem。 RootCA用于后面对多个服务器的证书进行签名验证。

3. 使用keytool生成服务器的密钥和证书,也可以继续使用openssl来创建。
keytool -genkeypair -alias ambari -keyalg RSA -keysize 2048 -validity 365 -keypass keypass -storepass storepass -keystore keystore  \
-dname "CN=ldap01.163.org, OU=DevOps, O=NetEase, L=Hangzhou, ST=Zhejiang, C=CN"

这条命令使用java的keytool为其中一个主机生成一个key,其密码为keypass。 因其是ldap01.163.org的服务器证书,故CN必须带上服务器的完整域名(FQDN)。客户端证书的DN能可以直接作为认证DN使用。

4. 生成签名请求csr
keytool -certreq -alias ambari -file csr.pem -keypass keypass -keystore keystore -storepass storepass
上面的命令生成ldap01.163.org服务器证书签名请求

5. 认证csr
openssl ca -config ./openssl.cnf -days 3650 -in csr -out cer
# 将转换为X.509 PEM格式
openssl x509 -in crt -out crt.pem -outform PEM

上面的命令使用之前的RootCA来验证ldap01.163.org的证书签名,并将验证结果转换成PEM格式输出

6. 生成truststore
keytool -importcert  -alias ambari -keystore truststore -keypass keypass -storepass storepass -file crt.pem
上述命令将经过RootCA签名后的证书转换为授信证书。

至此完成了服务器ldap01.163.org服务器的密钥和信任证书,将其上传到相应的服务器路径上即可。 另外LDAP服务的其他消费机器也需要如上操作完成对应的证书制作,然后将这些证书传输到服务器上。

证书完成后,需要在服务端和客户端都开启TLS。 客户端的名称必须是CA信任的名称,服务端需要CA证书和服务器自己的证书的私钥。

开启TLS支持

开启TLS需要变更元数据库,还是新建一个 tls.ldif 的文件,内容如下:

dn: cn=config
add: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/ldap/ssl/cacert.pem
-
add: olcTLSCertificateFile
olcTLSCertificateFile: /etc/ldap/ssl/server3_crt.pem
-
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/ldap/ssl/server3_key.pem

然后执行以下命令,完成元数据库更新。

sudo ldapmodify -Y EXTERNAL -H ldapi:/// -f tls.ldif

此外消费者在开启TLS的操作之外,还需要变更olcSyncrepl属性,新建一个文件 consumer_tls.ldif :

dn: olcDatabase={1}hdb,cn=config
replace: olcSyncRepl
olcSyncRepl: rid=0 provider=ldap://ldap01.163.org bindmethod=simple
 binddn="cn=admin,dc=163,dc=org" credentials=secret searchbase="dc=163,dc=org"
 logbase="cn=accesslog" logfilter="(&(object> schemachecking=on type=refreshAndPersist retry="60 +" syncdata=accesslog
 starttls=critical tls_reqcert=demand

额外的参数申明了消费端的LDAP服务器必须使用StartTLS方式来启动,可以看到rid=0,并且CA证书必须要求是Provider本机的身份。
执行以下命令后,重启slapd进程。

ldapmodify -Y EXTERNAL -H ldapi:/// -f consumer_tls.ldif

TLS生效后可以在Provider这边可以看到对应的记录。
性能调优
  LDAP服务的性能会受到所在服务器的内存多寡、磁盘性能和网络速率影响,除去硬件因素,其目录结构设计和应用场景也会影响服务性能。这些方面一旦确定下来,那么我们还可以从目录条目索引、数据库缓存等方面来优化我们的服务。另外还可以增加服务器的备份数,对查询请求进行负载均衡。
 LDAP还支持一种Overlay的方式,在前端和后端数据库之间添加代理缓存引擎的方法来提升查询性能。它首先判断查询请求是否包含在已经缓存的过滤器中:已存在的请求会从代理缓存服务器的本地数据库直接返回。其他请求则会传递至下层引用的ldap或者元数据后端来处理。缓存使用LRU替换策略来管理,后台会定期清除过期的查询缓存。其基本的请求过程如下:



要开启ProxyCache功能,需要新建一个LDAP服务器,按照之前的安装命令执行即可。 安装完成后,新增一个 proxy.ldif 文件,配置如下:

dn: cn=module{1},cn=config
objectClass: olcModuleList
cn: module{1}
olcModulePath: /usr/lib/ldap
olcModuleLoad: back_ldap

dn: cn=module{1},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: pcache

dn: olcDatabase={2}ldap,cn=config
objectClass: olcDatabaseConfig
objectClass: olcLDAPConfig
olcDatabase: {2}ldap
olcSuffix: cn=hadoop
olcRootDN: cn=admin,dc=ldap,dc=163,dc=org
olcDbURI: "ldap://ldap02.163.org"

dn: olcOverlay={0}pcache,olcDatabase={2}ldap,cn=config
objectClass: olcOverlayConfig
objectClass: olcPcacheConfig
olcOverlay: {0}pcache
olcPcache: hdb 100000 2 1000 3600
olcPcacheAttrset: 0 cn gidNumber sn uidNumber uid
olcPcacheAttrset: 1 cn gidNumber memberUid
olcPcacheTemplate: "(cn=)" 0 3600
olcPcacheTemplate: "(&(objectClass = )(uid=))" 0 3600
olcPcacheTemplate: "(&(objectClass = )(memberUid=))" 1 3600
olcPcacheTemplate: "(&(objectClass = )(uidNumber=))" 0 3600
olcPcacheTemplate: "(&(objectClass = )(gidNumber=))" 1 3600
olcPcacheTemplate: "(&(objectClass = )(|(memberUid=)(member=)))" 1 3600

dn: olcDatabase={0}hdb,olcOverlay={0}pcache,olcDatabase={2}ldap,cn=config
objectClass: olcHdbConfig
objectClass: olcPcacheDatabase
olcDatabase: {0}hdb
olcDbDirectory: /var/lib/ldap/pcache
olcDbCacheSize: 20
olcDbIndex: uid,memberUid pres,eq,sub
olcDbIndex: uidNumber,gidNumber pres,eq

然后执行命令,完成元数据的修改即可。

ldapmodify -Y EXTERNAL -H ldapi:/// -f  proxy.ldif

关于Proxy Cache更详细的说明,可以参考Proxy Cache Paper
LDAP的管理相关操作在这里不做赘述。

参考资料
https://help.ubuntu.com/lts/serverguide/openldap-server.html
http://www.zytrax.com/books/ldap/apd/#entrycsn
https://wiki.debian.org/LDAP/OpenLDAPSetup
https://www.howtoforge.com/how-to-install-openldap-server-on-debian-and-ubuntu
https://gist.github.com/bodgit/93ae33caecdee6da4890
http://bacedifo.blogspot.com/2013/07/openldap-ldap-backend-as-proxy.html
http://seanlook.com/2015/01/22/openldap_ldif_example/
https://datacenteroverlords.com/2012/03/01/creating-your-own-ssl-certificate-authority/


本文来自网易实践者社区,经作者金川授权发布。