# OpenLDAP
:一个开源的 LDAP 服务器。
- 官网 (opens new window)
- 它会运行一个 slapd 进程,作为 LDAP 服务器。
- 同类产品:
- Active Directory :简称为 AD ,同时支持 LDAP 和 Kerberos 协议。
- Apache Directory Server
# 部署
- 用 docker-compose 运行,配置如下:
version: "3" services: openldap: image: osixia/openldap:1.4.0 hostname: openldap ports: - 389:389 - 636:636 volumes: - ./data:/var/lib/ldap # 挂载存储目录 - ./conf:/etc/ldap/slapd.d # 挂载配置目录
- 该 Docker 镜像的 GitHub (opens new window)
- 默认监听两个端口:
- 389 :用于非加密通信。
- 636 :用于 SSL/TLS 加密通信。
- 默认创建了一个 {1}mdb 数据库,在容器内执行以下命令尝试查询:
ldapsearch -H ldapi:/// -Y EXTERNAL -b cn=config ldapsearch -H ldapi:/// -Y EXTERNAL -b olcDatabase={1}mdb,cn=config ldapsearch -x -D cn=admin,dc=example,dc=org -w admin -b dc=example,dc=org
- 接下来可以在 Ldap Admin 中开始操作。
# 原理
- OpenLDAP 支持创建多种数据库,常见的后端类型如下:
config # 用于存储 Slapd 的配置 bdb # 将数据存储到 Berkeley DB 文件数据库,已弃用 hdb # 另一种 bdb ,已弃用 ldap # 将数据存储到另一个 LDAP 服务器 ldif # 将数据存储以 LDIF 格式存储在文件中 mdb # 将数据存储到 LMDB 数据库中 meta # 元数据目录 monitor # 用于监控 slapd ,只能创建一个数据库实例 passwd # 用于以只读权限访问 /etc/passwd 文件 shell # 基于 Shell sql # 基于 SQL
- 一般的数据库推荐采用 mdb 。
# 命令
语法:
ldapadd|ldapmodify|ldapdelete|ldapsearch -H ldap://localhost:389 # 采用 ldap 协议,指定服务器的 URI -H ldaps://localhost:636 # 采用 ldaps 协议,进行加密通信 -H ldapi:///var/run/ldapi # 采用 ldapi 协议,通过 Unix Socket 连接 -b <dn> # 指定服务器上的一个 dn 作为查询的起点 -f <file> # 读取并执行 .ldif 文件的内容 -X dn:<dn> # 采用 SASL 认证模式(默认采用),需要先指定用户 dn ,接着按提示输入密码 -Y <mechanism> # 指定 SASL 机制 -x # 采用简单认证模式 -D <dn> # 指定用户名 -w <password> # 输入明文密码(用于简单认证模式)
例:
ldapsearch -H ldapi:/// -Y EXTERNAL -b dc=example,dc=org # 采用 SASL 认证模式,通过 Unix Socket 连接(这样可以免密登录) ldapsearch -x -b dc=example,dc=org # 采用简单认证模式,但并指定用户,即作为匿名用户 ldapsearch -x -D cn=admin,dc=example,dc=org -w admin -b dc=example,dc=org # 采用简单认证模式,并指定用户
# 配置
OpenLDAP 提供了一个在线配置系统,称为 OLC 。
- OLC 系统使用一个 Root DN 为
cn=config
的 DIT 来存储配置信息,从而支持在线修改配置,且不用重启就能生效。 - OLC 系统的配置属性的名称都以
olc
开头。
- OLC 系统使用一个 Root DN 为
OLC 系统中的每个条目会在磁盘的配置文件目录下保存为一个 .ldif 文件,如下:
/etc/ldap/slapd.d/ ├── cn=config.ldif # 存储 `dn: cn=config` 条目,该条目的子节点则存储在 cn=config/ 目录下 └── cn=config/ ├── cn=module{0}.ldif # 存储 `dn: cn=module{0}` 条目,它用于加载模块 ├── cn=schema.ldif ├── cn=schema/ │ ├── cn={0}core.ldif │ ├── cn={1}cosine.ldif │ └── cn={2}nis.ldif ├── olcDatabase={0}config.ldif ├── olcDatabase={-1}frontend.ldif ├── olcDatabase={1}mdb.ldif └── olcDatabase={1}mdb/ ├── olcOverlay={0}memberof.ldif └── olcOverlay={1}refint.ldif
- 用户不应该直接修改这些 .ldif 文件,而应该通过 ldapadd、ldapmodify 等命令进行在线配置。
olcDatabase={0}config.ldif
的内容示例如下:dn: olcDatabase={0}config objectClass: olcDatabaseConfig olcDatabase: {0}config olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * break olcRootDN: cn=admin,cn=config olcRootPW: e1NTSEF9MGJYN2xxMDZZOEQxVmZwVTFyZUs3RFZrMng4TDFPYWk= structuralObjectClass: olcDatabase ConfigentryUUID: 72c4b61a-be77-103a-9ff0-9ddaa4d2c622 creatorsName: cn=config createTimestamp: 20201119055436Z entryCSN: 20201119072303.463947Z#000000#000#000000 modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth # 这一个长 dn 是默认用户 modifyTimestamp: 20201119072303Z
olcDatabase :[{<index>}] <type>
- 用于创建一个数据库实例。
- index :该数据库实例在同类数据库中的索引。不指定时会根据创建顺序自动生成。
- type :该数据库的后端类型。
olcSuffix: <dn suffix>
- 指定一个 dn 后缀。所有包含该 dn 后缀的查询请求会被转发到该数据库,通常与该数据库的 Root DN 相同。
- 查询目标指向 olcDatabase 的 dn 时,会返回该该数据库的配置信息。而查询目标指向 olcSuffix 时,会返回该数据库中存储的条目。
olcRootDN :<dn>
- 创建一个对该数据库有完全权限的 root 用户(这会保存为指定 dn 的条目)。
- dn :root 用户的名称,不一定指向实际存在的条目。可以引用 SASL 用户。
olcRootPW :<password>
- 为 root 用户指定密码。可以指定多个密码。
- password :可以是明文密码,也可以是密码的哈希值。
olcAccess: to <what> [ by <who> [<accesslevel>] [<control>] ]+
- 允许 what 被 who 访问,分配 accesslevel 级别的权限。
- 如果不配置 olcAccess ,则默认为
* by * read
。 - 如果用户访问一个对象时没有读取权限,会报错该对象不存在。
- 在存在多个 olcAccess 规则时,最先匹配的那个会生效。如下,匿名用户会应用第二个 by 规则。
olcAccess: {0}to * by self write by anonymous auth by * read
- what 的取值示例:
* # 所有条目或属性 dn=<regex> # 与正则表达式匹配的条目或属性
- who 的取值示例:
* # 所有用户,包括匿名用户 anonymous # 匿名用户 users # 通过身份认证的用户 self # 目标条目自身的用户 dn=<regex> # 与正则表达式匹配的用户
# 配置密码
生成密码的哈希值:
root@openldap:/# slappasswd New password: # 按提示输入密码 Re-enter new password: {SSHA}CZX8qY73ejSEKSyWsKFxpWqSJoyDgCRG # 它会根据输入的密码生成一个哈希值
创建一个 .ldif 文件:
# 替换 {0}config 数据库的 Root 密码 dn: olcDatabase={0}config, cn=config # 指定一个要操作的条目 changetype: modify # 指定操作类型为 modify replace: olcRootPW # 指定 modify 类型为 replace ,并指定要操作的属性 olcRootPW: {SSHA}CZX8qY73ejSEKSyWsKFxpWqSJoyDgCRG # 采用刚刚生成的哈希值作为密码
导入 .ldif 文件:
root@openldap:~# ldapadd -H ldapi:/// -Y EXTERNAL -f 1.ldif SASL/EXTERNAL authentication started # 显示的前 3 行表示正在进行 SASL 认证 SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth # 当前 SASL 的用户名 SASL SSF: 0 modifying entry "olcDatabase={0}config, cn=config" # 正在修改一个条目(操作成功并不会显示反馈,操作失败才会显示报错)
尝试用新密码查询:
ldapsearch -x -D "cn=admin,cn=config" -w ****** -b 'cn=config'
# 配置数据库
创建一个 .ldif 文件:
# 设置 {1}mdb 数据库的 olcSuffix dn: olcDatabase={1}mdb,cn=config changetype: modify replace: olcSuffix olcSuffix: dc=example,dc=org # 设置 {1}mdb 数据库的 root 名 dn: olcDatabase={1}mdb,cn=config changetype: modify replace: olcRootDN olcRootDN: cn=root,dc=example,dc=org # 设置 {1}mdb 数据库的 root 密码 dn: olcDatabase={1}mdb,cn=config changetype: modify replace: olcRootPW olcRootPW:{SSHA}CZX8qY73ejSEKSyWsKFxpWqSJoyDgCRG # 设置 {1}mdb 数据库的访问权限 dn: olcDatabase={1}mdb,cn=config changetype: modify replace: olcAccess olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * break olcAccess: {1}to attrs=userPassword,shadowLastChange by self write # 设置对密码的权限 by dn="cn=root,dc=example,dc=org" write by anonymous auth by * none olcAccess: {2}to * by self read # 设置对条目的权限 by dn="cn=root,dc=example,dc=org" write by * none
TODO :
- 这里使用默认创建的
olcDatabase={1}mdb,cn=config
数据库,不知道如何创建新的数据库。 - 上述配置中可以修改 olcRootDN 的名称,但改变默认的 olcSuffix 就会导致查询不到。
- 这里使用默认创建的
导入 .ldif 文件:
root@openldap:~# ldapadd -H ldapi:/// -Y EXTERNAL -f 1.ldif SASL/EXTERNAL authentication started SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth SASL SSF: 0 modifying entry "olcDatabase={1}mdb,cn=config" modifying entry "olcDatabase={1}mdb,cn=config" modifying entry "olcDatabase={1}mdb,cn=config" modifying entry "olcDatabase={1}mdb,cn=config"
# 导入条目
创建一个 .ldif 文件:
dn: ou=dev,dc=example,dc=org objectClass: organizationalUnit ou: dev dn: ou=ops,dc=example,dc=org objectClass: organizationalUnit ou: ops dn: cn=LeoHsiao,ou=ops,dc=example,dc=org objectClass: person cn: LeoHsiao sn: Leo
导入 .ldif 文件:
root@openldap:~# ldapadd -H ldapi:/// -Y EXTERNAL -f 1.ldif SASL/EXTERNAL authentication started SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth SASL SSF: 0 adding new entry "ou=dev,dc=example,dc=org" adding new entry "ou=ops,dc=example,dc=org" adding new entry "cn=LeoHsiao,ou=ops,dc=example,dc=org"