LDAP+SSSD 部署
选择 openldap 与 sssd 的理由
最近有在不同 Linux 节点(非集群)上统一管理用户的需求,这个时候开始觉得 Windows AD 确实很方便。其实 Linux 机器也可以加入 AD 域, samba 也有相关的功能,暂时还没有探索多少。
NIS 也是一个轻量的目录服务协议,但配置起来相对死板麻烦。值得注意的是, Ubuntu 22.04 中将 nis 版本更新到了 4 ,配置方式和之前有很大不同,在配置时踩了很多坑。供参考的资料大多也不适用于现在的情况,加上不太能搞懂组件间的联系等,最后放弃了这条道路。
FreeIPA 是 RH 牵头搞的、全家桶型的身份管理系统。它不仅内置了 bind 来配置 DNS ,有 acme server 来签发证书,支持 kerberos 认证,还有一个 Web 界面来调整设置,非常方便。可以说自己配一套配置到最后,也是需要配好这一坨东西。但由于 bind 的版本依赖问题, Ubuntu 竟然从 20.04 开始就没打包 freeipa-server
这个包了。 Docker 版本也试了下,第一次启动很慢,运行时占用资源也稍多,启动中间还出了很多错误,暂时也没有精力去排查,更没有换 rh 系发行版的打算,只能搁置了。另外 Docker 封装里有 systemd ,这也是个小问题。
由于需求其实比较简单,服务器区域准备设置防火墙并没有公开服务,也没有必要去搞得很复杂。所以最后决定自己搭一套 LDAP 。
最开始尝试的是 ApacheDS ,这是一个用 Java 写的 LDAP 和 Kerberos 服务器。 Ubuntu 里有 ApacheDS 这个包,和 Debian 上游的完全一样,官方源里面这个包可谓非常之坑,我不懂 Java ,它对我来说就更坑了:一个是假如存密码用了 CRYPT 系列的编码,那么 ApacheDS 就完全登录不了了,提示 NoClassDefFoundError 。看了下是缺少 libcommons-codec-java
这个包的库,但它已经作为依赖被安装了。在我手动把这个库的 jar 复制到 ApacheDS 所在的 lib 目录后,这个问题解决了。但当设置 TLS 的时候,又出现了 No Cipher Suites in Common
的问题,搜了下网上的意思,都是说应该是别的地方出问题,这下我没有魔改的思路了。但这是发行版打包的问题,因为如果安装并使用的是 ApacheDS 自己打包的 deb ,那就完全不会出现这些问题。他们自己打包的 deb 没有 systemd 整合,其实这还是小问题。关键是他没有源,需要三天两头去检查升级。发行版都有 security 更新,直接更新就好了,这自己去检查可太累了。再加上之前 log4j 爆出问题,谁知道这么一坨没有时常更新的服务什么时候会炸。浅瞄一眼没有合适的 Docker 封装,算了算了。
最后尝试了最经典的 openldap ,还是它好用啊,一路下来基本没出现过啥问题,妙啊。
SSSD 可以缓存登录凭据,这样认证服务器挂了(在我的场景下)问题也不算还大,暂时没看 pam-ldap 是否有同样的选项。据说 sssd 还有其他高级功能,没研究。
openldap server 的配置
设置 hostname 和 FQDN
首先设置好 hostname 和 FQDN 。因为 sssd 需要加密,加密需要证书。
$ sudo hostnamectl hostname ldap
$ sudo vim /etc/hosts
...
127.0.0.1 localhost.localdomain localhost
127.0.0.1 ldap.example.com ldap
...
检查设置:
$ hostname
ldap
$ hostname -f
ldap.example.com
设置 openldap server
$ sudo apt update
$ sudo apt install slapd ldap-utils
安装中可以跳过 openldap 服务器设置,安装完后再进行:
$ sudo dpkg-reconfigure slapd
- Omit OpenLDAP server configuration? No
- DNS domain name: example.com
- Organization name: Example Organization
- Password
- Do you want the database to be removed when slapd is purged? No ,这里根据需要
- Move old database? Yes ,防手滑
创建证书
如果有证书就可以跳过自签证书的步骤,直接放好证书给服务用就好了。
$ sudo apt install gnutls-bin ssl-cert
创建证书模板:
$ sudo mkdir /etc/ssl/templates
/etc/ssl/templates/ca_server.conf
:
cn = LDAP Service CA
ca
cert_signing_key
expiration_days = 3650
/etc/ssl/templates/ldap_server.conf
:
organization = "Example Organization"
cn = ldap.example.com
tls_www_server
encryption_key
signing_key
expiration_days = 3650
创建 CA 私钥:
$ sudo certtool -p --outfile /etc/ssl/private/ca_server.key
创建 CA 证书:
$ sudo certtool -s --load-privkey /etc/ssl/private/ca_server.key --template /etc/ssl/templates/ca_server.conf --outfile /etc/ssl/certs/ca_server.pem
域名的私钥:
$ sudo certtool -p --sec-param high --outfile /etc/ssl/private/ldap_server.key
域名的证书:
$ sudo certtool -c --load-privkey /etc/ssl/private/ldap_server.key --load-ca-certificate /etc/ssl/certs/ca_server.pem --load-ca-privkey /etc/ssl/private/ca_server.key --template /etc/ssl/templates/ldap_server.conf --outfile /etc/ssl/certs/ldap_server.pem
让服务有权限访问这个证书:
$ sudo mkdir /etc/ssl/slapd
$ sudo cp /etc/ssl/private/ldap_server.key /etc/ssl/slapd/
$ sudo chown -R openldap /etc/ssl/slapd/
$ sudo chmod 710 /etc/ssl/slapd
配置 openldap server 使用证书
创建 addcerts.ldif
:
dn: cn=config
changetype: modify
add: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/ssl/certs/ca_server.pem
-
add: olcTLSCertificateFile
olcTLSCertificateFile: /etc/ssl/certs/ldap_server.pem
-
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/ssl/private/ldap_server.key
顺序必须是: CA 证书、服务证书、服务私钥,不然报实现自定义错误。证书权限忘调整也报错。
应用更改:
$ sudo ldapmodify -H ldapi:// -Y EXTERNAL -f addcerts.ldif
$ sudo systemctl restart slapd
openldap 客户机配置
如果是自签发证书,需要信任自己的 CA 证书:
$ sudo cp /etc/ssl/certs/ca_server.pem /etc/ldap/ca_certs.pem
编辑 /etc/ldap/ldap.conf
,调整 CA 证书文件:
TLS_CACERT /etc/ldap/ca_certs.pem
测试 STARTTLS 通信:
$ ldapwhoami -H ldap:// -x -ZZ
anonymous
由于 ldaps://
已经过时,不应该再使用。
配置 sssd 认证登录
安装软件包:
$ sudo apt-get install sssd-ldap ldap-utils
创建 /etc/sssd/sssd.conf
,权限 0600 ,所有权 root:root :
[sssd]
config_file_version = 2
domains = example.com
[domain/example.com]
id_provider = ldap
auth_provider = ldap
chpass_provider = ldap
ldap_uri = ldap://ldap.example.com
cache_credentials = True
ldap_search_base = dc=example,dc=com
[pam]
offline_credentials_expiration = 60000
启动 sssd 服务:
$ sudo systemctl start sssd
自动创建家目录:
$ sudo pam-auth-update --enable mkhomedir
在 LDAP 上创建用户并测试
推荐 Apache Directory Studio 作为 GUI 工具,不用也没啥。
dn: dc=example,dc=com
objectClass: dcObject
objectClass: organization
objectClass: top
dc: example
o: example.com
dn: ou=People,dc=example,dc=com
objectClass: organizationalUnit
objectClass: top
ou: People
dn: ou=Group,dc=example,dc=com
objectClass: organizationalUnit
objectClass: top
ou: Group
dn: uid=usera,ou=People,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: posixAccount
cn: User A
gidNumber: 10001
homeDirectory: /home/usera
sn: usera
uid: usera
uidNumber: 10001
loginShell: /bin/bash
userPassword: wow
dn: cn=usera,ou=Group,dc=example,dc=com
objectClass: posixGroup
cn: usera
gidNumber: 10001
导入后,在 ldap 客户机上应该能查询到相关用户,并且以该用户身份登录:
$ getent passwd usera
usera:*:10001:10001:User A:/home/usera:/bin/bash
$ id usera
uid=10001(usera) gid=10001(usera) groups=10001(usera)
SSH 配置
LDAP Server
修改 LDAP 的 schema ,加上用户的公钥字段:
dn: cn=openssh-lpk,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: openssh-lpk
olcAttributeTypes: ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey'
DESC 'MANDATORY: OpenSSH Public key'
EQUALITY octetStringMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
olcObjectClasses: ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY
DESC 'MANDATORY: OpenSSH LPK objectclass'
MAY ( sshPublicKey $ uid )
)
Client
新建 /usr/local/bin/ssh-keyldap
:
#!/bin/sh
# vim: set ts=4:
#
# This script finds and prints authorized SSH public keys in LDAP for the
# username specified as the first argument.
#
# The program must be owned by root and not writable by group or others.
# It expects configuration file /etc/ssh/ldap.conf in format of ldap.conf(5).
#
# sshd_config for OpenSSH 6.2+:
#
# AuthorizedKeysCommand /usr/local/bin/ssh-keyldap
# AuthorizedKeysCommandUser nobody
#
set -eu
LDAPCONF='/etc/ssh/ldap.conf'
log() {
logger -s -t sshd -p "auth.$1" "$2"
}
uid="$1"
export LDAPCONF
if [ ! -r "$LDAPCONF" ]; then
log err "file $LDAPCONF does not exist or not readable"
exit 1
fi
if ! expr "$uid" : '[a-zA-Z0-9._-]*$' 1>/dev/null; then
log err "bad characters in username: $uid"
exit 2
fi
keys=$(ldapsearch -x -LLL -ZZ -o ldif-wrap=no "(&(uid=$uid)(sshPublicKey=*))" \
'sshPublicKey' | sed -n 's/^sshPublicKey:\s*\(.*\)$/\1/p')
keys_count=$(echo "$keys" | grep '^ssh' | wc -l)
log info "Loaded $keys_count SSH public key(s) from LDAP for user: $uid"
echo "$keys"
新建 /etc/ssh/ldap.conf
:
# /etc/ssh/ldap.conf
# See ldap.conf(5) for details
# This file should be world readable but not world writable.
BASE ou=people,dc=itszzz,dc=top
URI ldap://hn00.itszzz.top
编辑 /etc/ssh/sshd_config
,加入:
AuthorizedKeysCommand /usr/local/bin/ssh-keyldap
AuthorizedKeysCommandUser nobody