前言
LDAP(Lightweight Directory Access Protocol),轻型目录访问协议, 具体请阅读官方文档,深入了解还需阅读RFC4510
目前主要使用场景为集中账户管理,对于管理的帐号未达到一定数量级时可能对这个的需求并不是很迫切,但当发展到一定层度的时期,就会很迫切的需要有一种集中管理帐号的方案。
如果资源不是特别紧张,建议初期就使用集中管理帐号方案,这样当发展到一定数量级时,只要维护好这套系统即可,若没有发展起来,也不会带来过多的额外工作,总之好处多多。
约定
系统:centos 6.5
软件版本:openldap-2.4.46
官网地址:http://www.openldap.org
源码目录:/usr/local/src/
安装目录:/data/openldap/
配置文件:/data/conf/
证书路径:/etc/openldap/ssl/
数据库:mdb
准备
安装编译依赖,下载、解压源码
yum install gcc gcc-c++ libtool-ltdl-devel openssl-devel cyrus-sasl-devel libgcrypt-devel libicu-devel openslp-devel
cd /usr/local/src
wget ftp://ftp.openldap.org/pub/OpenLDAP/openldap-release/openldap-2.4.46.tgz
tar zxf openldap-2.4.46.tgz
编译、安装
可通过./configure --help 查看编译选项及参数
默认启动bdb、hdb、mdb库,这里关闭bdb、hdb,使用mdb
cd openldap-2.4.46
./configure --prefix=/data/openldap --sysconfdir=/data/conf/ --enable-bdb=no --enable-hdb=no --enable-accesslog --enable-auditlog --enable-syslog --enable-modules --enable-debug --with-tls --enable-overlays=yes --enable-ppolicy=mod --enable-crypt
make depend
make
make install
--prefix:指定安装目录
--enable-bdb=no:关闭bdb 数据库
--enable-hdb=no:关闭hdb 数据库
--enable-modules=yes:启用动态模块
--enable-overlays:激活所有overlay
修改配置文件
此处仅列出需增加或修改的内容
此配置设定了TLS证书、访问权限、ppolicy默认规则、索引等
slapd.conf
include /DATA/conf/openldap/schema/collective.schema
include /DATA/conf/openldap/schema/corba.schema
include /DATA/conf/openldap/schema/cosine.schema
include /DATA/conf/openldap/schema/duaconf.schema
include /DATA/conf/openldap/schema/dyngroup.schema
include /DATA/conf/openldap/schema/inetorgperson.schema
include /DATA/conf/openldap/schema/java.schema
include /DATA/conf/openldap/schema/misc.schema
include /DATA/conf/openldap/schema/nis.schema
include /DATA/conf/openldap/schema/openldap.schema
include /DATA/conf/openldap/schema/pmi.schema
include /DATA/conf/openldap/schema/ppolicy.schema
loglevel 256
logfile /DATA/logs/openldap/slapd.log
moduleload ppolicy.la
moduleload ppolicy.so
# Certificate/SSL Section
TLSVerifyClient never
TLSCipherSuite DEFAULT
TLSCertificateFile /etc/openldap/ssl/CAcert.pem
TLSCertificateKeyFile /etc/openldap/ssl/CAkey.pem
access to attrs=userPassword,givenName,sn
by self =xw
by anonymous auth
by dn.base="cn=admin,dc=example,dc=com" write
by dn.base="cn=Manager,dc=example,dc=com" write
by * none
access to *
by self read
by dn.base="cn=admin,dc=example,dc=com" write
by dn.base="cn=Manager,dc=example,dc=com" write
by * read
# enable on-the-fly configuration (cn=config)
database config
access to *
by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" manage
by * none
# enable server status monitoring (cn=monitor)
database monitor
access to *
by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" read
by dn.exact="cn=Manager,dc=example,dc=com" read
by * none
#######################################################################
# MDB database definitions
#######################################################################
database mdb
maxsize 1073741824
suffix "dc=example,dc=com"
rootdn "cn=Manager,dc=example,dc=com"
# Cleartext passwords, especially for the rootdn, should
# be avoid. See slappasswd(8) and slapd.conf(5) for details.
# Use of strong authentication encouraged.
rootpw {SSHA}zrXUepdOBZmP1c4qHQdbaqFJ8nhB++Fk
# The database directory MUST exist prior to running slapd AND
# should only be accessible by the slapd and slap tools.
# Mode 700 recommended.
directory /data/opt/openldap/var/openldap-data
#此段一定要加到database之后,否则会报错
overlay ppolicy
ppolicy_default "cn=default,ou=pwpolicies,dc=example,dc=com"
ppolicy_hash_cleartext
ppolicy_use_lockout
# Indices to maintain
index objectClass eq,pres
index ou,cn,mail,surname,givenname eq,pres,sub
index uidNumber,gidNumber,loginShell eq,pres
index uid,memberUid eq,pres,sub
index nisMapName,nisMapEntry eq,pres,sub
检测配置文件
/data/openldap/sbin/slaptest -f /data/conf/openldap/slapd.conf -u
配置文件无误输出下行,如有错误会有具体信息提示
config file testing succeeded
生成动态配置文件
OpenLDAP 2.3及更高版本,使用运行时动态配置,...简而言之就是修改配置后无需重启...
将slapd.conf 配置文件生成为动态配置文件,目录为slapd.d
/data/openldap/sbin/slaptest -f /data/conf/openldap/slapd.conf -F /data/conf/openldap/slapd.d
生成证书文件
生成顶级CA,支持TLS 协议
cd /etc/openldap/ssl/
openssl req -new -x509 -nodes -out CAcert.pem -keyout CAkey.pem -days 365
准备基础用户及组文件
此处创建ldap用户及组是通过写ldif文件实现
创建一个DN,一个rootdn用户,两个组People、Groups,一个管理员用户admin,一个密码规则相关帐号
cat ~/base.ldif
# example.com
dn: dc=example,dc=com
dc: example
o: liepass.Inc
objectClass: dcObject
objectClass: organization
# Manager,example.com
dn: cn=Manager,dc=example,dc=com
cn: Manager
description: LDAP administrator
objectClass: organizationalRole
objectClass: top
roleOccupant: dc=example,dc=com
# People,example.com
dn: ou=People,dc=example,dc=com
ou: People
objectClass: top
objectClass: organizationalUnit
# Groups,example.com
dn: ou=Groups,dc=example,dc=com
ou: Groups
objectClass: top
objectClass: organizationalUnit
# administrators,example.com
dn: cn=admin,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: shadowAccount
cn: admin
sn: admin
uid: admin
userPassword: {SSHA}RCBSFS+fgrS/a3VvO3pwEmSj8G5d68nd
dn: ou=pwpolicies,dc=example,dc=com
ou: pwpolicies
objectClass: top
objectClass: organizationalUnit
# add default policy to DIT
# attributes preceded with # indicate the defaults and
# can be omitted
# passwords must be reset every 30 days,
# have a minimum length of 6 and users will
# get a expiry warning starting 1 hour before
# expiry, when the consecutive fail attempts exceed 5
# the count will be locked and can only be reset by an
# administrator, users do not need to supply the old
# password when changing
dn: cn=default,ou=pwpolicies,dc=example,dc=com
objectClass: pwdPolicy
objectClass: person
objectClass: shadowAccount
objectClass: organizationalPerson
uid: default
sn: default
cn: default
pwdAttribute: userPassword
pwdMaxAge: 2592000
pwdExpireWarning: 3600
pwdInHistory: 3
#pwdCheckQuality: 0
pwdMaxFailure: 5
pwdLockout: TRUE
#pwdLockoutDuration: 0
#pwdGraceAuthNLimit: 0
#pwdFailureCountInterval: 0
pwdMustChange: TRUE
pwdMinLength: 8
pwdAllowUserChange: TRUE
pwdSafeModify: FALSE
启动服务,并初始化ldif 文件
前台启动服务
/data/openldap/libexec/slapd -F /DATA/conf/openldap/slapd.d/ -h "ldap:/// ldaps:/// ldapi:///" -d 256
ldap:389
ldaps:636
-d:日志级别
需要注意,TLS 协议使用的是ldap:389,不是ldaps:636
执行base.ldif
/data/openldap/bin/ldapadd -D "cn=Manager,dc=example,dc=com" -W -f ~/base.ldif
创建系统服务
根据实际情况修改如下变量
slapd=/usr/sbin/slapd
slaptest=/usr/sbin/slaptest
lockfile=/var/lock/subsys/slapd
configdir=/etc/openldap/slapd.d/
configfile=/etc/openldap/slapd.conf
pidfile=/var/run/slapd.pid
slapd_pidfile=/var/run/openldap/slapd.pid
cat /etc/init.d/slapd
#!/bin/bash
#
# slapd This shell script takes care of starting and stopping
# ldap servers (slapd).
#
# chkconfig: - 27 73
# description: LDAP stands for Lightweight Directory Access Protocol, used \
# for implementing the industry standard directory services.
# processname: slapd
# config: /etc/openldap/slapd.conf
# pidfile: /var/run/slapd.pid
### BEGIN INIT INFO
# Provides: slapd
# Required-Start: $network $local_fs
# Required-Stop: $network $local_fs
# Should-Start:
# Should-Stop:
# Default-Start:
# Default-Stop:
# Short-Description: starts and stopd OpenLDAP server daemon
# Description: LDAP stands for Lightweight Directory Access Protocol, used
# for implementing the industry standard directory services.
### END INIT INFO
# Source function library.
. /etc/init.d/functions
# Define default values of options allowed in /etc/sysconfig/ldap
SLAPD_LDAP="yes"
SLAPD_LDAPI="no"
SLAPD_LDAPS="no"
SLAPD_URLS=""
SLAPD_SHUTDOWN_TIMEOUT=3
# OPTIONS, SLAPD_OPTIONS and KTB5_KTNAME are not defined
# Source an auxiliary options file if we have one
if [ -r /etc/sysconfig/ldap ] ; then
. /etc/sysconfig/ldap
fi
slapd=/usr/sbin/slapd
slaptest=/usr/sbin/slaptest
lockfile=/var/lock/subsys/slapd
configdir=/etc/openldap/slapd.d/
configfile=/etc/openldap/slapd.conf
pidfile=/var/run/slapd.pid
slapd_pidfile=/var/run/openldap/slapd.pid
RETVAL=0
#
# Pass commands given in $2 and later to "test" run as user given in $1.
#
function testasuser() {
local user= cmd=
user="$1"
shift
cmd="$@"
if test x"$user" != x ; then
if test x"$cmd" != x ; then
/sbin/runuser -f -m -s /bin/sh -c "test $cmd" -- "$user"
else
false
fi
else
false
fi
}
#
# Check for read-access errors for the user given in $1 for a service named $2.
# If $3 is specified, the command is run if "klist" can't be found.
#
function checkkeytab() {
local user= service= klist= default=
user="$1"
service="$2"
default="${3:-false}"
if test -x /usr/kerberos/bin/klist ; then
klist=/usr/kerberos/bin/klist
elif test -x /usr/bin/klist ; then
klist=/usr/bin/klist
fi
KRB5_KTNAME="${KRB5_KTNAME:-/etc/krb5.keytab}"
export KRB5_KTNAME
if test -s "$KRB5_KTNAME" ; then
if test x"$klist" != x ; then
if LANG=C $klist -k "$KRB5_KTNAME" | tail -n 4 | awk '{print $2}' | grep -q ^"$service"/ ; then
if ! testasuser "$user" -r ${KRB5_KTNAME:-/etc/krb5.keytab} ; then
true
else
false
fi
else
false
fi
else
$default
fi
else
false
fi
}
function configtest() {
local user= ldapuid= dbdir= file=
# Check for simple-but-common errors.
user=ldap
prog=`basename ${slapd}`
ldapuid=`id -u $user`
# Unaccessible database files.
dbdirs=""
if [ -d $configdir ]; then
for configfile in `ls -1 $configdir/cn\=config/olcDatabase*.ldif`; do
dbdirs=$dbdirs"
"`LANG=C egrep '^olcDbDirectory[[:space:]]*:[[:space:]]+[[:print:]]+$' $configfile | sed 's,^olcDbDirectory: ,,'`
done
elif [ -f $configfile ]; then
dbdirs=`LANG=C egrep '^directory[[:space:]]+' $configfile | sed 's,^directory[[:space:]]*,,' | tr -d \"`
else
exit 6
fi
for dbdir in $dbdirs; do
if [ ! -d $dbdir ]; then
exit 6
fi
for file in `find ${dbdir}/ -not -uid $ldapuid -and \( -name "*.dbb" -or -name "*.gdbm" -or -name "*.bdb" -or -name "__db.*" -or -name "log.*" -or -name alock \)` ; do
echo -n $"$file is not owned by \"$user\"" ; warning ; echo
done
if test -f "${dbdir}/DB_CONFIG"; then
if ! testasuser $user -r "${dbdir}/DB_CONFIG"; then
file=DB_CONFIG
echo -n $"$file is not readable by \"$user\"" ; warning ; echo
fi
fi
done
# Unaccessible keytab with an "ldap" key.
if checkkeytab $user ldap ; then
file=${KRB5_KTNAME:-/etc/krb5.keytab}
echo -n $"$file is not readable by \"$user\"" ; warning ; echo
fi
# Check the configuration file.
slaptestout=`/sbin/runuser -m -s "$slaptest" -- "$user" "-u" 2>&1`
slaptestexit=$?
# slaptestout=`echo $slaptestout 2>/dev/null | grep -v "config file testing succeeded"`
# print warning if slaptest passed but reports some problems
if test $slaptestexit == 0 ; then
if echo "$slaptestout" | grep -v "config file testing succeeded" >/dev/null ; then
echo -n $"Checking configuration files for $prog: " ; warning ; echo
echo "$slaptestout"
fi
fi
# report error if configuration file is wrong
if test $slaptestexit != 0 ; then
echo -n $"Checking configuration files for $prog: " ; failure ; echo
echo "$slaptestout"
if /sbin/runuser -m -s "$slaptest" -- "$user" "-u" > /dev/null 2> /dev/null ; then
#dirs=`LANG=C egrep '^directory[[:space:]]+[[:print:]]+$' $configfile | awk '{print $2}'`
for directory in $dbdirs ; do
if test -r $directory/__db.001 ; then
echo -n $"stale lock files may be present in $directory" ; warning ; echo
fi
done
fi
exit 6
fi
}
function start() {
[ -x $slapd ] || exit 5
[ `id -u` -eq 0 ] || exit 4
configtest
# Define a couple of local variables which we'll need. Maybe.
user=ldap
prog=`basename ${slapd}`
harg="$SLAPD_URLS"
if test x$SLAPD_LDAP = xyes ; then
harg="$harg ldap:///"
fi
if test x$SLAPD_LDAPS = xyes ; then
harg="$harg ldaps:///"
fi
if test x$SLAPD_LDAPI = xyes ; then
harg="$harg ldapi:///"
fi
# System resources limit.
if [ -n "$SLAPD_ULIMIT_SETTINGS" ]; then
ulimit="ulimit $SLAPD_ULIMIT_SETTINGS &>/dev/null;"
else
ulimit=""
fi
# Release reserverd port
[ -x /sbin/portrelease ] && /sbin/portrelease slapd &>/dev/null || :
# Start daemons.
echo -n $"Starting $prog: "
daemon --pidfile=$pidfile --check=$prog $ulimit ${slapd} -h "\"$harg\"" -u ${user} $OPTIONS $SLAPD_OPTIONS
RETVAL=$?
if [ $RETVAL -eq 0 ]; then
touch $lockfile
ln $slapd_pidfile $pidfile
fi
echo
return $RETVAL
}
function stop() {
# Stop daemons.
prog=`basename ${slapd}`
[ `id -u` -eq 0 ] || exit 4
echo -n $"Stopping $prog: "
# This will remove pid and args files from /var/run/openldap
killproc -p $slapd_pidfile -d $SLAPD_SHUTDOWN_TIMEOUT ${slapd}
RETVAL=$?
# Now we want to remove lock file and hardlink of pid file
[ $RETVAL -eq 0 ] && rm -f $pidfile $lockfile
echo
return $RETVAL
}
# See how we were called.
case "$1" in
configtest)
configtest
;;
start)
start
RETVAL=$?
;;
stop)
stop
RETVAL=$?
;;
status)
status -p $pidfile ${slapd}
RETVAL=$?
;;
restart|force-reload)
stop
start
RETVAL=$?
;;
condrestart|try-restart)
status -p $pidfile ${slapd} > /dev/null 2>&1 || exit 0
stop
start
;;
usage)
echo $"Usage: $0 {start|stop|restart|force-reload|status|condrestart|try-restart|configtest|usage}"
RETVAL=0
;;
*)
echo $"Usage: $0 {start|stop|restart|force-reload|status|condrestart|try-restart|configtest|usage}"
RETVAL=2
esac
exit $RETVAL
添加系统服务
chmod +x /etc/init.d/slapd && chkconfig slapd on
部署web 管理应用
可选web 有phpldapadmin、ldap-account-manager,ldapadmin
本文不对具体安装(可见官方文档)做说明,只对需要注意的点分别做说明
phpldapadmin
请先copy并编辑config.php.example 文件
读取的SSL/TLS配置文件是/etc/openldap/ldap.conf增加如下内容
# 证书路径
TLS_CACERTDIR /etc/openldap/ssl/
TLS_REQCERT allow
ERROR 1
Fatal error: Cannot redeclare password_hash() in /DATA/html/phpldap/lib/functions.php on line 2236
修改lib/functions.php 文件
将password_hash全部替换为password_hash_custom
sed -i "s/password_hash/password_hash_custom/g" lib/functions.php
sed -i "s/password_hash/password_hash_custom/g" lib/TemplateRender.php
sed -i "s/password_hash/password_hash_custom/g" lib/ds_ldap_pla.php
sed -i "s/password_hash/password_hash_custom/g" lib/PageRender.php
sed -i "s/password_hash/password_hash_custom/g" config/config.php
ERROR 2
Unrecognized error number: 8192: preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead
修改lib/functions.php 2568 2573
< $a[$key] = preg_replace('/\\\([0-9A-Fa-f]{2})/e',"''.chr(hexdec('\\1')).''",$rdn);
---
> $a[$key] = preg_replace_callback('/\\\([0-9A-Fa-f]{2})/',function(){return "''.chr(hexdec('\\1')).''";},$rdn);
< return preg_replace('/\\\([0-9A-Fa-f]{2})/e',"''.chr(hexdec('\\1')).''",$dn);
---
> return preg_replace_callback('/\\\([0-9A-Fa-f]{2})/',function(){return "''.chr(hexdec('\\1')).''";},$dn);
修改lib/ds_ldap.php 1120 1125
< $a[$key] = preg_replace('/\\\([0-9A-Fa-f]{2})/e',"''.chr(hexdec('\\1')).''",$rdn);
---
> $a[$key] = preg_replace_callback('/\\\([0-9A-Fa-f]{2})/',function(){return "''.chr(hexdec('\\1')).''";},$rdn);
< return preg_replace('/\\\([0-9A-Fa-f]{2})/e',"''.chr(hexdec('\\1')).''",$dn);
---
> return preg_replace_callback('/\\\([0-9A-Fa-f]{2})/',function(){return "''.chr(hexdec('\\1')).''";},$dn);
参考:https://bugs.launchpad.net/ubuntu/+source/phpldapadmin/+bug/1241425/comments/4
ldap-account-manager
ldap-account-manager读取的SSL/TLS配置文件是/etc/ldap.conf增加如下内容
# 证书路径
TLS_CACERTDIR /etc/openldap/ssl/
TLS_REQCERT allow
LDAP 用户自行修改密码 web工具
https://ltb-project.org/download
想要使用,需要修改配置文件内keyphrase字段对应的默认值$keyphrase = "secret";
随意修改,只要不是默认的值就行,否则会报错
Token encryption requires a random string in keyphrase setting
参考
openldap 官方文档:http://www.openldap.org/doc/admin24/index.html
TLS 配置说明:http://www.openldap.org/faq/data/cache/185.html
生成CA:https://blog.csdn.net/howeverpf/article/details/21622545?reload
Archlinux:https://wiki.archlinux.org/index.php/OpenLDAP
Archlinux:https://wiki.archlinux.org/index.php/LDAP_authentication
TLS 1.3概述:http://www.inforsec.org/wp/?p=1960
objectClass 介绍:https://blog.csdn.net/qq_27376871/article/details/52037317
ppolicy:http://www.zytrax.com/books/ldap/ch6/ppolicy.html
phpldapadmin相关:
http://permalink.gmane.org/gmane.comp.ldap.davedap/4937
http://forums.debian.net/viewtopic.php?f=5&t=111508