打造统一的 Kerberos 和 OpenLDAP 服务

背景

什么是 LDAP,它用来做什么?

LDAP 是轻量目录访问协议,英文全称是 Lightweight Directory Access Protocol,一般都简称为 LDAP。它是基于 X.500 标准的,但是简单多了并且可以根据需要定制。与 X.500 不同,LDAP 支持 TCP/IP,这对访问 Internet 是必须的。LDAP 的核心规范在 RFC 中都有定义,所有与 LDAP 相关的 RFC 都可以在 LDAPman RFC 网页中找到。LDAP 最新的 RFC 规范文档是 『RFC 4511 Lightweight Directory Access Protocol (LDAP): The Protocol』。

什么是 Kerberos,它用来做什么?

Kerberos 这一名词来源于希腊神话『三个头的狗--地狱之门守护者』系统设计上采用客户端/服务器结构与 DES 加密技术,并且能够进行相互认证,即客户端和服务器端均可对对方进行身份认证。可以用于防止窃听、防止 replay 攻击、保护数据完整性等场合,是一种应用对称密钥体制进行密钥管理的系统。

Kerberos 是一种网络认证协议,其设计目标是通过密钥系统为客户机/服务器应用程序提供强大的认证服务。该认证过程的实现不依赖于主机操作系统的认证,无需基于主机地址的信任,不要求网络上所有主机的物理安全,并假定网络上传送的数据包可以被任意地读取、修改和插入数据。在以上情况下, Kerberos 作为一种可信任的第三方认证服务,是通过传统的密码技术(如:共享密钥)执行认证服务的。

使用Kerberos时,一个客户端需要经过三个步骤来获取服务:

  1. 认证:客户端向认证服务器发送一条报文,并获取一个含时间戳的 Ticket-Granting Ticket(TGT)。

  2. 授权:客户端使用 TGT 向 Ticket-Granting Server(TGS)请求一个服务 Ticket。

  3. 服务请求:客户端向服务器出示服务 Ticket,以证实自己的合法性。该服务器提供客户端所需服务,在 Hadoop 应用中,服务器可以是 namenode 或 jobtracker。

为此,Kerberos 需要 The Key Distribution Centers(KDC)来进行认证。KDC 只有一个 Master,可以带多个 slaves 机器。slaves 机器仅进行普通验证。Mater 上做的修改需要自动同步到 slaves。

另外,KDC 需要一个 admin,来进行日常的管理操作。这个 admin 可以通过远程或者本地方式登录。

什么是 SASL?

简单认证和安全层(Simple Authentication and Security Layer),也是一套 RFC 定义的标准。它的核心思想是把用户认证和安全传输从应用程序中隔离出来。像 SMTP 协议在定义之初都没有考虑到用户认证等问题,现在 SMTP 可以配置使用 SASL 来完成这方面的工作。

SASL支持多种认证方法,比如:

  • ANONYMOUS:无需认证。

  • PLAIN:明文密码方式(cleartext password)

  • DIGEST-MD5:HTTP Digest 兼容的安全机制,基于 MD5,可以提供数据的安全传输层。这个是方便性和安全性结合得最好的一种方式。也是默认的方式。

  • GSSAPI:Generic Security Services Application Program Interface

    GSSAPI 本身是一套 API,由 IETF 标准化,其最主要也是著名的实现是基于 Kerberos 的,所以一般说到 GSSAPI 都暗指 Kerberos 实现。

  • EXTERNAL:认证已经在环境中实现了,比如 SSL/TLS, IPSec。

我们的目标是什么?

LDAP 作为存储,Kerberos 作为网络认证协议。都是标准协议,有的系统支持 LDAP,有的系统支持 Kerberos,如果将二者打通的话,可以做到一套数据,多种认证方式,适配能力会更强。

OpenLDAP 服务器

安装 OpenLDAP 服务器

apt-get install -y slapd ldap-utils krb5-kdc-ldap

配置 OpenLDAP

使用 OpenLDAP 作为 Kerberos 存储的数据库,需要将 kerberos.schema 添加到 OpenLDAP 的 cn=config 树中。

  1. 新增配置文件 schema_convert.conf,输入下面的内容。

    include /etc/ldap/schema/core.schema
    include /etc/ldap/schema/collective.schema
    include /etc/ldap/schema/corba.schema
    include /etc/ldap/schema/cosine.schema
    include /etc/ldap/schema/duaconf.schema
    include /etc/ldap/schema/dyngroup.schema
    include /etc/ldap/schema/inetorgperson.schema
    include /etc/ldap/schema/java.schema
    include /etc/ldap/schema/misc.schema
    include /etc/ldap/schema/nis.schema
    include /etc/ldap/schema/openldap.schema
    include /etc/ldap/schema/ppolicy.schema
    include /etc/ldap/schema/kerberos.schema
  2. 创建临时目录,用来存放 LDIF 文件

    mkdir /tmp/ldif_output
  3. 使用 slapcat 命令转换上面指定的 schema 文件

    slapcat -f schema_convert.conf -F /tmp/ldif_output -n0 -s \
    "cn={12}kerberos,cn=schema,cn=config" > /tmp/cn=kerberos.ldif
  4. 修改生成的 /tpm/cn\=kerberos.ldif 文件,找到并修改下面的两行:

    dn: cn=kerberos,cn=schema,cn=config
    ...
    cn: kerberos

    另外,还需要移除位于文件末位的下面几行:

    structuralObjectClass: olcSchemaConfig
    entryUUID: 18ccd010-746b-102d-9fbe-3760cca765dc
    creatorsName: cn=config
    createTimestamp: 20090111203515Z
    entryCSN: 20090111203515.326445Z#000000#000#000000
    modifiersName: cn=config
    modifyTimestamp: 20090111203515Z
  5. 使用 slapadd 命令加载新的 schema

    ldapadd -Q -Y EXTERNAL -H ldapi:/// -f /tmp/cn\=kerberos.ldif
  6. krbPrincipalName 属性增加索引

    ldapmodify -Q -Y EXTERNAL -H ldapi:/// <<EOF
    dn: olcDatabase={1}hdb,cn=config
    add: olcDbIndex
    olcDbIndex: krbPrincipalName eq,pres,sub
    EOF
  7. 最后,更新访问控制清单(ACL)

    ldapmodify -Q -Y EXTERNAL -H ldapi:///
    Enter LDAP Password:
    dn: olcDatabase={1}hdb,cn=config
    replace: olcAccess
    olcAccess: to attrs=userPassword,shadowLastChange,krbPrincipalKey by
        dn="cn=admin,dc=example,dc=com" write by anonymous auth by self write by * none
    -
    add: olcAccess
    olcAccess: to dn.base="" by * read
    -
    add: olcAccess
    olcAccess: to * by dn="cn=admin,dc=example,dc=com" write by * read
    
    modifying entry "olcDatabase={1}hdb,cn=config"

Kerberos 安装

krb5-user krb5-kdc krb5-admin-server krb5-kdc-ldap

配置

Note
以 7V1.NET 域名为例。
  1. 在配置文件 /etc/krb5.conf 中增加 [dbmodules] 小节,加入一下内容:

    /etc/krb5.conf 中添加的内容
    [dbmodules]
    openldap_ldapconf = {
        db_library = kldap
        ldap_kdc_dn = "$ldap_kdc_dn"
        # this object needs to have read rights on
        # the realm container, principal container and realm sub-trees
        ldap_kadmind_dn = "$ldap_kadmind_dn"
        # this object needs to have read and write rights on
        # the realm container, principal container and realm sub-trees
        ldap_service_password_file = $ldap_service_password_file
        ldap_servers = ldap://$LDAP_HOST
        ldap_conns_per_server = 5
    }
    Note
    1. $ldap_kdc_dn 指的是『KDC』服务将采用哪个 OpenLDAP DN与 OpenLDAP 服务通信。

    2. $ldap_kadmind_dn 指的是『KADMIND』服务将采用哪个 OpenLDAP DN与 OpenLDAP 服务通信。

    3. $ldap_service_password_file 表示存储 OpenLDAP DN 密码的文件路径。

    4. $LDAP_HOST 表示 OpenLDAP 的服务器地址。

  2. /etc/krb5.conf[realms] 小节中将特定的『REALM』(如 7V1.NET)的 database_module 设置为刚才添加的 openldap_ldapconf

    [realms]
    7V1.NET = {
        kdc = $KDC_ADDRESS
        admin_server = $KDC_ADDRESS
        default_domain = $DOMAIN_REALM
        database_module = openldap_ldapconf
    }
  3. 使用 kdb5_ldap_util 命令来初始化 Kerberos 数据库

    为 OpenLDAP DN 生成密码文件
    kdb5_ldap_util -D $ADMIN_DN -w $ADMIN_PW stashsrvpw -f $ldap_service_password_file $ADMIN_DN
    创建 Kerberos 数据库
    kdb5_ldap_util -D $ADMIN_DN -w $ADMIN_PW create -subtrees $DOMAIN_DN -r $KRB5REALM -s
Tip
小提示
  1. Kerberos 的 Principal 『惯例』

    1. host/${hostname} 表示一台机器,它的域名是 hostname。

    2. ldap/${hostname} 表示位于 hostname 上的一个 LDAP 服务,同理,还有 ssh/${hostname} 等。

    3. ${username}/admin 会在 username 使用 kadmin 时被 kadmin 读取。

  2. kadmin、kpasswd 等程序都会连接 krb5-admin-server 服务,需在服务器端开启 kadmind 服务,并在 /etc/krb5.conf 设置 admin_server

  3. kadmind 程序启动的时候需要进行 『Seeding random number generator』 操作,如果机器上 IO 比较少,生成过程非常缓慢,系统无法初始化完成。而此时客户端调用 kadmind 服务时会报出『kpasswd: Cannot contact any KDC for requested realm changing password』错误。

    注意观察日志文件中下面两行内容:

    Jul 28 11:35:51 bbdb76488f37 kadmind[443](info): Seeding random number generator
    Jul 28 11:38:49 bbdb76488f37 kadmind[443](info): starting

    如果特别缓慢,可以使用ping -f $HOSTNAME命令产生随机IO。

  4. Kerberos krb5-kdc 程序有『BUG』,如果使用类型 cn=kdc,cn=kerberos,dc=7v1,dc=net 等作为查询 DN 的话, 会出现崩溃,所以直接使用 RootDN 来搞啦。

saslauthd

安装

saslauthd 需要和 OpenLDAP 装在同一台机器上。

配置 saslauthd

  1. 修改配置文件 /etc/default/saslauthd

    START=yes
    DESC="SASL Authentication Daemon"
    NAME="saslauthd"
    MECHANISMS="kerberos5"
    MECH_OPTIONS=""
    THREADS=5
    OPTIONS="-c -m /var/run/saslauthd"
  2. 添加账号 『openldap』、『sasl』,赋予合适的权限

    sudo adduser openldap sasl
  3. 为本机生成 Kerberos Principal,并授权

    kadmin.local -q "ank -randkey host/`hostname`"
    kadmin.local -q "ktadd host/`hostname`"
  4. 测试

    testsaslauthd -u username -p password

配置 OpenLDAP 和 Kerberos

  1. 需要先把 /etc/krb5.conf 拷一份到 OpenLDAP 服务器。

    任何 Kerberos 的客户端机器都需要配置文件 /etc/krb5.conf

  2. 在 Kerberos 中为 OpenLDAP 服务器添加 Principal。

    kadmin.local -q "ank -randkey ldap/`hostname`"
    kadmin.local -q "ktadd ldap/`hostname`"
  3. 新增 /etc/ldap/sals2/slapd.conf,增加下面内容:

    pwcheck_method: saslauthd
    mech_list: gs2-krb5 gssapi plain
  4. 修改 /etc/ldap/ldap.conf,增加下面内容:

    SASL_MECH GSSAPI
  5. 重启 OpenLDAP 服务器。

  6. 为 LDAP 用户设置密码

    ldapmodify -Q -Y EXTERNAL -H ldapi:/// <<EOF
    dn: ${USER_DN}
    changetype: modify
    replace: userPassword
    userPassword:: `echo -n {SASL}username@7V1.NET|base64`
    EOF
    Tip
    采用 SASL 授权后,OpenLDAP 的 userPassword 字段实际上是 『username@7V1.NET』 的 base64编码,如:echo -n {SASL}username@7V1.NET|base64

Docker

这么复杂的操作,好痛苦,怎么破?别怕,我们有 Docker 大法。

OpenLDAP 篇

  1. 从 github 上将 docker-openldap 克隆下来,执行构建命令 docker build -t lifei/openldap .

  2. 使用新构建的镜像来启动一个容器,docker run --name=openldap -d lifei/openldap, 就这样一个名为『openldap』的 OpenLDAP 服务容器就启起来了。

  3. 进入这个镜像进行配置,执行命令 docker exec -it openldap bash,进入 bash。

  4. 这个镜像采用环境变量作为配置项,先准备如下面的内容:

    export NAME=7v1
    export DOMAIN_DN=dc=7v1,dc=net
    export ADMIN_DN=cn=admin,$DOMAIN_DN
    export ADMIN_PW=1234
    export KRB5REALM=7V1.NET
    export DOMAIN_REALM=7v1.net

    以上变量含义比较清晰,就不再解释了。

  5. 执行 OpenLDAP 的初始化命令 openldap-init.sh,OpenLDAP 服务就按照上面的配置初始化好了。

  6. 若 Kerberos 想使用 OpenLDAP 作为后端存储,则只需执行 Kerberos 初始化命令,kerberos-init.sh,Kerberos 所需要的设置也配置完毕了。

  7. 未完待续,等 Kerberos 配置完毕后,再来配置使用 Kerberos 作为 OpenLDAP 的认证。

Kerberos 篇

  1. 从 github 上将 docker-kerberos 克隆下来,执行构建命令 docker build -t lifei/kerberos .

  2. 使用新构建的镜像来启动一个容器, docker run --link openldap:openldap --name=kerberos -d lifei/kerberos,就这样一个名为『kerberos』容器就启起来了,但是与上面的『openldap』容器不同,此时的 Kerberos 服务还未启动。

  3. 进入这个镜像进行配置,执行命令 docker exec -it kerberos bash,进入 bash。

  4. 这个镜像同样采用环境变量作为配置项,先准备如下面的内容:

    export DOMAIN_DN=dc=7v1,dc=net
    export ADMIN_DN=cn=admin,$DOMAIN_DN
    export ADMIN_PW=1234
    export KRB5REALM=7V1.NET
    export DOMAIN_REALM=7v1.net
    export LDAP_HOST=openldap
  5. 执行初始化命令 kerberos-init.sh,键入『Master Key』的密码两次,Kerberos 环境就配置完了。

  6. 若想让 OpenLDAP 使用 Kerberos 作为验证方法的话,在执行 kadmin.local -q "ank root/admin",键入密码,生成一个可以使用 kadmin 命令的账号,下文的 krb5-init.sh 会用到这个账号。

OpenLDAP使用Kerberos作为验证方法

  1. 在 openldap 容器中设置环境命令 export KDC_ADDRESS=kerberos容器的IP

  2. 执行 krb5-init.sh,初始化 krb5.conf 文件并为 OpenLDAP 服务器生成两个 principal。

  3. 重启 openldap 容器,创建测试账号 smith

    # openldap容器中执行
    UID=smith && ldapadd -x -D $ADMIN_DN -w $ADMIN_PW <<EOF
    dn: ou=people,$DOMAIN_DN
    objectClass: organizationalUnit
    ou: people
    
    dn: cn=$UID,ou=people,$DOMAIN_DN
    objectclass: inetOrgPerson
    sn: $UID
    uid: $UID
    userpassword: {SASL}$UID@$KRB5REALM
    description: $UID
    ou: people
    EOF
    
    kadmin -q "ank smith"
    
    ldapwhoami -x -D cn=smith,ou=people,$DOMAIN_DN -W

results matching ""

    No results matching ""