背景
IDaaS产品中非常重要的一个能力,就是支持LDAP Server的对接,完成账号同步和账号登录委托认证能力,主要解决以下三个问题:
- 基于用户提供LDAP Server配置和凭据信息,作为LDAP Client从LDAP Server中同步用户企业的组织架构和人员账号信息;
- 支持同步的人员账号能够通过LDAP委托认证流程,完成企业员工的SSO登录(基于LDAP SSO登录到IDaaS);
- 从LDAP Server同步企业目录信息后,IDaaS作为源(IDaaS随后扮演权威身份中心进行账号的统一管理),再写回变动的组织架构和人员账号信息到LDAP Server中,保证企业目录信息的一致性;
对LDAP的初步认识
什么是LDAP(Lightweight Directory Access Protocol):顾名思义,一个轻量级的目录访问协议,用来访问目录服务(Directory Service)。
为什么是轻量级,因为还有一个X.500 Directory Access Protocol。
X.500 was hard on both the systems (large footprint) and network (bandwidth intensive).LDAP solved these problems by allowing for authentication and authorization of users to servers, files, and applications while reducing overhead, bandwidth use, and demand on endpoints.
那么LDAP协议作为一个访问协议,它在技术架构的表现上是一个CS架构模式(如下图所示,图片来源Okta):以IDaaS为例,IDaaS产品要解决的三个问题,都是会作为LDAP Client去访问LDAP Server(这里LDAP Server和Directory Service等同了),来实现功能。
另外一个角度来看,LDAP是要访问目录服务,肯定是需要查询和操作数据,那这个Server中的数据是什么样的?如下图所示,一个树形组织的数据信息,图片来自于JumpCloud的Blog:What is LDAP?,
从这个图片中,可以引申出几个专有术语(完整LDAP术语看这里):
- DIT,Directory Information Tree:LDAP Server中的整个树被称为目录信息树,DIT;
- DSA,Directory Server Agent:代指这个LDAP Server服务;
- Entry:条目,树中的每个节点,都被称为一个条目,使用属性来描述现实世界的物品(item);(可以理解成RDMS数据库中的一行)
- Attribute:一个条目会包含多个属性,代表了这个条目的部分信息;(可以理解成RDMS数据库中的一行的某列数据)
- Root DSE:Directory Server Agent Specific Entry,叫Root DSE,一个目录服务中的特殊条目,提供了整个目录的信息;
- DN,Distinguished Name:条目的唯一标识,LDAP协议都是基于这个唯一标识来查询和操作数据;(可以理解成在整个RDMS数据库中的一行的逻辑主键,基于数据库的逻辑主键,下图来源JumpCloud)
- RDN,Relative Distinguished Name:就是一个条目的名字,如ou、o、dc、cn、uid等;多值的RDN,是指多个名称组合起来才称为这个条目的名字;(可以理解成RDMS数据库中的一行的逻辑主键,基于单个表的逻辑主键,下图来源JumpCloud)
- Base DN:你要操作的目录树的某个具体子树的DN;
对LDAP从四个方面的理解
基于上面的简单认识,我们可以把LDAP比喻成RDMS(SQL),都是用来存储数据的,都只是一种规范形式,只是LDAP和RDMS两者的表现形式不一样。另外一个,如同RDMS具有Mysql、Oracle、PG等实现,LDAP具体到不同的厂商产品实现会在某些细节上表现出差异,如OpenLDAP Server和AD。
以下主要从四个方面来理解LDAP协议:
- 目录服务存储的数据构成规则:我们要通过LDAP协议操作的数据在Server中的构成规则和格式是怎么样的?
- 连接和认证;我们作为Client如何连接到Server,并认证通过可以进行对应的操作;
- LDAP的两类操作:查询数据和修改数据;
- LDIF,The LDAP Data Interchange Format,LDAP协议文本交换数据格式(Optional);
LDAP目录服务存储的数据构成规则
存储数据的服务,不管是KV数据库、RDB等,都会具备对应的规范,对于LDAP来说,也就是Schema,对应的Schema信息可以从Root DSE中获取。
上文提到整个DIT中的每个节点都是一个Entry,那么从Entry开始来理解对应的LDAP Schema。
Object Class
首先,每个Entry都是一个Object Class的一个具体实例,用来标识这个Entry是一个什么类型的对象,以及用来要求这个Entry必须具备一些什么样的属性。
我们对接LDAP要解决的第一个问题就是同步企业组织架构和人员信息,而组织机构和人员类型的Entry在LDAP对应的Object Class就是OrganizationalUnit(组织机构)和Person(人员)。
- 基础信息:
a. Object Identifier,OID:在LDAP广泛使用,唯一定义一个元素的存在,Person的OID就是2.5.6.6;
b. Objectclass name:可选,一个用来代替OID的名称,如person;
c. Description:可选,人类可读描述信息
d. Objectclass kind(Category):这个Class是什么类型的,主要分为三种:Abstract、Structural、Auxiliay。 - Must Attributes:这个Object的实例Entry必须要包含的属性;
- May Attributes:这个Object的实例Entry可能包含的属性;
- SuperClasses:这个Object class继承的超类;
- SubClasses:这个Object class的子类;
Object Category和Super Class、Sub Class这三个内容是紧密关联的:
- 首先每个Object Class都具备一种类型,只有Structural Class才可以实例化一个Entry,然后每个Structural Class都是继承一个Abstract Class,最顶级的Class 是Top;
- Auxiliay Class 是辅助类,不能初始化实例,可以被继承,用来规定某些必须包含的属性;
类比来讲,Top这个Class相当于Java的Object,所有的Object Class都是继承Top,然后Abstract Class类似于Java中的抽象类,不能初始化对象,而Auxiliary Class类似于Java的中的接口Interface,也不能初始化对象,只能用来定义某些Object Class包含的属性;只有Structural Class才可以初始化实例,然后可以集成Abstract Class、Structural Class、Auxiliary Class;
Attribute Type
在Object Class中提到了Must Attributes 和 May Attributes,也就是这个Object的Entry必须和可能包含的属性有哪些?那么每个属性也是具备自己的类型,包含了存储什么类型的数据,怎么存储等等;
一样的格式,Attribute Type中也由几部分信息组成:
- 基础信息:OID、Name、Description等;
- 可选的字符串描述:single-value标识这个属性在单个Entry只能由一个;
- Attribute Syntax:对应的Attribute的数据格式类型等,有Directory String、IA5 String、Printable String、Boolean、Integer、Numeric String、Octet String、DN、Telephone Number、Postal Address、Authentication Password等等;
- Matching Rule:属性的匹配规则,是否支持 == 判断,支持排序、支持subString;
- SuperType 和 Subtypes:意味着Attribute Type也是支持继承的。出于两个考量:
a. 一个是允许一些Attribute Type从另一个Attribute Type继承一些属性。
b. 另外一个就是能够把一些Attribute Type进行分组,比如说针对name这Attribute Type,具有cn、sn、c、l、ou、title、givenName等等子属性,这样我们去检索的时候,指定(name=zhangsan),就能够获取所有name以及子属性为zhangsan的条目。
在ObjectClass中定义的属性,一般被称为User attributes,也就是用来提供给LDAP Client使用和存储的属性。除此之外,LDAP还额外定义了两种类型的属性:Operational Attributes和Collective Attributes,目的应用于特定场景。简单理解下Operation Attributes是用来提供给LDAP Server在内部使用,通常不认为是Client访问的Entry信息的一部分。Collective Attributes具体详情可以参考RFC 3671,目前对应的参考文档中都没有详细的描述,同时看到我们去做对接时也并不需要关心。
Client可以通过显示指定方式获取到Operation Attributes,但是不能修改它。目前LDAP定义的Operational Attributes有如下几个具体属性:
Attribute name | Type | Category | Description |
---|---|---|---|
createTimestamp | DIRECTORY OPERATION | Standard | RFC2252: time which object was created |
modifyTimestamp | DIRECTORY OPERATION | Standard | RFC2252: time which object was last modified |
creatorsName | DIRECTORY OPERATION | Standard | RFC2252: name of creator |
modifiersName | DIRECTORY OPERATION | Standard | RFC2252: name of last modifier |
hasSubordinates | DIRECTORY OPERATION | Standard | X.501: entry has children |
ref | DISTRIBUTED OPERATION | Standard | RFC3296: named reference - a labeledURI |
entryUUID | DIRECTORY OPERATION | ApacheDS | UUID of the entry |
entryDN | DIRECTORY OPERATION | ApacheDS | DN of the entry |
entryCSN | DIRECTORY OPERATION | ApacheDS | Change sequence number of the entry |
nbChildren | DIRECTORY OPERATION | ApacheDS | The number of children for this entry |
nbSubordinates | DIRECTORY OPERATION | ApacheDS | The number of subordinates for this entry |
entryParentId | DIRECTORY OPERATION | ApacheDS | Attribute holding the id of parent entry |
对于我们来讲,日常会着重关注的Operational Attributes有entryUUID和entryDN,entryDN属性就是这个条目的DN,但是由于DN可以被修改,因此我们唯一定位一个Entry的时候是使用entryUUID。具体到IDaaS场景的话,也就是我们会使用entryUUID作为一个User的ExternalId(外部主键),这样子我们去同步账户的时候,即使这个账户被移动了OU,我们也是可以通过再次同步保持和LDAP一致组织结构。
连接和认证
每个协议都会存在自己的连接方式和端口,LDAP也不例外,我们可以通过LDAP协议连接到Server,对应的端口和Http(Https)一样,支持两个389和636(默认端口),分别是LDAP和LDAPs;
LDAP类似于Http协议,就是明文格式的数据传输;LDAPs类似于Https协议,也就是LDAP over SSL,基于SSL/TLS就行对应的加密;除此外,在LDAP的基础上,又规定了在LDAP Session中,可以发送一个StartTLS请求,把普通的LDAP Session升级成基于TLS的加密会话;
总结来讲,LDAP去连接的时候就会存在三种分支:一个是LDAP协议,一个LDAP+StartTLS,一个是LDAPs;后面两种涉及到SSL/TLS加密,就会存在对于证书验证相关的问题,这也是为什么很多产品连接配置中会存在输入证书指纹的选项,因为很多证书是自签的。
LDAP的认证支持多种方式,分别是Simple Bind和SASL(Simple Authentication And Security Layer)其中Simple Bind由分为:Anonymously Authentication、Unauthenticated Authentication;Name/password Authentication。
我们作为Client访问LDAP Server的时候都是使用Name/passsword方式。为什么说是Name/password方式,因为LDAP Client的另外一个身份是LDAP Server的用户类型的数据条目,对OpenLDAP来讲就是Object Class为Person的条目,对AD来讲就是Object Class为User的条目。而Name属性的表现形式为这个待认证User的DN。
基于Simple Bind + LDAP(LDAPs),我们就能够连接到LDAP Server了,而这样的一个认证请求,被称为是LDAP Bind Request。对应来讲,解决我们背景提到的第二个委托认证问题,就是使用指定员工账号在LDAP Server中的DN,发起一个Bind Request,如果认证通过,那么就代表这个员工登录的账号密码正确,就可以完成LDAP到IDaaS的SSO了。
LDAP的两类操作
查询Search
一个Search操作通过指定查询条件来查询对应条目的部分或者全部信息。一个标准LDAP search request包含的元素有多个,详细信息参考LDAP Search Operation,我们主要关注如下四个元素:
- Search Base DN:前面提到,整个LDAP Server存储的是一棵树,我们要查询符合条件的数据,可以从Root DSE 根节点进行检索,也可以通过DN指定到某个节点,只查询对接节点所代表的子树,缩小查询目标大小,提高检索性能。
- Search Scope:检索范围(我更想表达为检索方式,Base DN + Search Scope圈定了对应Search Request的目标查询对象),有四种检索范围:
a. baseObject(base):只查询Base DN这个单个节点,看下这单个节点是否满足查询条件;
b. singleLevel(one):查询Base DN的一级子节点,不包含Base DN,也不包含一级子节点下的其它子孙节点;
c. wholeSubTree(sub):查询Base DN代表的整颗子树,包含Base DN和所有的子孙节点;
d. subordinateSubtree(subordinates):查询Base DN代表的整颗子树,但是不包含Base DN,只包含子孙节点; - Search Filter:查询的过滤条件,用来确定在指定的查询范围(Base DN + Scope)中的目标Entry。具体的Filter表达式语法可以参考这篇文档,下文中也会针对具体Case进行说明。
- Required Attributes:顾名思义,这个是描述了要求Server应该返回的Entry属性集合,比如说cn、objectClass等属性。除了显式指定要求属性外,LDAP还定义了属性要求的两个特殊字符,"" 和 "+",分别代表要求所有的user attributes和operational attributes。如果一个Search Request指定的Required Attributes 集合为空,那么Server应该把对应的取值当成""处理,返回所有的user attributes。
下面来通过几个例子来描述下Filter语法,前面提到IDaaS需要同步所有用户,那么对应在OpenLDAP Server和AD(不同厂商实现细节不一样)的Filter查询语法应该如下:
功能 | OpenLDAP | AD |
---|---|---|
查询所有用户 | (objectClass=person) | (&(objectCategory=person)(objectClass=user)) |
查询所有组织机构 | (&(objectClass=organizationalUnit)(objectClass=organization)) | (&(objectClass=organizationalUnit)(objectClass=organization)) |
查询单个用户 | (&(uid=zhangsan)(objectClass=person)) | (&(sAMAccountName=zhangsan)(&(objectCategory=Person)(objectClass=User))) |
从表格的示例来看,LDAP的Filter语法支持等于(=) 、逻辑与(&),逻辑或(|)等运算符,类似的还有逻辑非(!),大于等于(>=),小于等于(<=)等。从结构表现上来看,逻辑运算符在前,每个参与运算的表达式都会被括号包括在内,括号里面的内容是查询Entry属性的运算表达式,如objectClass=person是查询所有objectClass都是person的Entry。
修改:Add、Modify、Delete
- Add操作是往LDAP Server写入一条Entry数据,包含对应的DN和其它属性,根据Entry对应的objectClass要求,其它属性中必须包含所有Must Attributes属性才能成功完成Entry的创建;
- Modify操作可以修改Entry的属性,总共有四种修改类型:包括增-add、删-delete、改-replace、增加-increment四个操作;举例来说,add修改类型,可以指定Entry的DN,然后增加某个属性A=a;
- Delete操作:指定DN,从Server中删除该条entry;
LDIF,LDAP协议交换数据格式
LDIF,The LDAP Data Interchange Format,一种文本表示的LDAP数据交换格式。目前主要使用在如下场景:
- 原始备份机制,从LDAP Server导出LDIF格式的数据进行备份;
- 大批量操作,如给LDAP Server中的每个entry都增加一个属性,或者大规模调整LDAP Server中的层次架构,可能把对应修改写入LDIF文件中,让LDAP Server应用是最高效的;
- 数据迁移场景,从LDAP Server迁移到另一个LDAP Server,这两个Server所在的网络环境、操作系统都不相同;
dn: cn=test,o=xxxxxx,ou=Alibaba,dc=example,dc=com
changetype: modify
delete: userPassword
userPassword:: e1NTSEF9Slh1TXREbTNZcmZNbUJoUC9tamlMSEdMNzcyNzNJcXJZUSswV1E9PQ==
\-
add: userPassword
userPassword:: e01ENX1KZFZhMG9PcVFBcjBaTWR0Y1R3SHJRPT0=
\-
大致来讲,LDIF数据交换格式的样例如上所示。这个例子对这个entry(cn=test,o=xxxxxx,ou=Alibaba,dc=example,dc=com),同时做了删除密码和增加密码的操作。其它的LDIF具体详情可以参考这篇文档,对LDIF数据的理解和学习,更多的是让我们能够阅读具有图形界面的LDAP Client(Apache DS Studio等)的日志信息,帮助我们更好学习LDAP协议。
总结
本文从IDaaS对接LDAP建设产品能力要解决的三个问题开始,先对LDAP做了一个简单的描述,建立起一个对LDAP协议的感性认知(对于技术人员来讲,对应的概念均可以类比到RDMS系统),然后再从LDAP协议的具体四个方面对整个LDAP协议做了一个拆分理解,包含数据存储构成规则、连接与认证、查询和更改操作,以及LDIF数据协议格式。总的来讲,本文的内容主要是为了帮助理解对接LDAP Server需要的概念,基本上属于理论认知,而LDAP只是一个协议规范,不同的产品在具体实现时,会存在不同的实现细节,这部分信息需要在实际对接的时候再细细梳理。
参考文档
- JumpCloud:What is LDAP?
- LDAP.com: 链接,该网站下的多篇文档均有参考,不再详细列出。
- Okta:What is LDAP & How Does It work?
- LDAP.com: LDAP RFCs
- Apache Directory Studio:Operational Attributes
- LDAP Search Operation