Openldap-教程(centos)
源码包版本:2.4.54
系统:centos 6.x
介绍
ldap是什么
- LDAP:Lightweight Directory Access Protocol,轻量目录访问协议
- LDAP服务是一个为只读(查询、浏览、搜索)访问而优化的非关系型数据库,呈树状结构组织数据
- LDAP主要用做用户信息查询(如邮箱、电话等)或对各种服务访问做后台认证以及用户数据权限管控
- 与LDAP一样提供类似的目录服务软件还有ApacheDS、Active Directory、Red Hat Directory Service
- 自己理解:每一条DN可以由多个对象(objectClass)组成,每个对象又有不同的属性组成(必须+非必须),其实就是以objectClass为标准,方便各个系统通用
ldap名词
- DC:domain component一般为公司名,例如:dc=163,dc=com
- OU:organization unit为组织单元,最多可以有四级,每级最长32个字符,可以为中文
- CN:common name为用户名或者服务器名,最长可以到80个字符,可以为中文
- DN:distinguished name为一条LDAP记录项的名字,有唯一性(类似绝对路径),例如:dc: cn=admin,ou=developer,dc=163,dc=com"
- SN:suer name(真实名称)
- O:organization(组织-公司)
- C:countryName(国家)
- ldif扩展名:LDAP data Interchange Format (
LDIF
), 相当于mysql的sql语句 - olc开头:可以看到很多文件名和字段名都有前缀"olc" (OpenLDAP Configuration), 理解就好。
架构
组织方式
企业级命名组织架构
schema
schema定义一个条目所包含的对象类(objectClass)以及每一个对象类所包含的属性值(attribute value)
应用场景
-
机器认证
-
用户认证
-
用户/系统组
-
地址簿
-
组织代表
-
资产追踪
-
电话信息商店
-
用户资源管理
-
电子邮件地址查询
-
应用程序配置存储
-
PBX配置存储
-
等等…..
OpenLDAP属于开源集中账号管理软件。由于支持众多系统平台(Windows、Linux、Centos、Debian、Ubuntu、SUSE、Gentoo、openSUSE、Fedora、FreeBSD等),它被众多互联网企业、证券、银行、物流实现系统等用于账号的集中分配管理。
编译安装
优势:可以安装最新的版本、快速bug修复
缺点:新人比较难以上手
依赖环境
yum安装依赖
# enable-sql依赖:unixODBC
# with-tls依赖:gnutls-devel
yum -y install libtool-ltdl-devel unixODBC-devel gnutls-devel
源码安装openssl
# yum装的版本太旧,需要安装较新版本
wget https://www.openssl.org/source/openssl-1.0.2s.tar.gz
tar xzf openssl-1.0.2s.tar.gz && cd openssl-1.0.2s
# 添加-fPIC参数,python的hashlib、ssl内置模块不能用动态链接库编译(ssl编译错误日志有打印解决方法)
./config --prefix=/usr/local/openssl -fPIC
make && make install
源码安装berkeleydb
# 注意:当前版本最高支持5.1.x
https://www.oracle.com/cn/database/technologies/berkeleydb.html
http://download.oracle.com/berkeley-db/db-5.1.29.tar.gz
tar xzf db-5.1.29.tar.gz && cd db-5.1.29/build_unix
# 默认安装路径:/usr/local/BerkeleyDB.5.1
../dist/configure && make && make install
编译安装openldap
静态编译参数说明
# 静态编译说明:https://www.openldap.org/doc/admin24/overlays.html
# Backend 后端相关,默认保存在mdb数据库
--enable-sql:支持mysql、PostgreSQL等,根据自身需求确定是否开启(当前编译备用),依赖包:unixODBC-devel
# Overlay 相关
# 建议静态编译所有overlays,就不需要做加载步骤了,而slapd大小只增加了0.2M
--enable-modules:支持动态加载模块,这个建议开启,用于可能加载其它的overlays
--enable-overlays:将所有可用overlay编译成静态文件至启动文件(slapd)内,源码目录:servers/slapd/overlays/,包含下面的enable
# --enable-memberof:将memberof编译成静态文件至启动文件(slapd)内,也就是说不需要动态加载了,使用的时候定义overlay就行了
# --enable-dynacl:动态加载ACL
# --enable-accesslog:登录日志模块
# --enable-auditlog:密码审计模块(修改密码的时候,日志会被记录下来)
动态编译参数说明
# 注意:没有特殊需求建议编译成静态模块
# --enable-overlays=mod
# mod表示用动态模块(module)的方式编译模块
# --enable-modules 这个必须开启,否则配置dn: cn=module,cn=config会报错
./configure --prefix=/usr/local/openldap --enable-modules --enable-overlays=mod --enable-sql --with-tls
# 编译成功后,下面的目录有相关的la文件
ll /usr/local/openldap/libexec/openldap/*.la
# 查看编译成静态的overlays和backends模块
/usr/local/openldap/libexec/slapd -VVV
# 没有static overlays的数据了
# (env) [root@zaza-test openldap-2.4.54]# /usr/local/openldap/libexec/slapd -VVV
# @(#) $OpenLDAP: slapd 2.4.54 (Oct 15 2020 15:36:55) $
# root@zaza-test:/usr/local/src/openldap-2.4.54/servers/slapd
#
# Included static backends:
# config
# ldif
# monitor
# bdb
# hdb
# mdb
# relay
# sql
openldap升级注意事项
pqchecker依赖openldap的动态库,所以openldap升级的同时需要重新编译pqchecker
编译源码包
# 下载
wget https://www.openldap.org/software/download/OpenLDAP/openldap-release/openldap-2.4.54.tgz
# 解压编译安装
tar xzf openldap-2.4.54.tgz && cd openldap-2.4.54
export LDFLAGS="-L/usr/local/openssl/lib -L/usr/local/BerkeleyDB.5.1/lib"
export CPPFLAGS="-I/usr/local/openssl/include -I/usr/local/BerkeleyDB.5.1/include"
export LD_LIBRARY_PATH="/usr/local/BerkeleyDB.5.1/lib"
# 或者 LD_LIBRARY_PATH="/usr/local/BerkeleyDB.5.1/lib" ./configure
./configure --prefix=/usr/local/openldap --enable-modules --enable-overlays --enable-sql --with-tls
make depend && make && make install
# 环境设置
grep -q openldap /etc/profile || echo 'export PATH=$PATH:/usr/local/openldap/bin:/usr/local/openldap/sbin' >> /etc/profile
source /etc/profile
# slapd 依赖 libdb-5.1.so
cat > /etc/ld.so.conf.d/bdb.conf << EOF
/usr/local/BerkeleyDB.5.1/lib/
EOF
# 查看是否加载动态链接库
ldconfig -v | grep libdb-5.1.so
# 这里安装类似mysql的安装
# 初始状态下,LDAP是一个空目录,即没有任何数据。可通过程序代码向目录数据库中添加数据,也可使用OpenLDAP客户端工具ldapadd命令来完成添加数据的操作,该命令可将一个LDIF文件中的条目添加到目录。因此,需要首先创建一个LDIF文件,然后再进行添加操作。
查看编译成静态的overlays和backends模块
/usr/local/openldap/libexec/slapd -VVV
# 示例:
# (env) [root@zaza-test openldap-2.4.54]# /usr/local/openldap/libexec/slapd -VVV
# @(#) $OpenLDAP: slapd 2.4.54 (Oct 15 2020 15:36:55) $
# root@zaza-test:/usr/local/src/openldap-2.4.54/servers/slapd
#
# Included static overlays:
# accesslog
# auditlog
# collect
# constraint
# dds
# deref
# dyngroup
# dynlist
# memberof
# ppolicy
# pcache
# refint
# retcode
# rwm
# seqmod
# sssvlv
# syncprov
# translucent
# unique
# valsort
# Included static backends:
# config
# ldif
# monitor
# bdb
# hdb
# mdb
# relay
# sql
# tls检查
ldd /usr/local/openldap/libexec/slapd|grep libgnutls
源码安装pqchecker
如果启用ppolicy的密码复杂度的话,需要此三方模块
注意:pqchecker依赖openldap的动态库,所以openldap升级的同时需要重新编译pqchecker
# 依赖
yum -y install java-1.8.0-openjdk-devel
# 下载
wget -O pqchecker-2.0.0.tar.gz https://codeload.github.com/mahiso/pqchecker/tar.gz/v2.0.0
# 编译安装
# PARAMDIR定义了策略文件目录路径,文件名为:pqparams.dat
tar xzf pqchecker-2.0.0.tar.gz && cd pqchecker-2.0.0
./configure --prefix=/usr/local/pqchecker LDAPSRC=/usr/local/src/openldap-2.4.54 JAVAHOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.265.b01-0.el6_10.x86_64 PARAMDIR=/usr/local/openldap/etc/openldap
make && make install
# 默认的密码策略是,至少一个大写字母(UU),一个小写字母(LL),一个数字(DD),一个特殊字符(SS)。
# 0|01010101
# 0|UULLDDSS
# |号后面的为密码规则,每两位表示一个规则(0-99)
# 自动生成的策略文件
cat /usr/local/openldap/etc/openldap/pqparams.dat
LDAPS 对比 LDAP over TLS
- LDAPS:可以理解为https,加密所有数据
- TLS:它默认走389端口,通讯的数据会加密
openssl生成tls证书
客户机生成请求,csr -> CA签发 -> crt -> 拷回客户机已签名的证书
这里默认是v1版本,用CN验证,可以考虑使用v3版本,用SAN验证,可以使用泛域名+多ip证书认证
修改openssl.cnf配置文件
执行openssl命令时参数获取默认值的配置文件,生成多个证书的时候就不用一直填写基础信息了
# 参考目录:/etc/pki/CA/
mkdir /usr/local/openldap/etc/openldap/CA
cp /usr/local/openssl/ssl/openssl.cnf /usr/local/openldap/etc/openldap/CA/
# 编辑配置文件
vim /usr/local/openldap/etc/openldap/CA/openssl.cnf
###########################################################################################
# ca默认值
[ CA_default ]
dir = /usr/local/openldap/etc/openldap/CA # 定义了默认ca文件存放的路径
default_days = 3650 # 证书有效期
# 表示证书请求request,用于生成CSR文件
[ req ]
distinguished_name = req_distinguished_name # (DN)是一个扩展属性段落,用于指定证书请求时可被识别的字段名称
[ req_distinguished_name ]
# CN ==> Sichuan ==> Chengdu ==> SYS ==> IT ==> zaza ==> 260458726.com
countryName_default = CN # 默认国家
stateOrProvinceName_default = Sichuan # 默认省份
localityName_default = Chengdu # 默认城市
0.organizationName_default = Internet Zaza Ltd # 默认公司
organizationalUnitName_default = IT # 默认部门
# Chrome 58以后不再使用CN校验地址(就是就是浏览器地址栏URL中的那个地址host)了,而是使用SAN,但是IE 11还是使用CN
# ca文件的话,就是一个根证书颁发机构的名称即可
commonName_default = Zaza Root CA # CN名称,这里是颁发者,如果是签名的就是颁发给的人
emailAddress_default = 260458726@qq.com
###########################################################################################
ca证书相关
生成ca秘钥(ca.pem)
cd /usr/local/openldap/etc/openldap/CA
/usr/local/openssl/bin/openssl genrsa -out ca.pem 4096
生成ca证书签名请求文件(ca.csr)
# Certificate Signing Request(CSR)
# 配置文件中已经有默认值了,shell交互时一路回车就行
# 其实执行req的时候,如果没有ca秘钥,会自动生成的
/usr/local/openssl/bin/openssl req \
-new \
-sha256 \
-out ca.csr \
-key ca.pem \
-config openssl.cnf
生成ca根证书(ca.crt)
这个证书就是每个客户端需要安装的根证书 存放路径:/etc/openldap/cacerts/ca.crt
/usr/local/openssl/bin/openssl x509 \
-req \
-days 3650 \
-in ca.csr \
-signkey ca.pem \
-out ca.crt
# 查看内容
/usr/local/openssl/bin/openssl req -in ca.csr -text
生成openldap服务证书
生成openldap服务秘钥(openldap.pem)
/usr/local/openssl/bin/openssl genrsa -out openldap.pem 2048
生成openldap服务证书签名请求文件((openldap.csr)
/usr/local/openssl/bin/openssl req \
-new \
-sha256 \
-out openldap.csr \
-key openldap.pem \
-config openssl.cnf
# Common Name 注意:必须为openldap服务的域名或者对应服务器ip,否则客户端会认证失败
# Common Name 注意:必须为openldap服务的域名或者对应服务器ip,否则客户端会认证失败
# Common Name 注意:必须为openldap服务的域名或者对应服务器ip,否则客户端会认证失败
用CA证书签发终端服务证书(openldap.crt)
/usr/local/openssl/bin/openssl x509 \
-req \
-days 3650 \
-CA ca.crt \
-CAkey ca.pem \
-CAcreateserial \
-in openldap.csr \
-out openldap.crt
验证证书
通过ca证书公钥验证openldap服务端证书的合法性
/usr/local/openssl/bin/openssl verify -CAfile /usr/local/openldap/etc/openldap/CA/ca.crt /usr/local/openldap/etc/openldap/CA/openldap.crt
验证openldap服务是否通过CA验证
# 需要解析域名:openldap.zaza.com,或者在hosts里面临时解析测试
# openssl s_client -connect openldap.zaza.com:389 -showcerts -state -CAfile /usr/local/openldap/etc/openldap/CA/ca.crt
# 最后日志为ok代表通过
# Verify return code: 0 (ok)
# 服务器上验证
# openssl s_client -connect openldap.zaza.com:389 -showcerts -state -CAfile /usr/local/openldap/etc/openldap/CA/ca.crt
# 客户端上验证
# openssl s_client -connect openldap.zaza.com:636 -showcerts -state -CAfile /etc/openldap/cacerts/ca.crt
# 报错,因为是系统时间不对导致的,更新时间即可:ntpdate 0.centos.pool.ntp.org
# Verify return code: 9 (certificate is not yet valid)
配置slapd.conf
此配置文件是老版本的配置文件,新版本已经弃用
注:可以通过使用slapd将slapd.conf转换为slapd-config
基础配置slapd.ldif
vim /usr/local/openldap/etc/openldap/slapd.ldif
# olcPidFile后面配置tls
# 配置后,同时支持tls和ssl
# 客户机生成请求,csr -> CA签发 -> crt -> 拷回客户机已签名的证书
olcTLSCACertificateFile: /usr/local/openldap/etc/openldap/CA/ca.crt
olcTLSCertificateFile: /usr/local/openldap/etc/openldap/CA/openldap.crt
olcTLSCertificateKeyFile: /usr/local/openldap/etc/openldap/CA/openldap.pem
# olcTLSVerifyClient: never # 默认选项就是never
# Load dynamic backend modules:
# 这里只能配置后端模块,当前版本默认的是mdb后端
# 使用LMDB为backends后端存储模块
# 类似初始化数据库,数据库是空库(顶层根目录是:zaza.com),通常称为BaseDN
olcSuffix: dc=zaza,dc=com
# 最高权限,管理员账号(用户名Manager可以自定义,例如admin、root)
olcRootDN: cn=Manager,dc=zaza,dc=com
# 管理员密码
olcRootPW: secret
# 建议使用加密密码:slappasswd -s secret
# MD5加密方式:slappasswd -h {MD5} -s zaza
# olcRootPW: {SSHA}ea+EFdqmej+ORnCTwsEjD6clEa2HlMVv
生成配置数据库
其实就是基于slapd.ldif初始化运行环境,类似mysql的mysql_install_db
# 这个是配置数据库目录
mkdir -pv /usr/local/openldap/etc/slapd.d
# 生成配置数据库,这里必须:100.00% eta 才表示配置文件没有问题,
/usr/local/openldap/sbin/slapadd -n 0 -F /usr/local/openldap/etc/slapd.d -l /usr/local/openldap/etc/openldap/slapd.ldif
# 临时增加config密码管理权限
grep olcRootPW /usr/local/openldap/etc/slapd.d/cn\=config/olcDatabase\=\{0\}config.ldif || sed -i '/olcRootDN: cn=config/a\olcRootPW: secret' /usr/local/openldap/etc/slapd.d/cn\=config/olcDatabase\=\{0\}config.ldif
# 使用tls即可,基于389端口,只有数据加密
/usr/local/openldap/libexec/slapd -F /usr/local/openldap/etc/slapd.d
# 启动后,官方推荐命令修改(主要是有一个CRC32校验),ldapmodify标准修改密码
# 建议不要修改olcRootDN: cn=config为olcRootDN: cn=Manager,dc=zaza,dc=com,因为各库功能不同,用单独账号管理
# 密码生成:slappasswd -s secret
ldapmodify -x -H ldap:/// -D "cn=config" -w secret << EOF
dn: olcDatabase={0}config,cn=config
changetype: modify
replace: olcRootPW
olcRootPW: {SSHA}ea+EFdqmej+ORnCTwsEjD6clEa2HlMVv
EOF
# 这里才是存放后端数据的真实位置
# olcDbDirectory: /usr/local/openldap/var/openldap-data
线上激活tls
# 1、参考:openssl生成tls证书
# 2、配置
ldapadd -x -H ldap:/// -D "cn=config" -w secret << EOF
# 接口添加配置
dn: cn=config
changetype: modify
add: olcTLSCACertificateFile
olcTLSCACertificateFile: /usr/local/openldap/etc/openldap/CA/ca.crt
-
add: olcTLSCertificateFile
olcTLSCertificateFile: /usr/local/openldap/etc/openldap/CA/ldapserver.crt
-
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /usr/local/openldap/etc/openldap/CA/ldapkey.pem
EOF
# 3、重启服务
管理与测试
启动
相关协议
URL | Protocol | Transport |
---|---|---|
ldap:/// | LDAP | TCP port 389 |
ldaps:/// | LDAP over SSL | TCP port 636 |
ldapi:/// | LDAP | IPC (Unix-domain socket) |
# tls:基于389端口,只有数据加密
# ssl:636端口
/usr/local/openldap/libexec/slapd -F /usr/local/openldap/etc/slapd.d -h "ldap:/// ldapi:/// ldaps:///"
# 测试
ldapsearch -x -b '' -s base '(objectclass=*)' namingContexts
调试前台启动
# 前台运行打印所有debug日志
/usr/local/openldap/libexec/slapd -d -1 -F /usr/local/openldap/etc/slapd.d
# 关键日志打印,如授权进程日志
/usr/local/openldap/libexec/slapd -d 128 -F /usr/local/openldap/etc/slapd.d
关闭
killall slapd
导入常见的schema
# 使用cn=config账号导入,密码设置参考:### 生成运行时配置
cd /usr/local/openldap/etc/openldap/schema
# inetorgperson和memberOf依赖:cosine
ldapadd -x -H ldap:/// -D "cn=config" -w secret -f cosine.ldif
ldapadd -x -H ldap:/// -D "cn=config" -w secret -f inetorgperson.ldif
# nis包含posixAccount、shadowAccount、posixGroup
ldapadd -x -H ldap:/// -D "cn=config" -w secret -f nis.ldif
# 密码策略
ldapadd -x -H ldap:/// -D "cn=config" -w secret -f ppolicy.ldif
常见类的字段属性
- PosixAccount:https://ldapwiki.com/wiki/PosixAccount
- PosixGroup:https://ldapwiki.com/wiki/PosixGroup
- shadowAccount:https://ldapwiki.com/wiki/shadowAccount
配置条目
命令行管理工具
ACL授权
# mdb授权
# 权限设置,否则会报权限异常:Result: Insufficient access (50)
ldapmodify -x -H ldap:/// -D "cn=config" -w secret << EOF
dn: olcDatabase={1}mdb,cn=config
add: olcAccess
olcAccess: to *
by self write
by anonymous auth
by self read
EOF
创建组织单元:用户、组
ldapadd -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret << EOF
# 首先必须添加:dn: dc=zaza,dc=com
# 如果创建过则不需要创建了
dn: dc=zaza,dc=com
objectClass: top
objectClass: dcObject
objectClass: organization
o: zaza的测试ldap
dc: zaza
# 创建用户组织
dn: ou=People,dc=zaza,dc=com
objectClass: organizationalUnit
ou: People
# 创建组组织
dn: ou=group,dc=zaza,dc=com
objectClass: organizationalUnit
ou: group
EOF
添加系统账号
ldapadd -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret << EOF
# 添加组
dn: cn=zaza,ou=Group,dc=zaza,dc=com
objectClass: posixGroup
objectClass: top
cn: zaza
gidNumber: 1001
# 添加系统账号:zaza
dn: uid=zaza,ou=People,dc=zaza,dc=com
objectClass: top
objectClass: account
objectClass: posixAccount
objectClass: shadowAccount
cn: zaza
uid: zaza
uidNumber: 1001
gidNumber: 1001
homeDirectory: /home/zaza
loginShell: /bin/bash
gecos: zaza
# pam_ldap用的是MD5验证,密码为:secret
# 首次登陆后,改密为:{crypt}xxxx任然可以登录
userPassword: {MD5}Xr4ilOzQ4PCOq3aQ0qbuaQ==
# shadowLastChange等于0代表下次登录必须修改
shadowLastChange: 0
shadowMax: 0
shadowWarning: 0
# 添加系统账号:yaya
# uniqueMember 必须使用 cn=yaya,ou=People,dc=zaza,dc=com 而不是 uid=yaya,ou=People,dc=zaza,dc=com
dn: cn=yaya,ou=People,dc=zaza,dc=com
objectClass: top
objectClass: account
objectClass: posixAccount
objectClass: shadowAccount
cn: yaya
uid: yaya
uidNumber: 1002
gidNumber: 1001
homeDirectory: /home/yaya
loginShell: /bin/bash
gecos: yaya
# pam_ldap用的是MD5验证,密码为:secret
# 首次登陆后,改密为:{crypt}xxxx任然可以登录
userPassword: {MD5}Xr4ilOzQ4PCOq3aQ0qbuaQ==
# shadowLastChange等于0代表下次登录必须修改
shadowLastChange: 0
shadowMax: 0
shadowWarning: 0
EOF
创建管理组织单元及用户
#### 也可以从文件里面读取配置
# ldapadd -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret -f managers.ldif
ldapadd -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret << EOF
# 创建管理员单元(部门)
dn:ou=managers, dc=zaza, dc=com
ou:managers
objectclass:organizationalUnit
# managers单元下创建用户a
dn:cn=zaza,ou=managers,dc=zaza,dc=com
# cn 常用名称
cn:zaza
# sn 真实名称
sn:zhangsa
objectclass:person
# managers单元下创建用户b
dn:cn=yaya,ou=managers,dc=zaza,dc=com
cn:yaya
sn:lisi
objectclass:person
EOF
密码管理
查询用户
ldapsearch -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret "(cn=zaza)" -b ou=People,dc=zaza,dc=com
ldapsearch -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret "(cn=yaya)" -b ou=People,dc=zaza,dc=com
使用管理账号为用户添加密码
# 需要指定具体的dn值
# 默认自动生成随机密码,秘密会打印出来
# ldappasswd -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret "cn=yaya,ou=People,dc=zaza,dc=com"
# 也可以-s指定密码
# ldappasswd -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret "cn=yaya,ou=People,dc=zaza,dc=com" -s 123456
ldappasswd -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret "uid=zaza,ou=People,dc=zaza,dc=com" -s secret
ldappasswd -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret "cn=yaya,ou=People,dc=zaza,dc=com" -s secret
用户权限测试
# 测试,普通用户可以索引所有的数据?
ldapsearch -x -LLL -H ldap:/// -D "uid=zaza,ou=People,dc=zaza,dc=com" -w secret "(uid=zaza)" -b ou=People,dc=zaza,dc=com
ldapsearch -x -LLL -H ldap:/// -D "cn=yaya,ou=People,dc=zaza,dc=com" -w secret "(uid=yaya)" -b ou=People,dc=zaza,dc=com
用户修改密码
# 默认ACL不能修改自己的密码
# 修改自己的密码
ldappasswd -x -H ldap:/// -D "uid=zaza,ou=People,dc=zaza,dc=com" -w secret -s secret
# 管理员可以修改所有
ldappasswd -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret "uid=zaza,ou=People,dc=zaza,dc=com" -s secret
overlay模块配置
overlay可以理解成类似github的钩子功能
加载modules
modules用于加载其它没有静态编译的模块(Backend和Overlay模块)
当前模块所有已经做了静态编译,不需要加载这个模块
# 前置条件:激活动态加载模块(加载一次即可)
# --enable-modules这个参数实现,编译必须添加,否则报错
ldapadd -x -H ldap:/// -D "cn=config" -w secret << EOF
dn: cn=module,cn=config
objectClass: olcModuleList
# 这里指定动态模块路径,多个路径用冒号分割:olcModulePath: /usr/local/lib:/usr/local/lib/slapd
# 其实也可以不定义olcModulepath,因为编译的时候已经默认指定到下面的路径了
olcModulepath: /usr/local/openldap/libexec/openldap
cn: module
# 加载模块
# olcModuleLoad: memberof.la
# 也可以忽略后缀的简写方式
# olcModuleLoad: memberof
# 也可以直接指定完整的路径
# olcModuleLoad: /usr/local/lib/smbk5pwd.la
EOF
激活memberOf
使用场景
组用户有个字段叫member,可以查询当前组有哪些用户
但是用户没有组的属性字段,也就是用户登录的时候无法获取到自己属于哪些组
所以需要由系统(memberOf功能)来维护二者的映射关系。即
- group添加member的时候会自动给对应的entry添加memberof字段
- 当删除entry的时候,也会从group里删除member字段
加载memberof
已经静态编译则不需要加载此模块
# 默认已经静态编译至slapd
ldapadd -x -H ldap:/// -D "cn=config" -w secret << EOF
dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: memberof.la
EOF
激活异常
# 激活的模块不存在的话,会抛出以下的异常
(env) [root@zaza-test etc]# ldapadd -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret << EOF
> dn: cn=module{0},cn=config
> changetype: modify
> add: olcModuleLoad
> olcModulepath: /usr/local/openldap/libexec/openldap
> olcModuleLoad: aaaaaaa.la
> EOF
modifying entry "cn=module{0},cn=config"
ldap_modify: Other (e.g., implementation specific) error (80)
additional info: <olcModuleLoad> handler exited with 1
确认模块是否加载
# cat /usr/local/openldap/etc/slapd.d/cn\=config/cn\=module\{0\}.ldif
slapcat -n 0 -F /usr/local/openldap/etc/slapd.d | grep olcModuleLoad
# 包含以下内容标示加载成功
# olcModuleLoad: {0}memberof.la
创建memberof overlay
# 创建olcOverlay类型的条目
# 注意:多次执行会创建多条
ldapadd -x -H ldap:/// -D "cn=config" -w secret << EOF
dn: olcOverlay=memberof,olcDatabase={1}mdb,cn=config
objectClass: olcConfig
objectClass: olcMemberOf
objectClass: olcOverlayConfig
objectClass: top
olcOverlay: memberof
olcMemberOfDangling: ignore
olcMemberOfRefInt: TRUE
# groupOfNames 类对应的属性名为 member
# groupOfUniqueNames 类对应的属性名为 uniqueMember
# groupOfNames 和 groupOfUniqueNames 均可以使用,主要看程序的支持程度?
# 对应的类名
olcMemberOfGroupOC: groupOfUniqueNames
# 对应的属性名称
olcMemberOfMemberAD: uniqueMember
olcMemberOfMemberOfAD: memberOf
EOF
查询memberof overlay
# 命令行查询
ldapsearch -x -LLL -H ldap:/// -D "cn=config" -w secret "(olcOverlay=memberof)" -b "cn=config"
# 或者查看文件(可能会重复录入)
ll /usr/local/openldap/etc/slapd.d/cn\=config/olcDatabase\=\{1\}mdb
-rw------- 1 root root 623 Oct 13 11:39 olcOverlay={0}memberof.ldif
-rw------- 1 root root 520 Oct 13 11:41 olcOverlay={1}refint.ldif
-rw------- 1 root root 623 Oct 13 13:40 olcOverlay={2}memberof.ldif # 异常数据
-rw------- 1 root root 623 Oct 13 13:42 olcOverlay={3}memberof.ldif # 异常数据
-rw------- 1 root root 623 Oct 13 13:42 olcOverlay={4}memberof.ldif # 异常数据
-rw------- 1 root root 623 Oct 13 13:42 olcOverlay={5}memberof.ldif # 异常数据
创建memberof测试数据
ldapadd -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret << EOF
# dn: dc=zaza,dc=com
# objectclass: domain
# dc: zaza
# dn: ou=group,dc=zaza,dc=com
# objectclass: organizationalUnit
# ou: group
# dn: ou=People,dc=zaza,dc=com
# objectclass: organizationalUnit
# ou: People
# 测试账号:test1
dn: uid=test1,ou=People,dc=zaza,dc=com
objectclass: account
uid: test1
# 测试账号:test2
dn: uid=test2,ou=People,dc=zaza,dc=com
objectclass: account
uid: test2
# 测试组:testgroup1
dn: cn=testgroup1,ou=group,dc=zaza,dc=com
objectclass: groupOfUniqueNames
cn: testgroup1
# 组成员
uniqueMember: uid=test1,ou=People,dc=zaza,dc=com
uniqueMember: uid=test2,ou=People,dc=zaza,dc=com
# 测试组:testgroup2
dn: cn=testgroup2,ou=group,dc=zaza,dc=com
objectclass: groupOfUniqueNames
cn: testgroup2
# 组成员
uniqueMember: uid=test1,ou=People,dc=zaza,dc=com
uniqueMember: uid=test2,ou=People,dc=zaza,dc=com
EOF
测试memberof是否生效
# 查看test1有哪些组
ldapsearch -x -LLL -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret "(uid=test1)" -b dc=zaza,dc=com memberOf
uniqueMember的dn值设置异常测试
ldapadd -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret << EOF
# 添加映射组:testusergroup1
dn: cn=testusergroup1,ou=group,dc=zaza,dc=com
objectclass: groupOfUniqueNames
cn: testusergroup1
description: testusergroup1
# uniqueMember必须使用dn定义的记录值
uniqueMember: uid=zaza,ou=People,dc=zaza,dc=com
# yaya没有这条dn,所以memberOf索引
uniqueMember: uid=yaya,ou=People,dc=zaza,dc=com
# 添加映射组:testusergroup2
dn: cn=testusergroup2,ou=group,dc=zaza,dc=com
objectclass: groupOfUniqueNames
cn: testusergroup2
description: testusergroup2
# uniqueMember必须使用dn定义的记录值
# zaza没有这条dn,所以memberOf索引
uniqueMember: cn=zaza,ou=People,dc=zaza,dc=com
uniqueMember: cn=yaya,ou=People,dc=zaza,dc=com
EOF
# 只会显示:memberOf: cn=testusergroup1,ou=group,dc=zaza,dc=com,因为testusergroup2的uniqueMember配置错误啦
ldapsearch -x -LLL -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret "(cn=zaza)" -b ou=People,dc=zaza,dc=com memberOf
# 只会显示:memberOf: cn=testusergroup2,ou=group,dc=zaza,dc=com,因为testusergroup2的uniqueMember配置错误啦
ldapsearch -x -LLL -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret "(uid=yaya)" -b ou=People,dc=zaza,dc=com memberOf
UniqueMember注意事项
uniqueMember必须使用dn定义的记录值,否则无法索引
定义如下:
The UniqueMember AttributeTypes is defined as:
OID of 2.5.4.50
NAME: UniqueMember
DESC:
EQUALITY:
ORDERING:
SYNTAX: 1.3.6.1.4.1.1466.115.121.1.12 (DN) # 说明必须为DN值
USAGE: UserApplications
Extended Flags:
X-ORIGIN: RFC 4519
Used as MUST in:
Used MAY in:
激活ppolicy
密码策略,外部模块pqchecker实现密码复杂度
本次加入的密码策略,大致上有以下内容:
- 密码是长度至少为8位
- 密码至少包含数字,大写字母,小写字母,特殊字符
- 5次内使用过的密码不能再次使用
- 连续输入错误超过5次,密码将锁定5分钟
加载ppolicy
已静态编译,不需要动态加载了
ldapadd -x -H ldap:/// -D "cn=config" -w secret << EOF
dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: ppolicy.la
EOF
创建ppolicy overlay
ldapadd -x -H ldap:/// -D "cn=config" -w secret << EOF
dn: olcOverlay=ppolicy,olcDatabase={1}mdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcPPolicyConfig
olcOverlay: ppolicy
# 定义调用的默认策略DN(会默认应用这个策略)
olcPPolicyDefault: cn=default,ou=policies,dc=zaza,dc=com
olcPPolicyHashCleartext: TRUE
olcPPolicyUseLockout: FALSE
olcPPolicyForwardUpdates: FALSE
EOF
创建olcPPolicyDefault声明的默认策略
ldapadd -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret << EOF
# 创建ou
dn: ou=policies,dc=zaza,dc=com
objectClass: organizationalUnit
ou: People
# 创建策略,对应olcPPolicyDefault值
dn: cn=default,ou=policies,dc=zaza,dc=com
objectClass: person
objectClass: pwdPolicy
objectClass: pwdPolicyChecker
cn: default
sn: default
pwdAllowUserChange: TRUE
pwdAttribute: userPassword
pwdExpireWarning: 259200
pwdFailureCountInterval: 0
pwdGraceAuthNLimit: 5
pwdInHistory: 5
pwdLockout: TRUE
pwdLockoutDuration: 300
pwdCheckQuality: 2
pwdCheckModule: /usr/local/pqchecker/lib/pqchecker.so
pwdMaxAge: 2592000
pwdMaxFailure: 5
pwdMinAge: 0
pwdMinLength: 8
# 系统级别的没有必要强制改密码
pwdMustChange: FALSE
# 注意此处不用添加 pwdSafeModify: TRUE, 可能会导致错误
# 该属性控制用户在密码修改操作期间是否必须发送当前密码。如果属性值为FALSE(缺省值),则用户不必发送其当前密码。如果属性值为TRUE,那么修改密码值时用户必须发送当前密码
# pwdSafeModify: TRUE
# 密码重置
pwdReset: TRUE
EOF
# 修改属性
ldapmodify -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret << EOF
dn: cn=default,ou=policies,dc=zaza,dc=com
changetype: modify
replace: pwdMustChange
pwdMustChange: FALSE
EOF
关于弱密码
管理员重置的是弱密码也不影响流程,只有在改密码的时候才会校验密码复杂度
测试功能
# 可以直接修改,管理员不受此限制?
ldappasswd -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret "uid=zaza,ou=People,dc=zaza,dc=com" -s secret
# 生效了
ldappasswd -x -H ldap:/// -D "uid=zaza,ou=People,dc=zaza,dc=com" -w secret -s secret
# 异常日志
Result: Constraint violation (19) # 这个是pqchecker密码策略复杂度的错误日志
Additional info: Password fails quality checking policy # 这个是ppolicy密码未到8位数的错误日志(ppolicy没有复杂度)
# 也可以通过程序日志查看
tail -n 10 /var/log/messages
# 异常日志
Oct 14 17:16:11 zaza-test pqchecker[24928]: Checking password quality for uid=zaza,ou=People,dc=zaza,dc=com.
Oct 14 17:16:11 zaza-test pqchecker[24928]: Password rejected.
# 正常日志
Oct 14 17:16:53 zaza-test pqchecker[24928]: Checking password quality for uid=zaza,ou=People,dc=zaza,dc=com.
Oct 14 17:16:53 zaza-test pqchecker[24928]: Password accepted.
密码策略属性
密码策略涉及的属性如下:
- pwdAllowUserChange:允许用户修改其密码
- pwdAttribute, pwdPolicy:对象的一个属性,用于标识用户密码。默认值(目前唯一支持的)是userPassword
- pwdExpireWarning:密码过期前警告天数
- pwdFailureCountInterval:多久时间后重置密码失败次数, 单位是秒
- pwdGraceAuthNLimit:密码过期后不能登录的天数,0代表禁止登录。
- pwdInHistory:开启密码历史记录,用于保证不能和之前设置的密码相同。
- pwdLockout:定义用户错误密码输入次数超过pwdMaxFailure定义后, 是否锁定用户, TRUE锁定(默认).
- pwdLockoutDuration:密码连续输入错误次数后,帐号锁定时间。
- pwdMaxAge:密码有效期,到期需要强制修改密码, 2592000是30天
- pwdMaxFailure:密码最大失效次数,超过后帐号被锁定。
- pwdMinAge:密码最小有效期, 默认为0, 用户随时更改密码, 如果定义了, 用户在离上次更改密码 + 定义的时间之内不能更改密码
- pwdMinLength:用户修改密码时最短的密码长度
- pwdMustChange:用户在帐户锁定后由管理员重置帐户后是否必须更改密码, 并且只有在pwdLockout为TRUE时才相关, 如果值为FLASE(默认值), 管理员帮用户解锁用户后, 用户不必更改密码, 如果为TRUE, 就必须更改密码。如果使用pwdReset来解锁用户, 其值将覆盖此属性
- pwdSafeModify:该属性控制用户在密码修改操作期间是否必须发送当前密码。如果属性值为FALSE(缺省值),则用户不必发送其当前密码。如果属性值为TRUE,那么修改密码值时用户必须发送当前密码。
- pwdLockoutDuration:帐号锁定后,不能自动解锁,此时需要管理员干涉
激活accesslog
用于记录用户操作
加载accesslog
已静态编译,不需要动态加载了
ldapadd -x -H ldap:/// -D "cn=config" -w secret << EOF
dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: accesslog.la
EOF
# 查看是否加载成功
slapcat -n 0 -F /usr/local/openldap/etc/slapd.d | grep olcModuleLoad
创建accesslog dit文件
类似创建mysql的数据库
# 目录信息树(Directory Information Tree (DIT))是指数据被表示在一种分级的树形结构中
# accesslog实际上是保存到单独的mdb里面
# 类似创建mysql的数据库
mkdir -pv /usr/local/openldap/var/openldap-data/accesslog
# 新创建一个DIT文件:/usr/local/openldap/etc/slapd.d/cn=config/olcDatabase={2}mdb.ldif
# 创建了一个独立的mdb路径,类似创建mysql实例
ldapadd -x -H ldap:/// -D "cn=config" -w secret << EOF
dn: olcDatabase={2}mdb,cn=config
changetype: add
objectClass: olcDatabaseConfig
objectClass: olcMdbConfig
olcDatabase: {2}mdb
olcDbDirectory: /usr/local/openldap/var/openldap-data/accesslog
olcSuffix: cn=accesslog
# 管理员账号可以查询此数据
olcRootDN: cn=Manager,dc=zaza,dc=com
olcDbIndex: default eq
# 需要记录的数据
olcDbIndex: entryCSN,objectClass,reqEnd,reqResult,reqStart
EOF
# 查看结果
# (env) [root@zaza-test ~]# tree /usr/local/openldap/var/openldap-data/
# /usr/local/openldap/var/openldap-data/
# ├── accesslog # 独立的accesslog(类似mysql的实例)
# │ ├── data.mdb
# │ └── lock.mdb
# ├── data.mdb # 账户数据数据(类似mysql的实例)
# ├── DB_CONFIG.example
# └── lock.mdb
创建accesslog overlay
ldapadd -x -H ldap:/// -D "cn=config" -w secret << EOF
dn: olcOverlay=accesslog,olcDatabase={1}mdb,cn=config
changetype: add
objectClass: olcOverlayConfig
objectClass: olcAccessLogConfig
olcOverlay: accesslog
olcAccessLogDB: cn=accesslog
olcAccessLogOps: all
olcAccessLogSuccess: TRUE
olcAccessLogPurge: 30+00:00 01+00:00
EOF
查看数据
# 先来一条测试数据
ldapsearch -x -LLL -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret "(uid=yaya)" -b ou=People,dc=zaza,dc=com
# 查看日志
ldapsearch -x -LLL -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret -b cn=accesslog
激活auditlog
审计功能,如修改密码
以标准的LDIF格式记录数据库的所有变更,类似mysql的binlog数据
承载压力大的服务器,建议添加定时任务(或者logrotate)进行切割、归档、压缩
加载auditlog
已经静态编译则不需要加载此模块
ldapadd -x -H ldap:/// -D "cn=config" -w secret << EOF
dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: auditlog.la
EOF
查看是否加载成功
slapcat -n 0 -F /usr/local/openldap/etc/slapd.d | grep olcModuleLoad
创建auditlog overlay
# 创建日志目录
mkdir -pv /usr/local/openldap/var/log
# 创建overlay
ldapadd -x -H ldap:/// -D "cn=config" -w secret << EOF
dn: olcOverlay=auditlog,olcDatabase={1}mdb,cn=config
changetype: add
objectClass: olcOverlayConfig
objectClass: olcAuditLogConfig
olcOverlay: auditlog
olcAuditlogFile: /usr/local/openldap/var/log/auditlog.ldif
EOF
# mdb授权
# 权限设置,否则会报权限异常:Result: Insufficient access (50)
ldapmodify -x -H ldap:/// -D "cn=config" -w secret << EOF
dn: olcDatabase={1}mdb,cn=config
add: olcAccess
olcAccess: to *
by self write
by anonymous auth
by self read
EOF
测试功能
# 先来一条测试数据
ldappasswd -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret "uid=zaza,ou=People,dc=zaza,dc=com" -s secret
# 用户自己没有权限修改密码了?
ldappasswd -x -H ldap:/// -D "uid=zaza,ou=People,dc=zaza,dc=com" -w secret -s secret
# 没有权限的异常日志
# Result: Insufficient access (50)
# 查看日志
cat /usr/local/openldap/var/log/auditlog.ldif
异常处理
权限异常码50处理
# 用户自己没有权限修改密码了?
ldappasswd -x -H ldap:/// -D "uid=zaza,ou=People,dc=zaza,dc=com" -w secret -s secret
# 没有权限的异常日志
# Result: Insufficient access (50)
# 建议调试模式运行
# /usr/local/openldap/libexec/slapd -d -1 -F /usr/local/openldap/etc/slapd.d
# 获取的异常日志如下(获取50关键字)
5f86c582 mdb_modify_internal: 0x00000008: uid=zaza,ou=People,dc=zaza,dc=com
5f86c582 => access_allowed: backend default write access denied to "uid=zaza,ou=People,dc=zaza,dc=com" # 这个问题导致的,发现没有写权限导致的
5f86c626 mdb_modify_internal: 0x00000008: uid=zaza,ou=People,dc=zaza,dc=com
5f86c626 => access_allowed: backend default write access denied to "uid=zaza,ou=People,dc=zaza,dc=com"
5f86c626 mdb_modify: modify failed (50) # 没有权限操作
5f86c626 send_ldap_result: conn=1008 op=1 p=3
5f86c626 send_ldap_result: err=50 matched="" text=""
5f86c626 slap_graduate_commit_csn: removing 0x7fdf9c104a60 20201014093430.033623Z#000000#000#000000
5f86c626 send_ldap_extended: err=50 oid= len=0
5f86c626 send_ldap_response: msgid=2 tag=120 err=50
# 解决方法:mdb授权自己可写即可
# 默认权限
Backend ACL: access to *
by * none
# 权限设置,否则会报权限异常:Result: Insufficient access (50)
ldapmodify -x -H ldap:/// -D "cn=config" -w secret << EOF
dn: olcDatabase={1}mdb,cn=config
add: olcAccess
olcAccess: to *
by self write
by anonymous auth
by self read
EOF
权限异常码19处理
# 秘密合规的状态下,返回异常码19
ldappasswd -x -H ldap:/// -D "uid=zaza,ou=People,dc=zaza,dc=com" -w secret -s secret@#D
Result: Constraint violation (19)
# 调试查看
/usr/local/openldap/libexec/slapd -d -1 -F /usr/local/openldap/etc/slapd.d
# 异常日志如下
5f87ff7c check_password_quality: module error: (/usr/local/pqchecker/lib/pqchecker.so) Unable to verify the quality of the password. Problem in parameters..[1]
5f87ff7c send_ldap_result: conn=1000 op=1 p=3
5f87ff7c send_ldap_result: err=19 matched="" text="Unable to verify the quality of the password. Problem in parameters."
5f87ff7c send_ldap_extended: err=19 oid= len=0
5f87ff7c send_ldap_response: msgid=2 tag=120 err=19
# 想起了是因为openldap版本升级了,而编译pqchecker又依赖openldap的动态库,所以需要重新编译pqchecker
web ui管理工具
ldap-account-management
环境依赖
- nginx
- php >= 7.0.0
schema依赖
用户 个人信息 在你的LDAP服务器中对象类inetOrgPerson不被支持。 Unix 在你的LDAP服务器中对象类posixAccount不被支持。 Shadow 在你的LDAP服务器中对象类shadowAccount不被支持。
组 Unix 在你的LDAP服务器中对象类posixGroup不被支持。
导入schema
cd /usr/local/openldap/etc/openldap/schema
# inetorgperson依赖cosine
ldapadd -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret -f cosine.ldif
ldapadd -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret -f inetorgperson.ldif
# nis包含posixAccount、shadowAccount、posixGroup
ldapadd -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret -f nis.ldif
配置
http://localhost/ldap-account-manager/templates/config/conflogin.php
服务器地址:ldap://10.0.26.26
树状结构后缀:dc=zaza,dc=com
合法用户列表:cn=Manager,dc=zaza,dc=com
测试依赖的schema
http://localhost/ldap-account-manager/templates/tests/schemaTest.php
客户端管理工具
http://www.ldapadmin.org/download/ldapadmin.html
ACL设置
语法
olcAccess: to [resources]
by [who] [type of access granted] [control]
by [who] [type of access granted] [control]
# More 'by' clauses, if necessary....
# mdb授权
# 默认权限
Backend ACL: access to *
by * none
# 修改权限
ldapmodify -x -H ldap:/// -D "cn=config" -w secret << EOF
dn: olcDatabase={1}mdb,cn=config
add: olcAccess
# 权限设置,否则auditlog模块会报权限异常:Result: Insufficient access (50)
olcAccess: to *
by self write
by anonymous auth
by self read
EOF
# 示例
https://my.oschina.net/u/142602/blog/134746
olcAccess: to *
by self write
by anonymous auth
by self read
access to attrs=userPassword
by self write
by anonymous auth
by dn.base="cn=Manager,dc=mycompany,dc=com" write
by * none
access to *
by self write
by dn.base="cn=Manager,dc=mycompany,dc=com" write
by * read
migrationtools
migrationtools 开源工具通过查找/etc/passwd、/etc/shadow、/etc/groups 生成LDIF 文件,并通过ldapadd 命令更新数据库数据,完成用户添加
centos接入openldap
服务定义
- nslcd:Daemon for NSS and PAM lookups using LDAP,增强了nss_ldap功能,预设RFC2307标准
- nscd:name service cache daemon 用于缓存信息:passwd, group, hosts databases through standard libc interfaces (getpwnam, getpwuid, getgrnam, getgrgid, gethostbyname, and others)
- sssd: System Security Services Daemon 红帽独立的认证套件,本文档不用此服务进行认证
架构图
pam_ldap架构图
实现客户端和服务端交互
nslcd架构图
主要是增强了nss_ldap功能
ncsd架构图
缓存的数据
pam登录流程
- ssh登录(/etc/ssh/sshd_config有pam的配置UsePAM yes),此时nslcd(/etc/nscd.conf)会向openldap请求相关用户数据
- 本地和openldap返回没有命中相关账号(Invalid user xxxx)的话则退出
- 账号存在的话,pam首先进入pam_unix流程,认证失败,进入pam_ldap流程
- pam_ldap(/etc/pam_ldap.conf)向openldap请求用户密码数据
- 密码匹配成功后登陆完成,失败则退出登录
相关软件包
- /etc/nsswitch.conf ==> rpm -ql glibc-2.12-1.212.el6_10.3.x86_64|egrep nsswitch.conf
- /etc/sysconfig/authconfig ==> rpm -ql authconfig-6.1.12-23.el6.x86_64|egrep “sysconfig/authconfig”
- /etc/pam.d/system-auth ==> rpm -ql pam-1.1.1-22.el6.x86_64|grep system
- /etc/pam_ldap.conf ==> rpm -ql pam_ldap-185-11.el6.x86_64|grep pam_ldap.conf
- /etc/nslcd.conf ==> rpm -ql nss-pam-ldapd-0.7.5-32.el6_10.1.x86_64|grep nslcd.conf
- /etc/openldap/ldap.conf ==> rpm -ql openldap.x86_64|grep ldap.conf 所以导致openldap-clients无法使用此配置文件? openldap服务
安装nslcd服务
原理说明:
The file nslcd.conf
contains the configuration information for running nslcd (see nslcd(8)). The file contains options, one on each line, defining the way NSS lookups and PAM actions are mapped to LDAP lookups.
- pam_ldap原理:https://arthurdejong.org/nss-pam-ldapd/pam_ldap.8
安装nslcd
# nslcd进程由以下包提供
# centos:nss-pam-ldapd
# ubuntu: nslcd
# nss-pam-ldapd依赖包:nscd、pam_ldap
yum -y install nss-pam-ldapd
配置ldapd认证
强烈建议使用authconfig配置openldap认证,直接修改配置文件容易出错!!!因为每个系统都不一样,配置也有一定的区别
# 强烈建议使用authconfig配置openldap认证,直接修改配置文件容易出错!!!,每个系统都不一样,配置也有一定的区别
# 配置前一定要备份
authconfig --savebackup=/opt/openldap_$(date +%s).bak
# 配置后会自动启动nslcd
# 建议使用:sha512加密模式,目前linux密码用的加密方式,比较安全
# authconfig --enableldap --enableldapauth --ldapserver=ldap://10.0.26.26 --disableldaptls --enablemkhomedir --ldapbasedn="dc=zaza,dc=com" --update
# TLS可以简单理解为ldaps的升级:它默认走389端口(所以ldapserver仍然是ldap://),但是传输数据的会加密
# ldap的地址必须为openssl对应的Common Name值
authconfig --enableldap --enableldapauth --ldapserver=ldap://10.0.26.26 --enableldaptls --enablemkhomedir --ldapbasedn="dc=zaza,dc=com" --update
# 配置异常后恢复命令
# authconfig --restorebackup=/opt/openldap_xxxxxxx.bak
# 测试
authconfig --test | grep ldap
# 重启sshd
/etc/init.d/sshd restart
authconfig配置变更项
自动生成的不要手动修改,除了增加openldap的账号密码!!!
下面配置的是管理员账号,正常情况下授权一个只读账号到指定的ou(和属性)即可
/etc/sysconfig/authconfig
# /etc/sysconfig/authconfig
USEMKHOMEDIR=yes
USELDAPAUTH=yes
USELDAP=yes
/etc/nslcd.conf
需要手动修改部分配置
vim /etc/nslcd.conf
# 注意:如果有密码认证的话需要单独新增账号配置(binddn和bindpw)
# 开启tls的话,证书配置文件需要调整
uri ldap://10.0.26.26
base dc=zaza,dc=com
ssl start_tls
#tls_cacertdir /etc/openldap/cacerts
tls_cacertfile /etc/openldap/cacerts/ca.crt
binddn cn=Manager,dc=zaza,dc=com
bindpw secret
# 映射说明
# https://arthurdejong.org/nss-pam-ldapd/nslcd.conf.5
# map MAP ATTRIBUTE NEWATTRIBUTE
# 映射 映射的组件 映射的远程属性(RFC 2307)
#map passwd homeDirectory unixHomeDirectory
/etc/nsswitch.conf
passwd: files ldap
shadow: files ldap
group: files ldap
netgroup: files ldap
automount: files ldap
/etc/openldap/ldap.conf
TLS_CACERTDIR /etc/openldap/cacerts
URI ldap://10.0.26.26
BASE dc=zaza,dc=com
# 注意如果有密码认证的话需要单独新增账号配置
# binddn cn=Manager,dc=zaza,dc=com
# bindpw secret
# 还没有找到此配置文件的用处,因为没有安装openldap-clients软件包
# TLS_REQCERT never
URI ldap://10.0.26.26
# URI 可以配置多个openldap服务器
# URI ldap://192.168.1.2 ldap://192.168.1.3
BASE dc=zaza,dc=com
# 报错则使用调试模式测试
# ldap_sasl_bind(SIMPLE): Can't contact LDAP server (-1)
ldapsearch -x -LLL -d1
# 发现配置文件没有生效:ldap_connect_to_host: TCP localhost:389
/etc/pam_ldap.conf
需要手动修改部分配置
vim /etc/pam_ldap.conf
# pam_ldap.so会读取此配置文件
base dc=zaza,dc=com
uri ldap://10.0.26.26
ssl start_tls
#tls_cacertdir /etc/openldap/cacerts
tls_cacertfile /etc/openldap/cacerts/ca.crt
# md5加密模式
# 首次登陆后,改密为:{crypt}xxxx任然可以登录
pam_password md5
# 注意如果有密码认证的话需要单独新增账号配置
binddn cn=Manager,dc=zaza,dc=com
bindpw secret
/etc/pam.d
# 强烈建议使用authconfig配置openldap认证,直接修改配置文件容易出错!!!,每个系统都不一样,配置也有一定的区别
# /etc/pam.d/system-auth-ac
auth sufficient pam_ldap.so use_first_pass
# auth required pam_deny.so
account required pam_unix.so broken_shadow
# account sufficient pam_localuser.so
# Saccount sufficient pam_succeed_if.so uid < 500 quiet
account [default=bad success=ok user_unknown=ignore] pam_ldap.so
password sufficient pam_ldap.so use_authtok
# password required pam_deny.so
# session required pam_limits.so
session optional pam_mkhomedir.so umask=0077
# session required pam_unix.so
session optional pam_ldap.so
# /etc/pam.d/smartcard-auth-ac 没有必要吧
# 指纹识别? 应该没有必要/etc/pam.d/fingerprint-auth-ac
# /etc/pam.d/password-auth-ac
auth sufficient pam_ldap.so use_first_pass
# auth required pam_deny.so
account required pam_unix.so broken_shadow
# account sufficient pam_localuser.so
# account sufficient pam_succeed_if.so uid < 500 quiet
account [default=bad success=ok user_unknown=ignore] pam_ldap.so
password sufficient pam_unix.so sha512 shadow nullok try_first_pass use_authtok
password sufficient pam_ldap.so use_authtok
# password required pam_deny.so
# session required pam_limits.so
session optional pam_mkhomedir.so umask=0077
# session required pam_unix.so
session optional pam_ldap.so
启动nslcd
# authconfig执行后正常情况下回自动启动nslcd程序
/etc/init.d/nslcd start
# debug模式
# 注意时间:ntpdate 0.centos.pool.ntp.org
nslcd -d
# 测试
getent passwd zaza # 测试 nslcd 通信是否正常
ssh zaza@127.0.0.1 # 测试 pam_ldap是否正常
启动nscd
确认可以正常登录后,就可以启动密码缓存功能了
# 如何确认数据已经缓存了呢?
/etc/init.d/nscd start
常见问题
Invalid user
tailf /var/log/secure
# 直接报远程账号不存在
Jul 8 04:38:43 test sshd[1633]: Invalid user zaza from 10.0.26.28 # 没有索引到账号,说明nslcd获取列表账号失败
Jul 8 04:38:43 test sshd[1634]: input_userauth_request: invalid user zaza
Jul 8 04:38:43 test sshd[1633]: pam_unix(sshd:auth): check pass; user unknown
Jul 8 04:38:43 test sshd[1633]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=10.0.26.28
Jul 8 04:38:43 test sshd[1633]: pam_succeed_if(sshd:auth): error retrieving information about user zaza
Jul 8 04:38:45 test sshd[1633]: Failed password for invalid user zaza from 10.0.26.28 port 14886 ssh2
Jul 8 04:38:59 test sshd[1634]: Received disconnect from 10.0.26.28: 0:
# 查看日志
# 验证看一下有没有远程的账号,错误的时候:没有数据打印出来
getent passwd zaaz
# 查看错误日志
tailf /var/log/messages
Jul 8 04:38:43 test nslcd[1498]: [b0dc51] ldap_result() failed: Insufficient access # 说明nslcd账号配置错误
# 解决:/etc/nslcd.conf添加相关账号,并重启
binddn cn=Manager,dc=zaza,dc=com
bindpw secret
# 修改后重启
/etc/init.d/nslcd restart
# 获取数据成功
[root@test ~]# getent passwd zaza
zaza:x:1001:1001:zaza:/home/zaza:/bin/bash
# 或者这样测试
[root@test ~]# id zaza
uid=1001(zaza) gid=1001(zaza) groups=1001(zaza)
pam_ldap: ldap_search_s Insufficient access
tailf /var/log/secure
Jul 8 05:09:56 test unix_chkpwd[1720]: password check failed for user (zaza) # 这个数据正常,说明获取账号ok,unix_chkpwd true的代表命中暴力破解?
Jul 8 05:09:56 test sshd[1718]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=localhost user=zaza # 本地认证错误是正常的,因为是远程账号
Jul 8 05:10:06 test sshd[1718]: pam_ldap: ldap_search_s Insufficient access # 进入pam_ldap认证流程,但是报没有权限查询相关账号数据
Jul 8 05:10:08 test sshd[1718]: Failed password for zaza from 127.0.0.1 port 43989 ssh2
# 解决方法:修改pam_ldap配置文件
vim /etc/pam_ldap.conf
binddn cn=Manager,dc=zaza,dc=com
bindpw secret
pam_ldap: error trying to bind as user …… (Invalid credentials)
tailf /var/log/secure
Jul 8 05:17:47 test sshd[1729]: pam_ldap: error trying to bind as user "uid=zaza,ou=People,dc=zaza,dc=com" (Invalid credentials)
Jul 8 05:17:49 test sshd[1729]: Failed password for zaza from 127.0.0.1 port 43993 ssh2
Jul 8 05:17:58 test sshd[1730]: Connection closed by 127.0.0.1
# 到这一步说明:nslcd已经可以正常查询说有的数据,pam_ldap也可以正常的查询玩家密码信息,但是密码不匹配,需要确认一下pam_ldap配置的密码类型和创建的账号加密类型是否一致
[root@test ~]# grep "^pam_password" /etc/pam_ldap.conf
pam_password md5
# 添加系统账号:zaza
# 这里是crypt类型和pam_ldap.conf配置的(md5)不匹配导致的
dn: uid=zaza,ou=People,dc=zaza,dc=com
......
# userPassword: {SSHA}RAEPE0fK0Mmq0KbUoRlTs/Rldd1vHI2t
# userPassword 需要改成,密码为:secret
userPassword: {MD5}Xr4ilOzQ4PCOq3aQ0qbuaQ==
......
# 修改密码
ldappasswd -x -H ldap:/// -D "cn=Manager,dc=zaza,dc=com" -w secret "uid=zaza,ou=People,dc=zaza,dc=com" -s secret
pam_ldap: ldap_starttls_s: Connect error
服务端报错:TLS: can’t accept: A TLS packet with unexpected length was received.. (证书长度错误,不是预期的长度)
# https://serverfault.com/questions/628512/pam-ldap-and-ldaps-cant-contact-ldap-server
My CA certificate is the correct one, but openldap uses Mozilla Network Security Services (MozNSS) by default for checking the authority. So I have to add my self-signed CA to this database.
# 导入成MozNSS数据库格式
certutil -d /etc/openldap/certs -A -n "ldap CA" -t TCu,Cu,Tuw -a -i /etc/openldap/cacerts/ca.crt
# 验证
certutil -L -d /etc/openldap/certs
# 修改配置文件
vim /etc/pam_ldap.conf
tls_cacertdir /etc/openldap/certs
Access denied for user xxxx by PAM account configuration
异常状态(SELINUX=enforcing导致的?待测试)
# 连接服务器后直接退出(一)
[root@test ~]# ssh zaza@127.0.0.1
zaza@127.0.0.1's password:
You are required to change your LDAP password immediately.
Connection closed by 127.0.0.1
# 连接服务器后直接退出(二)
[root@test ~]# ssh zaza@127.0.0.1
zaza@127.0.0.1's password:
Connection closed by 127.0.0.1
# secure相关日志
tailf /var/log/secure
Oct 20 20:42:16 test sshd[1452]: fatal: Access denied for user zaza by PAM account configuration
# 重启sshd,任然异常
/etc/init.d/sshd restart
# 重启服务器,任然异常
正常状态
# 调试环境就可以正常登录,并提示修改密码
/usr/sbin/sshd -p 2222 -d
# 或者:/etc/init.d/sshd stop && /usr/sbin/sshd -d
# 连接服务器
[root@test ~]# ssh zaza@127.0.0.1 -p 2222
zaza@127.0.0.1's password:
You are required to change your password immediately (root enforced)
You are required to change your LDAP password immediately.
Last login: Tue Oct 20 23:26:49 2020 from localhost
WARNING: Your password has expired.
You must change your password now and login again!
Changing password for user zaza.
Enter login(LDAP) password:
# 使用bash执行,就可以正常登录,并提示修改密码
bash /etc/init.d/sshd restart
# 连接服务器
[root@test ~]# ssh zaza@127.0.0.1
zaza@127.0.0.1's password:
You are required to change your password immediately (root enforced)
You are required to change your LDAP password immediately.
Last login: Tue Oct 20 23:35:53 2020 from localhost
WARNING: Your password has expired.
You must change your password now and login again!
Changing password for user zaza.
Enter login(LDAP) password:
完整的登录流程日志
Jul 8 05:36:18 test unix_chkpwd[1753]: password check failed for user (zaza) # 暴力破解校验
Jul 8 05:36:18 test sshd[1751]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=localhost user=zaza # 本地验证失败
Jul 8 05:36:29 test unix_chkpwd[1754]: account zaza has password changed in future # 暴力破解校验
Jul 8 05:36:29 test sshd[1751]: Accepted password for zaza from 127.0.0.1 port 43997 ssh2 # 远程校验成功
Jul 8 05:36:29 test sshd[1751]: pam_unix(sshd:session): session opened for user zaza by (uid=0) # session走本地流程
Jul 8 05:36:52 test sshd[1756]: Received disconnect from 127.0.0.1: 11: disconnected by user # 远程退出
Jul 8 05:36:52 test sshd[1751]: pam_unix(sshd:session): session closed for user zaza # 关闭session信息
参考文档
- 原文作者:zaza
- 原文链接:https://zazayaya.github.io/2021/05/20/openldap-tutorial-by-centos.html
- 说明:转载本站文章请标明出处,部分资源来源于网络,如有侵权请及时与我联系!