天天看点

安全镜像高可用的OpenLDAP集群

安全镜像高可用的OpenLDAP集群

原文:http://www.grantcohoe.com/blog/2013/01/03/secure-mirroring-highly-available-openldap-cluster/

背景

我的组织在OpenLDAP目录服务器中存储用户信息,这包括联系信息,性格喜好,iButton的标识等。目录服务器不存储用户密码,并依赖于一个Kerberos KDC通过SASL提供身份验证。这是一个广受支持和很受欢迎的配置,因为可以实现SSO(单点登录),并保存密码在你的目录中。我们的客户通过LDAP身份验证访问机器,基本方法是基于PAM使用由用户提供的凭据绑定到LDAP服务器。如果绑定成功后,用户成功登录;如果没有,提示信息给用户。这个项目之前,我们有一台服务器运行CentOS5.8/OpenLDAP2.3的目录服务器。如果这台服务器坏掉,那么所有的用户数据都将丢失,没有人可以登录到任何其他的服务器(或网站,如果配置了斯坦福大学的WebAuth。查看本作更多的指导)。显然,这是一个需要纠正的一个问题。

技术

LDAP同步

OpenLDAP的2.4引入了被称为“mirrormode”的复制方法,这使您可以使用自己已有的syncrepl协议同步多台服务器之间的数据。以前(<=2.3的OpenLDAP)只允许你做主从式的复制,其中slave是只读的,不能修改。显然,这存在一些局限性,如果master(或OpenLDAP-ish中的“provider”)死了,那么你的用户无法做任何修改,直到master恢复。使用MirrorMode,您可以创建一个“N-way Multi-Master”的拓扑结构,允许所有服务器进行读/写,并立即复制到其它机器。

即使其中一台服务器死了,为了使用户能够持续访问LDAP,我们需要一种方法来保持数据包流动,并自动故障转移到辅助服务器。 Linux有一个叫做“Heartbeat”的包,具有这种功能。如果你熟悉Cisco的HSRP或开源的VRRP,它们的工作方式相同。服务器A被分配一个IP地址(例如10.0.0.1 ),服务器B被分配一个不同的地址(例如10.0.0.2 ) 。Heartbeat被配置以提供第三个IP地址( 10.0.0.3 ),将始终在两者之间保持可用。在master服务器上,以太网别名以虚拟IP地址创建。定期心跳( keep-alive包)被发送到其他服务器,表示“我还活着! ” 。如果这些消息消失(当服务器死机等) 时,辅助主机会注意到无法更新,并自动对自身创建一个类似的别名接口并分配虚拟IP地址。这种方法允许给你的客户端提供一台连接的主机,但是它实际上可在多个机器之间无缝切换。

SASL认证

配置MirrorMode的简单方法需要您存储复制的DN的凭据以明文方式配置在文件中。因为你是存储密码的明文,显然这不是很安全。因此,我们可以使用主机的Kerberos主体(principal)绑定到LDAP服务器作为复制DN并且执行所有我们需要做的任务,这比明文好多了!

SSL

如果要获得安全,我们应该使用LDAPS(LDAP + SSL)来获得我们的数据。我们将用我们的PKI来设置此功能,将此作为我们要确保的核心功能中的首要工作。

新服务器

我准备了两台新机,我们称他们为“warlock.example.com”和“ldap2.example.com”。他们每个都跑我最喜欢的Scientific Linux企业版Linux6.2,但用的是OpenLDAP2.4。 在2.3和2.4之间你不能做MirrorMode。

配置

首先我们需要安装所需的软件包。

    openldap (LDAP libraries)

    openldap-servers (Server)

    openldap-clients (Client utilities)

    cyrus-sasl (SASL daemon)

    cyrus-sasl-ldap (LDAP authentication for SASL)

    cyrus-sasl-gssapi (Kerberos authentication for SASL)

    krb5-workstation (Kerberos client utilities)

    heartbeat (Failover)

如果你不想使用最小安装,您可能同时需要安装openssh客户端和vim。有些套件可能不包含在你的基本发布包中,可能需要其他库(EPEL等)。

Kerberos Keytabs

每个主机都需要有自己的一套主机/和LDAP主体(principals),同时共享一个作为虚拟地址。最后,你需要具有下列主体(principals)的keytab :

    host/[email protected]

    ldap/[email protected]

    host/[email protected]

    ldap/[email protected]

警告:每次你写一个randkey'd主体至keytab的时候,它的KVNO(密钥版本号)会增加,从而使以前所有写入的主体无效。您需要合并共享的主体到每个主机的keytab。请参考我的Kerberos的工具指南,用于执行这些操作。

把这个文件存为etc/krb5.keytab,并在其上设置ACL,使LDAP用户可以读取它。

[[email protected] ~]# chmod 640 /etc/krb5.keytab

[[email protected] ~]# setfacl -m u:ldap:r /etc/krb5.keytab

[[email protected] ~]#

如果你看到这样的“Kerberos error 13”或“get_sec_context:13”错误,这意味着无法读取keytab,通常是LDAP服务器,尝试修复它。

NTP

Kerberos和OpenLDAP的同步需要同步时钟。如果您尚未设置,在继续之前按照我的指导在系统上设置NTP。

SASL

您需要在两个LDAP服务器上运行saslauthd。如果你还没有这个设置,在继续之前按照我的指南配置SASL认证。

日志

我们将使用Syslog和Logrotate为LDAP守护进程(slapd)管理日志文件。默认情况下它会输出local4。这个配置取决于你的系统,这里我使用默认配置。为slapd添加一个条目到您的rsyslog配置文件(通常是/etc/rsyslog.conf)。

local4.*                /var/log/slapd.log

现在,除非我们告诉logrotate滚动这个文件,否则它会变得非常大,并导致很多问题。我创建了一个配置文件,系统会根据系统默认设置自动滚动此日志文件。配置文件为/etc/logrotate.d/slapd:

/var/log/slapd.log {

    missingok

}

然后重新启动rsyslog。 Logrotate会运行cron任务,并且不需要重新启动。

数据目录

因为我从我的旧LDAP服务器抓取数据,我不会设置新的数据目录。在旧服务器和新master上,数据目录是/var/lib/ldap/。我只是从旧的服务器scp所有的内容到此目录。如果你这样做,我建议停止旧服务器片刻,以确保在执行过程中没有发生变化。在scp所以东西后,一定要改变这些东西的文件属性(chown)给LDAP用户。我还建议运行slapindex,以确保所有数据被索引。

现在要配置客户端库,使其方便地测试新的配置,编辑你的/etc/openldap/ldap.conf中的URI,并添加证书设置。

BASE            dc=example,dc=com

URI             ldaps://ldap.example.com

TLS_CACERT      /etc/pki/tls/example-ca.crt

TLS_REQCERT     allow

当在服务器上配置该文件时,TLS_REQCERT必须设置为ALLOW,因为我们要通过LDAPS做Syncrepl。很明显,因为我们使用的是共享的证书ldap.example.com,它不能匹配的服务器主机名就会失败。在所有的客户端上,都理所当然需要证书,否则在这种情况使我们无法通过LDAPS完成Syncrepl。

LDAPS证书

具有安全需求,OpenLDAP具有TLS通道内的会话的能力。这种工作方式和HTTPS一样。要做到这一点,你需要有生成基于给定的CA SSL证书的能力。此过程依据组织机构不同而各异。不管怎样,你所选择的主机名是至关重要的,因为主机名将被验证。在这种设置中,我们创建了一个“ldap.example.com”的主机名,所有LDAP客户端将配置使用。在两个主机上生成“ldap.example.com”主机名的相同SSL证书,并放置在每个服务器上。

我存放公有证书/etc/pki/tls/certs/slapd.pem, 私有证书/etc/pki/tls/private/slapd.pem, 以及CA证书/etc/pki/tls/example-ca.crt. 在获取这些文件后,我检查他们确保他们会实际工作:

[[email protected] ~]# openssl verify -CAfile /etc/pki/tls/example-ca.crt /etc/pki/tls/certs/slapd.pem

/etc/pki/tls/certs/slapd.pem: OK

[[email protected] ~]#

你需要允许LDAP帐户读取私有证书:

[[email protected] ~]# setfacl -m u:ldap:r /etc/pki/tls/private/slapd.pem

服务器配置

如果您使用的是以前的服务器配置,只需使用scp复制整个的/etc/openldap代码到新的服务器。在复制之前,确保你移走可能要拷贝进来的任何文件或目录。如果你不这样做,你可能需要做一些设置。OpenLDAP2.4使用配置的新方法被称为“cn=config”,它是存储在LDAP数据库中的配置数据。然而,由于这不是完全支持大多数客户端,我仍使用配置文件。要创建一个全新的安装,请参考我在此的其他文章之一。 (仍然在写这篇文章的过程中)

下面的SSL证书指令需要放置在你的slapd.conf中:

TLSCertificateFile /etc/pki/tls/certs/slapd.pem

TLSCertificateKeyFile /etc/pki/tls/private/slapd.pem

TLSCACertificateFile /etc/pki/tls/example-ca.crt

根据您的系统,您可能需要配置运行在LDAPS://上的守护进程。在Red-Hat系统中,这些是在/etc/sysconfig/ldap中。

为了Syncrepl功能需要,您需要给你的配置添加两件东西。首先在你的slapd.conf中:

# Syncrepl ServerID

serverID 001

# Syncrepl configuration for mirroring instant replication between another

# server. The binddn should be the host/ principal of this server

# stored in the Kerberos keytab

syncrepl rid=001

provider=ldaps://ldap2.example.com

type=refreshAndPersist

retry="5 5 300 +"

searchbase="dc=example,dc=com"

attrs="*,+"

bindmethod=sasl

binddn="cn=ldap1,ou=hosts,dc=example,dc=com"

mirrormode TRUE

该serverID值必须唯一地标识该服务器。请确保您在配置中加入辅助服务器,同样需要修改Syncrepl RID。

其次,你需要使复制的binddn具有完全读取所有对象的权限。这应该在你的ACL文件中控制(在slapd.conf中)。

access to *

        by dn.exact="cn=ldap1,ou=hosts,dc=example,dc=com" read

        by dn.exact="cn=ldap2,ou=hosts,dc=example,dc=com" read

警告:如果你没有一个现有的ACL设置,这样做只是防止非授权的任何修改。这些指令的目的是要添加到现有的ACL中。

做好以上全部设置并准备启动服务器。        

[[email protected] ~]# service slapd start

Starting slapd:                                            [  OK  ]

[[email protected] ~]#

确保当你做SASL绑定时,服务器正确地报告您的DN。通过下面命令验证这一点:

ldap1 ~ # su ldap -s /bin/bash

bash-4.1$ kinit -k -t /etc/krb5.keytab host/ldap1.example.com

bash-4.1$ ldapwhoami -Q

dn:cn=ldap1,ou=hosts,dc=example,dc=com

bash-4.1$

该DN是一个应该在你的ACL中设置,并且有权限访问任何东西。

由于LDAP用户总是需要这个主体(principal),我建议增加一个cronjob,以保持ticket活着。我写了一个脚本,它会更新您的ticket以及修复一个SELinux的权限管理问题。你可以在这里得到它,只要把它放到/usr/local/sbin/目录下。可能会有更好的方法,但是这个脚本也工作得挺好。

0 */2 * * * /usr/local/sbin/slaprenew

防火墙

不要忘记为LDAP和LDAPSSL开放389和636端口。

搜索

配置两台主服务器和辅助服务器后,我们现在需要使辅助服务器接收复制的初始数据。假设你不喜欢这样做,从旧服务器复制数据目录,你的master是唯一一个有真实数据的机器。启动这台机器上的服务,如果一切按计划顺利进行,你应该能够搜索到对象,以验证它工作正常。

[[email protected] ~]# kinit user

Password for [email protected]:

[[email protected] ~]# ldapsearch -h localhost uid=user uidNumber -Q -LLL

dn: uid=user,ou=users,dc=example,dc=com

uidNumber: 10046

[[email protected] ~]#

如果出现错误,提高调试级别(-d 1),并输出详细错误。

复制

依照如上所述在做kinit LDAP用户(host/ldap2.example.com)后,启动辅助LDAP服务器。如果你用tail跟踪slapd的日志文件,你应该可以看到发生的非常快的复制事件。这是服务器从在slapd.conf中指定的提供者处装载它的数据。一旦完成,请以上面类似的方式尝试搜索服务器。

[[email protected] ~]# tail /var/log/slapd.log

slapd[15759]: syncrepl_entry: rid=001 be_search (0)

slapd[15759]: syncrepl_entry: rid=001 uid=user,ou=Users,dc=example,dc=com

slapd[15759]: syncrepl_entry: rid=001 be_add uid=user,ou=Users,dc=example,dc=com (0)

slapd[15759]: syncrepl_entry: rid=001 LDAP_RES_SEARCH_ENTRY(LDAP_SYNC_ADD)

slapd[15759]: syncrepl_entry: rid=001 inserted UUID 84ba3544-5be8-102b-9a32-4dfcdb785320

slapd[15759]: syncrepl_entry: rid=001 be_search (0)

slapd[15759]: syncrepl_entry: rid=001 uid=user,ou=Users,dc=example,dc=com

slapd[15759]: syncrepl_entry: rid=001 be_add uid=user,ou=Users,dc=example,dc=com (0)

slapd[15759]: syncrepl_entry: rid=001 LDAP_RES_SEARCH_ENTRY(LDAP_SYNC_ADD)

slapd[15759]: syncrepl_entry: rid=001 inserted UUID 84c04164-5be8-102b-9a33-4dfcdb785320

slapd[15759]: syncrepl_entry: rid=001 be_search (0)

心跳(Heartbeat)

心跳为在多个Linux系统之间的配置资源提供故障转移(failover)。在这种情况下,我们将提供高可用性的一个别名IP地址(10.0.0.3),将此IP提供给客户端作为LDAP服务器(ldap.example.com)。 Heartbeat配置非常简单,只需要三个文件:

在/etc/ha.d/haresources中包含我们将故障转移的资源,这个配置在两台服务器上应该是相同的。

ldap1.example.com IPaddr::10.0.0.3/24/eth0:1/10.0.0.255

上面主机的名称不是“ldap.example.com”,究其原因是因为该条目必须是在配置文件(下面)中列出的一个节点。

在/etc/ha.d/authkeys中包含用于保护心跳报文的共享密码,这是为了防止有人未授权而连接一台服务器,并声称自己是群集的一部分。

auth 1

1 md5 mysupersecretpassword

请务必执行命令chmod,确保不是所有人都可读(推荐600)。

最后,/etc/ha.d/ha.cf是Heartbeat的配置文件,它应该包含每一个集群中的服务器、计时器和日志文件条目。

mcast           eth0 225.0.0.1 694 1 0

auto_failback   on

keepalive 1

deadtime 5

debugfile       /var/log/ha-debug.log

logfile         /var/log/ha.log

logfacility     local0

udp             eth0

node            ldap1.example.com

node            ldap2.example.com

在这些所有设置好之后,启动heartbeat服务。过一会,你应该看到另一个以太网接口显示为您的master。要测试故障转移,拔掉一台机子,看看会发生什么!

DNS Hack

使LDAP服务器响应共享IP地址的副作用是,它无法知道真正主机名。因此你需要编辑你的/etc/hosts文件,列出ldap.example.com名称对应的每个实际的主机IP地址。在这个例子中,ldap1是10.0.0.1、ldap2是10.0.0.2。你应该修改hosts文件,包括:

10.0.0.1 ldap.example.com ldap

10.0.0.2 ldap.example.com ldap

第一个条目应该是本地系统的地址(在ldap2案例中10.0.0.2应该在第一个)。

您的DNS服务器应该能够做到正向和反向查找ldap.example.com对应10.0.0.3(高度可用的地址)。

如果您有服务器之间绑定或者来自客户端绑定的问题,它通常是由于DNS的问题。

测试

如果一切按计划顺利进行,你应该可以在slapd的日志文件中看到同步(Sync)消息。

slapd[11059]: slapd starting

slapd[11059]: do_syncrep2: rid=002 LDAP_RES_INTERMEDIATE - REFRESH_DELETE

同样,如果您登录到客户机(在我的案例中,我做了这个KDC),你应该只能够在ldap.example.com主机上搜索,其他的将返回错误。

[[email protected] ~]# ldapsearch -Q -LLL uid=user displayName -h ldap.example.com

dn: uid=user,ou=users,dc=example,dc=com

displayName: Firstname Lastname (user)

[[email protected] ~]# ldapsearch -Q -LLL uid=user displayName -h ldap1.example.com

ldap_sasl_interactive_bind_s: Invalid credentials (49)

        additional info: SASL(-13): authentication failure: GSSAPI Failure: gss_accept_sec_context

[[email protected] ~]# ldapsearch -Q -LLL uid=user displayName -h ldap2.example.com

ldap_sasl_interactive_bind_s: Invalid credentials (49)

        additional info: SASL(-13): authentication failure: GSSAPI Failure: gss_accept_sec_context

[[email protected] ~]#

该原因是,客户端从不同的视角识别服务器的主机名是什么。这种差异导致SASL

崩溃,而不让你绑定。

错误

[[email protected] ~]# ldapsearch -h ldap.example.com uid=user

SASL/GSSAPI authentication started

ldap_sasl_interactive_bind_s: Invalid credentials (49)

        additional info: SASL(-13): authentication failure: GSSAPI Failure: gss_accept_sec_context

[[email protected] ~]#

在服务器上显示的错误:

slapd[9888]: GSSAPI Error: Unspecified GSS failure.  Minor code may provide more information (Wrong principal in request)

slapd[9888]: text=SASL(-13): authentication failure: GSSAPI Failure: gss_accept_sec_context

这意味着你的DNS没有配置或失去功能,按照说明修复它。

LDAP日志显示:

slapd[1901]: do_syncrepl: rid=005 rc -2 retrying

slapd[1901]: slap_client_connect: URI=ldap://ldap2.example.com ldap_sasl_interactive_bind_s failed (-2)

并且 /var/log/messages 包含:

GSSAPI Error: Unspecified GSS failure.  Minor code may provide more information (Credentials cache permissions incorrect)

你需要改变你的KRB5CC文件的SELinux上下文(默认是Scientific Linux上的/tmp/krb5cc_$UIDOFLDAPUSER),以便slapd进程可以读取。由于每次重新初始化票据,可能造成权限的违约风险,我推荐使用上面cronjob中的脚本。如果有人发现一个更好的方式能做到这一点,请让我知道!

如果您的客户端上启用SSL后,您会收到(使用-d 1):

TLS: can't connect: TLS error -5938:Encountered end of file.

检查服务器证书文件,可能是你设置的路径不正确。

总结

现在你有两个独立运行OpenLDAP2.4的LDAP服务器,并且它们之间可以进行数据同步。通过监听一个心跳服务IP地址,他们的目的是即使其中一台服务器死掉,也将始终保持可用。你也可以绑定SASL到每个服务器,以避免在您的配置文件中存储口令。

如果看完本文后你觉得有什么可以改善的或不清晰的地方,请随时与我联系!您也可以下载在http://archive.grantcohoe.com/projects/ldap上的示例配置文件。

继续阅读