linux下ldap新手使用入门
2012-06-11 17:24:21 阿炯

摘要:本文将讨论LDAP以及在我们所喜爱的操作系统——linux上实现LDAP。已经有很多关于LDAP的文献,所以我不想在这里再重复什么,我不准备讨论高级的LDAP概念、计划以及第二版和第三版LDAP的区别等等。事实上,我对这类问题也所知不多。相反地,我将尝试用简单明了的语言解释什么是LDAP, 它能给我们带来的好处以及我们如何使用它。

我不是一个LDAP专家,我只是一个LDAP的初学者,本文介绍的是我用LDAP做了些什么以及如何做,我不会使你再像以前的我一样感到迷惑。

问题:既然你是个LDAP初学者,为什么还要写一篇关于LDAP的文章?
最近我在一个项目上需要一位同事的帮助。这个项目的基础就是LDAP。我的同事可以在perl、邮件服务器等等方面帮助我,但是他对LDAP一无所知,事实是每一次他想要深入学习LDAP, 反而会对LDAP越来越迷惑。 因为该项目的基础是LDAP, 所以我给了这位同事半小时来学习LDAP,结果是一切反而变得清晰明了了。原因很简单,LDAP是在前端的,只需要集中一点点注意力或者一些生动的例子就可以了。在本文中我也正想这样做。

LDAP是什么?
LDAP是轻量目录访问协议(Lightweight Directory Access Protocol)的缩写,其实是一种目录服务,类似于我们在文件系统中所使用的目录,类似于我们查询电话号码使用的电话号码簿,类似于我们所使用诸如 NIS(Network Information Service)、DNS (Domain Name Service)等网络目录,也类似于你在花园中所看到的树木。

LDAP是一种特殊的数据库,但是LDAP和一般的数据库不同,明白这一点是很重要的。 LDAP对查询进行了优化,与写性能相比LDAP的读性能要优秀很多。一般地,目录服务提供什么样的服务呢?通常是根据查询的标准返回一定的信息。

实例
NIS目录
ypcat passwd
这将从NIS数据库返回用户名、密码、用户id等信息。
ypmatch atif passwd
返回用户atif的密码。
DNS目录
nslookup www.freeoa.net
返回www.freeoa.net的ip地址。
nslookup -type MX freeoa.net
返回主机名符合freeoa.net的MX记录信息。
LDAP目录
(我们将在下面详细阐述)
ldapsearch uid=aghaffar
返回关于用户aghaffar的所有公开信息。这和find / -uid aghaffar unix命令很类似。
ldapsearch uid=aghaffar mail
返回用户ughaffar的邮件信息

目录基础或根
在上面我们所提到的任何一种目录服务中都有一个我们开始浏览或搜索的开始点, 这个开始点就是通常所谓的根,这和一棵数的根也很类似,每棵都有一个根,以及很多的树枝树叶。
* 文件系统的根是 /
* NIS 的根是域名,比如 "freeoa.net"
* DNS 根是Internic(译者注:Internet网络信息中心,Internet的管理组织)
* LDAP同样有一个可定义的根,比如 "o=freeoa.net" ,这里o表示组织每个根都可以衍生出好多枝叶(正如同你邻居的花园中的树木一样), 对于文件系统来说,它的枝叶就是一个个文件及子目录,每一个枝叶都有一些属性。比如文件系统的枝叶(文件及子目录)有以下的属性:
* 名称
* 修改时间
* 所有者
* 组(译者注:所有者所在的组)
* 等等

下面是对一个LDAP目录讨论。
区分名(DN,Distinguished Name)
和自然界中的树不同,文件系统/LDAP/电话号码簿目录的每一片枝叶都至少有一个独一无二的属性,这一属性可以帮助我们来区别这些枝叶。在文件系统中, 这些独一无二的属性就是带有完整路径的文件名。比如/etc/passwd,该文件名在该路径下是独一无二的。当然我们可以有/usr/passwd, /opt/passwd,但是根据它们的完整路径,它们仍然是唯一的。

类似于DNS系统的FQDN正式域名,FQDN也是唯一的。在LDAP中,一个条目的区分名称叫做“dn”或者叫做区分名。在一个目录中这个名称总是唯一的。比如我的dn是"uid=aghaffar, ou=People, o=developer.ch"。不可能有相同的dn,但是我们可以有诸如"uid=aghaffar, ou=Administrators, o=developer.ch"的dn。这同上面文件系统中/etc/passwd 和 /usr/passwd的例子很类似。

我们有独一无二的属性,在"ou=Administrators, o=developer.ch" 中uid和在"ou=People, o=developer.ch"中的uid,这并不矛盾。

LDAP服务器
现在市场上有很多LDAP服务器,大多数都可以在linux上运行。本文将介绍openLDAP的使用。我为什么选择openLDAP,为什么你应该选择openLDAP?
* openLDAP是开放源码的
openLDAP的官方网站是http://www.openldap.org。你可以下载其源代码包自己编译,或者看看你的linux发行版是否已经包含了该软件包。如果已经包含了就可以安装预先编译好的版本从而少花费些力气。

构建LDAP目录
下面我们将介绍设置一个LDAP服务器的步骤。
步骤:
* 下载并安装openLDAP
* 配置 LDAP server
* 配置本地环境指向LDAP 安装
* 初始化LDAP 数据库
* 查询 LDAP
* 添加/修改 LDAP 条目

配置LDAP服务器
在我们的例子中,我将为freeoa.net构建LDAP服务器。你可以用你喜欢的编辑器编辑slapd.conf和ldap.comf配置文件来更改名称等参数以适应你的具体需求。在我的服务器上配置文件在/etc/openldap目录下,你的配置文件可能在/usr/local/etc/openldap或者别的地方,这要根据你的linux发行版本或者编译openldap的具体情况而定。
################# /etc/openldap/slapd.conf ###########################
# 下面的部分是我的suse linux 发行版本预先定义的
# 我们设置的部分在本文的第二和第三部分
  include /etc/openldap/slapd.at.conf
  include /etc/openldap/slapd.oc.conf
  schemacheck off
  pidfile /var/run/slapd.pid
  argsfile /var/run/slapd.args
  #####################################
  # ldbm database definitions
  #####################################
  # 定义使用的数据库类型。 缺省是ldbm
  database ldbm
  # 后缀或者根. 这是你LDAP目录的顶节点
  suffix "o=freeoa.net"
  # LDAP的dbs 保存的位置
  directory /var/lib/ldap
  # 目录管理员的区分名
  rootdn "cn=Manager, o=freeoa.net"
  # 保存ldap目录管理员的明文密码是很糟糕的,但是我们将在刚刚开始使用ldap时这样做
  rootpw secret
  # 这就是所有的一切

编辑你的 /etc/openldap/ldap.conf配置文件
该文件位于ldap客户端(我们将使用同一计算机作为服务器和客户端,当然这可以在同一计算机或不同的计算机上),通常诸如ldapdelete 、ldapadd等等的ldap客户端会读该文件的内容。
  ##########/etc/openldap/ldap.conf#########
  # LDAP 的缺省设置
  # 查看 ldap.conf(5) 可以获取更多的信息
  # 本文件应该设置为对所有人可读
  # 定义ldap服务器,可以用主机名或ip地址
  host 127.0.0.1
  # 定义我们要查询的目录的根
  # 我们将要使用的顶节点,这不一定是目录的根,比如我们可以使用
  # base = ou=users, o=freeoa.ch
  # 这时我们的一切查询都将从树根o=freeoa.net的分支开始
  现在启动ldap服务器。如果你使用SuSE预先编译好的openldap服务器,你可以通过下面的命令启动ldap服务
  /etc/rc.d/ldap start
  在redhat,该命令为:
  /etc/rc.d/init.d/ldap start
  如果你是使用缺省设置自己编译并安装的,你可以用/usr/local/libexec/slapd &启动ldap服务器。如果没有使用缺省设置,请找到slapd文件并运行它。

在新安装的LDAP服务器上添加数据
到现在为止,你的ldap服务器已经运行起来了,可以准备添加数据了。最标准的往ldap服务器中添加数据的方法是建立一个LDIF(LDAP目录交换格式)文件,你可以通过阅读man ldif来获得更多关于ldif的信息。

简单说来,ldif是ldap条目的文本表示,这些条目是很好读懂的,并且可以在来自两个不同的厂家的LDAP服务器间交换数据,哪怕使用的是不同的数据库后台或者是运行在不同的操作系统上。

是的,还有其他方法。我对为什么不使用XML替代LDIF感到很惊讶。就让我们一起来建立ldif文件,而不要感到任何的忙乱,一些应该记住的要点:
ldif 文件中的每一记录/条目都应用一个空行分开,空格是很重要的,"Atif Ghaffar" 和"Atif Ghaffar " 是完全不同的,下面是一个ldif文件freeoa.net.ldif
  dn: o=freeoa.net
  o: freeoa.net
  objectclass: top
  objectclass: organization
  dn: ou=editors, o=freeoa.net
  ou: editors
  objectclass: organizationalUnit
  dn: uid=aghaffar, ou=editors, o=freeoa.net
  uid: aghafar
  cn: Atif Ghaffar
  sn: Ghaffar
  givenname: Atif
  objectclass: person
  userpassword: yIvSBWSuLs2N2
  mailacceptinggeneralid: aghaffar@freeoa.net
  ou: editors
  dn: uid=mkempe, ou=editors, o=freeoa.net
  uid: mkempe
  cn: Magnus Kempe
  sn: Kempe
  givenname: Magnus
  objectclass: person
  userpassword: clearpass
  mailacceptinggeneralid: mkempe@freeoa.net
  maildrop: mkempe@developer.ch
  preferredlanguage: fr
  ou: editors

下面我们将这些数据加入ldap目录。我们使用命令行程序ldapadd
ldapadd -D "cn=Manager, o=freeoa.net" -w secret < freeoa.net.ldif

该命令使用“cn=Manager, o=freeoa.net”作为管理员的区分名(dn),使用secret作为密码,然后从freeoa.net.ldif文件中读取数据并写入ldap目录。如果一切正常的话你可以准备开始查询你的ldap目录了。为我的邮件服务器考虑,我希望一切正常。在继续之前,让我们一行一行的来检查一下这个ldif文件。
 1. 这一行定义顶级记录的区分名(dn),这将是目录树的根,这是必须定义的。
 2. 这一行我们定义组织(o),并赋值为“freeoa.net”。
 3. 这一行定义这个对象的对象类,我们定义为top。
 4. 定义对象的类型(这里是组织对象)。
 5. 空行为分割符。
 6. 定义组editors(这是freeoa editors分支),举一反三,我们也可以根据不同的目的来定义其他的分支,比如hosts和data分支。
 7. 明确定义组织单元(ou)editors的属性。属性是可以被用来查询的条目,比如,如果你想要查询所有属于editors的用户,你可以这样查询 “show all dn where ou=editors”。如果我们没有定义属性,则这条记录将不符合查询条件。
 8. 定义对象类(组织单元)。
 9. 空行为分割符。
 10. 定义属于组editors的用户aghaffar的区分名。
 11. 定义该用户的uid(要保证其是唯一的)。
 12. 定义该用户的cn(普通名字),比如,我喜欢将我的名字写成“Firstname Lastname”,而有的人可能喜欢“Lastname Firstname”.
 13. 姓
 14. 名
 15. 对象类(人)
 16. 用户密码(这里是加密的密码)。这串字符表示密码是用加密算法加密过的。剩下的部分是用“yI”加密的密码"yIvSBWSuLs2N"。
 17. 定义我的邮件地址。
 18. 定义我所属于的组织单元(组editors)
 19. 空行为分割符。
 20. 定义另一个用户mkempe
 21. 普通名字
 22. 姓
 23. 名
 24. 对象类
 25. 用户密码:注意这里我们使用明文密码。你可以为不同的用户使用不同的方案。这是根据每一条目定义的,而不是基于数据库定义的。因此一个用户可能使用明文密码,第二个可能使用加密密码,另一个则可能使用SHA认证方式。
 26. 邮件地址。
 27. maildrop:定义用户邮箱的真实位置。在这个例子中,这个服务器接受收件人为mkempe@freeoa.net的邮件,然后邮件服务器会 查询ldap服务器“有接受收件人为mkempe@freeoa.net的邮箱吗?”ldap服务器就会返回maildrop属性值。接着邮件服 务器就会将该邮件发给那个邮箱。
 28.母语:这是我们一个额外的属性,这个属性定义了用户的母语。我们可以利用存储在中央ldap服 务器的信息来为改用户提供更好的服务。比如,直接向该用户显示本页的法语版本。请注意前面一条记录没有母语、maildrop等属性,这也是ldap强大 的一个方面——不必象一个数据库中的表格那样拥有固定的结构。你可以在一条记录中定义3条属性,而在另一个中定义30条属性。
 29. 组织单元。

查询ldap数据库
让我们找出关于用户mkempe的所有数据
ldapsearch uid=mkempe
找出editors的所有区分名
ldapsearch '(&(objectclass=person)(ou=editors))' dn
更多的例子可以参考ldapsearch的man手册。

LDAP的好处
让我们来看一看迁移到LDAP所带来的好处。
LDAP是一个开放的标准。你将使用的大多数的新的应用程序都能够查询ldap数据库,甚至windows2000使用LDAP作为他的目录服务。信息的集中化将会带来巨大的利益,单点管理,减少错误,减少数据复制等等。

LDAP应用实例
如果我是康柏公司的推销员,我可以测试向你销售LDAP并告诉你可以用它来作为“联系人管理”,但是我不受雇于康柏公司,所以我会尝试向你介绍LDAP有趣的一些方面。
同一登录系统的单一数据源,用户帐号放置在一个集中的地方。你可能会使用一个LDAP目录来管理你的用户,放置用户的密码及其他信息,而不仅仅是存放在/et/passwd文件中。Windows/Unix/Mac用户都可以使用这些信息。

建议:你可以使用LDAP信息来进行用户认证,而不是用shadow或nis等等方式。
建议:你可以写一个小web程序来让用户不用登录到系统就可以更改他的存放在LDAP中的unix密码。这时你将需要使用pam_ldap, 可以参考资源列表中关于pam_ldap的url.

注意:
统一登录系统的单一数据源!=同一登录系统
大多数LDAP销售商在向你销售LDAP时,会告诉你如果你使用LDAP,你就可以拥有一个同一登录系统解决方案。其实这只说对了一半。同一登录系统并不是什么新玩艺,只不过时IT经理的噱头而已。

同一登录系统的实例:
* 你用"aghaffar"登录工作站
* 你访问公司的内部网,内部网中的web页面是密码保护的,你不用登录就可以访问,因为www服务器可以识别你的身份。
* 你开始运行一个程序,比如SAP, 再一次地,你不需要密码,因为系统已经拥有了你的身份和信息。
* 等等

当然你可以使用LDAP作为用户数据源,而在几个不同的应用程序间进行会话管理的小戏法就是所谓的“统一登录系统”了,要知道这和LDAP是风马牛不相及的。其实这可以用LDAP, NIS, NT域控制器帐号,数据库,平面文件实现的。

建议:你可能想要建立用户邮件帐号但是不想建立unxi帐号。没问题,我正在使用一个LDAP+Postfix 邮件服务器+ Cyrus IMAP/POP服务器的复合服务器来管理成千上万的用户,当然不用建立相应的系统帐号。

建议:你可能想要将不同应用程序的信息集中化。比如,将Netscape的首选项、书签等信息存入LDAP,并且用户可以将这些信息从一部计算机移动到 另一部,从LDAP服务器下载他/她的信息。这个用户可以从Windows NT Netscape 迁移到Linux/Solaris/Macintosh Netscape的同时还使用同样的用户信息(对不起,又提到了Microsoft,我知道这倒了你的胃口)。

情景:我憎恶老是在web和书面上填充我的个人信息。我不知道为什么人们老是想知道我的年龄、生日、办公室地址,即使已经告诉过他们。在我以前谋职的公司里,我们不得不建立庞大的表格,表格的信息75%都是一样的(姓名、主题、通信地址、楼层、管理员名字、部门)。

建议:比如,如果我的电话出故障了,我给你的唯一信息是“我的电话出故障了”。

资源列表:
PAM LDAP Module
pam_ldap 模块为Solarish和linux工作站提供LDAP目录认证,以及更改其在目录中的密码。

openLDAP
OpenLDAP 项目是一个协作开发强健的、商业等级的、具有丰富特性的、并且是开放源码的LDAP应用程序和工具的项目。该项目由一个全世界志愿者组成的社团管理,该社团利用Internet来交流、计划和开发OpenLDAP 软件和文档。

Java based excellent LDAP Browser/Editor
LDAP 浏览器/编辑器提供用户友好界面LDAP目录编辑工具。由JFC (SwingSet) 和JNDI 类库写成。可以连接第二和第三版LDAP 服务器。

kldap
kldap 是一个KDE的LDAP客户端(浏览器) 。你可以象你熟悉的Novell Administrator那样浏览LDAP目录。

LDAP理论精炼总结
1、LDAP是一种目录服务,特殊的数据库,数据读取速度高于写入速度;LDAP做了读取的优化,读取速度优于普通关系数据库。
2、不支持事务(transactions),不能回滚(rollback);LDAP一次事务只能修改一个条目。
3、服务器-客户端模式
4、基于条目(entries)
·条目由属性组成
·条目具有全局唯一标识DN(distinguished name),类似域名
·属性由类型(Type)组成,每个类型对应一个或多个值
·类型描绘了该信息是什么信息(电话,住址……)
·值是真实信息,文本类型
·Attributes have a syntax which specifies what type of
·data - see Schema later on

5、条目以树形结构组织
LDAP中的DN(Distinguished Names)
1、由本层名称连接全部上级名称组成,之间用逗号分隔。
例如:ou=sina,dc=com,dc=cn
2、最左边的部分叫做相对DN(RDN:relative distinguished name);剩下的叫做Base DN
RDN:ou=sina
Base DN:dc=com,dc=cn
3、在同一个Base DN下,RDN必须唯一;这很好理解,为了保证条目DN唯一。
LDAP的Schema,ObjectClass属性
1. Schema是一组定义如何存储数据的规则,可以保证数据一致性,降低重复数据,并保证应用程序可以有一个统一的数据接口。
2. 条目的Object Class属性定义了条目需要遵守的Schema。
Schema包括:
·必须的属性
·全部允许的属性
·属性如何比较
·限定属性的类型
3. ObjectClass属性定义了条目必须的和全部允许的属性;
4. 一个条目可以有多个ObjectClass属性,必须属性和可选属性是全部ObjectClass的全集;
5. Objectclass可以继承;
6. 不能有多继承,不能重写规则。

LDAP中常见属性
属性包括:
·名称(name):唯一标识,不区分大小写
·Object identifier(OID):用逗号分隔的整数序列
·属性语法:定义属性值的类型和如何比较
·属性可以有一个或多个值

常见属性名称缩写:
uid User id
cn Common Name
sn Surname
l Location
ou Organizational Unit
o Organization
dc Domain Component
st State
c Country

设定openldap的log输出
首先需要了解openldap的loglevel,slapd的日志文件级别,注意把你想要的项逐次相加之和就是loglevel.
# man slapd.conf
loglevel <integer> [...]

Specify the level at which debugging statements and operation statistics should be syslogged (currently logged to the syslogd(8) LOG_LOCAL4 facility). They must be considered subsystems rather than increasingly verbose log levels. Some messages with higher priority are logged regardless of the configured loglevel as soon as some logging is configured, otherwise anything is logged at all. Log levels are additive, and available levels are:
1 (0x1 trace) trace function calls
2 (0x2 packet) debug packet handling
4 (0x4 args) heavy trace debugging (function args)
8 (0x8 conns) connection management
16 (0x10 BER) print out packets sent and received
32 (0x20 filter) search filter processing
64 (0x40 config) configuration file processing
128 (0x80 ACL) access control list processing
256 (0x100 stats) stats log connections/operations/results
512 (0x200 stats2) stats log entries sent
1024 (0x400 shell) print communication with shell backends
2048 (0x800 parse) entry parsing
4096 (0x1000 cache) caching (unused)
8192 (0x2000 index) data indexing (unused)
16384 (0x4000 sync) LDAPSync replication
32768 (0x8000 none) .ly messages that get logged whatever log level is set

The desired log level can be input as a single integer that combines the (ORed) desired levels, both in decimal or in hexadecimal notation, as a list of integers (that are ORed internally), or as a list of the names that are shown between brackets, such that
loglevel 129
loglevel 0x81
loglevel 128 1
loglevel 0x80 0x1
loglevel acl trace

are equivalent. The keyword any can be used as a shortcut to enable logging at all levels (equivalent to -1). The keyword none, or the equivalent integer representation, causes those messages that are logged regardless of the configured loglevel to be logged. In fact, if no loglevel (or a 0 level) is defined, no logging occurs, so at least the none level is required to have high priority messages logged.

设置/etc/openldap/slapd.conf
添加:
#setup log
loglevel 256

设置/etc/syslog.conf
添加:
# save OpenLDAP log
local4.* /var/log/ldap/ldap.log

# mkdir /var/log/ldap
# touch /var/log/ldap/ldap.log
# chown -R ldap.ldap /var/log/ldap
# service ldap restart
# service syslog restart

安装sudo-ldap
建议屏蔽root的 ssh登录。把/etc/ssh/sshd_config 里的 PermitRootLogin yes 改成no。既然把帐号集中管理 那么安全是首要的。如果把sudo的配置文件放在 local 维护也就失去了架设ldap的意义了。迁移sudoers到 ldap 可按照如下步骤。

把 include /etc/ldap/schema/schema.OpenLDAP 加入到slapd.conf 里面。dpkg -L sudo-ldap 可以列出所有这个软件包的文件分布。这里我们需要把上面提到的schema.OpenLDAP 复制到/etc/ldap/schema 目录下。我们需要一个类似migrationstools的工具,这个工具需要正确的环境变量 SUDOERS_BASE。我们通过/usr/share/doc/sudo-ldap/sudoers2ldif 这个工具生成sudouser.ldif。

SUDOERS_BASE=ou=SUDOers,dc=linux,dc=loc
export SUDOERS_BASE
./sudoers2ldif /etc/sudoers > /tmp/sudoers.ldif
按照如下内容编辑 sudobase.ldif 并用ldapadd导入到ldap数据库,别忘记把sudoers.ldif同样导入到ldap数据库。
代码:
dn: ou=SUDOers,dc=linux,dc=loc
objectClass: top
objectClass: organizationalUnit
ou: SUDOers
把 index sudoUser eq 加入到slapd.conf 里面
#/etc/init.d/slapd stop 停下 slapd
#/etc/init.d/slapd start 启动 slapd
保证/etc/nsswitch.conf 里面有 sudoers:ldap files 这样系统才会让ldap认证sudo用户的合法性。
ldap 主从服务的实现
如果LDAP服务器出现意外,也就意味着所有用户无法登陆。因此保有备用LDAP服务器也就成了必须了。自从ldap2.4 开始不再支持slurpd 旧的配置文件不再有效了。

请在LDAP Maser 的slapd 加入如下代码。
overlay syncprov
syncprov-checkpoint 100 10
syncprov-sessionlog 100

请在LDAP Slave 的slapd 加入如下代码。
syncrepl rid=123
provider=ldap://master:389
type=refreshOnly
interval=01:00:00:00
searchbase="dc=linux,dc=loc"
filter="(objectClass=*)"
scope=sub
attrs="*"
schemachecking=off
bindmethod=simple
binddn="cn=syncuser,dc=linux,dc=loc"
credentials=secret
用户 syncuser 要有足够的权限对数据进行读写。
interval=01:00:00:00 是同步备用LDAP服务器的时间间隔。

用到的软件包
ldap服务器: slapd, phpldapadmin, migrationtools, php5, apache2, openssl, sudo-ldap
ldap客户机:ldap-utils, libnss-ldap, libpam-ldap, sudo-ldap

软件包描述
slapd 是ldap服务的主程序主要的配置文件为 /etc/ldap/slapd.conf, /etc/default/slapd
migrationtools 是把诸如 /etc/passwd, /etc/group 等转换成 ldif数据 可供ldap 读取数据。由于ldap数据格式ldif枯燥难懂 有了靠个工具生成ldif 将会事半功倍。
php5, 和 apache2 是为了phpldapadmin 预装的。本文不详细尽述。
openssl 是为了给ldap服务器和客户端加密通讯用的。
sudo-ldap
ldap-utils 是ldap客户端软件包,主要配置文件为 /etc/ldap/ldap.conf