• openldap介绍和使用


    openldap介绍和使用

    为什么会有本文?

    早期,公司是没有统一认证这个东西的,所以各自玩各自的。于是, confluence一个用户体系,gitlab一个用户体系,Jenkins一个用户体系等等, 开发中要用到的开源软件数不胜数,每个软件都要认证, 必须想办法统一账号。
    第三系统的认证通常都是配置化的, 比如oauth, openid, ldap。兼容最广泛就是ldap了,虽然是很老的系统(LDAPv3 was developed in the late 1990's to replace LDAPv2.),最后还是要使用它。

    在开始使用之前再明确一下我们的目标。为了统一公司内部的账号登录体系。公司的人员组织架构假设是这样子的:

    .
    └── company
        ├── ceo-Ryan
        ├── HR
        │   └── hr-a
        ├── 市场
        │   └── a
        ├── 研发
        │   ├── cto-Ryan
        │   ├── dev1
        │   │   ├── dev-Ryan
        │   │   ├── dev-Ryan2
        │   │   └── manager-Ryan
        │   ├── dev2
        │   └── dev3
        └── 行政
    

    实际上会复杂的多,简化的来看分为部门和成员。ldap就是存储这样的数据结构tree.

    咬牙看了很多博客,依旧云里雾里的感觉,最终决定上手尝试,并完整记录整个过程来加深理解。接下来将以一个什么都不懂的角色开始探索和使用ldap。

    先了解一些概念

    以下概念主要面向理解,去除了不关注的官方完整介绍,旨在能够快速了解我们要用的东西是什么样,如果需要更完整的概念介绍,参考后面的参考文献即可。

    什么是ldap

    LDAP是轻量目录访问协议,英文全称是Lightweight Directory Access Protocol,一般都简称为LDAP。按照我们对文件目录的理解,ldap可以看成一个文件系统,类似目录和文件树。

    ldap的软件

    ldap并不是一款软件,而是一个协议。

    现在市场上有关LDAP的产品已有很多,各大软件公司都在他们的产品中集成了LDAP服务,如Microsoft的ActiveDirectory、Lotus的Domino Directory、IBM的WebSphere中也集成了LDAP服务。LDAP的开源实现是OpenLDAP,它比商业产品一点也不差,而且源码开放。

    OpenLDAP 是最常用的目录服务之一,它是一个由开源社区及志愿者开发和管理的一个开源项目,提供了目录服务的所有功能,包括目录搜索、身份认证、安全通道、过滤器等等。大多数的 Linux 发行版里面都带有 OpenLDAP 的安装包。OpenLDAP 服务默认使用非加密的 TCP/IP 协议来接收服务的请求,并将查询结果传回到客户端。由于大多数目录服务都是用于系统的安全认证部分比如:用户登录和身份验证,所以它也支持使用基于 SSL/TLS 的加密协议来保证数据传送的保密性和完整性。OpenLDAP 是使用 OpenSSL 来实现 SSL/TLS 加密通信的。

    ldap的信息模型

    【重要部分】

    LDAP的信息模型是建立在"条目"(entries)的基础上。一个条目是一些属性的集合,并且具有一个全局唯一的"可区分名称"DN,一个条目可以通过DN来引用。每一个条目的属性具有一个类型和一个或者多个值。类型通常是容易记忆的名称,比如"cn"是通用名称(common name) ,或者"mail"是电子邮件地址。条目的值的语法取决于属性类型。比如,cn属性可能具有一个值"Babs Jensen" 。一个mail属性可能包含"bbs@kevin.com" 。一个jpegphoto属性可能包含一幅JPEG(二进制)格式的图片。

    LDAP的objectClass

    LDAP通过属性objectClass来控制哪一个属性必须出现或允许出现在一个条目中,它的值决定了该条目必须遵守的模式
    规则。可以理解为关系数据库的表结构。接下来会用到的objectClass有

    objectClass 含义
    olcGlobal 全局配置文件类型, 主要是cn=config.ldif 的配置项
    top 顶层的对象
    organization 组织,比如公司名称,顶层的对象
    organizationalUnit 重要, 一个目录节点,通常是group,或者部门这样的含义
    inetOrgPerson 重要, 我们真正的用户节点类型,person类型, 叶子节点
    groupOfNames 重要, 分组的group类型,标记一个group节点
    olcModuleList 配置模块的对象

    LDAP常用关键字列表

    ldap的entry是由各种字段构成,可以理解为关系数据库的字段。

    关键字 英文全称 含义
    dc Domain Component 域名的部分,其格式是将完整的域名分成几部分,如域名为example.com变成dc=example,dc=com
    uid User Id 用户ID,如“tom”
    ou Organization Unit 组织单位,类似于Linux文件系统中的子目录,它是一个容器对象,组织单位可以包含其他各种对象(包括其他组织单元),如“market”
    cn Common Name 公共名称,如“Thomas Johansson”
    sn Surname 姓,如“Johansson”
    dn Distinguished Name 惟一辨别名,类似于Linux文件系统中的绝对路径,每个对象都有一个惟一的名称,如“uid= tom,ou=market,dc=example,dc=com”,在一个目录树中DN总是惟一的
    rdn Relative dn 相对辨别名,类似于文件系统中的相对路径,它是与目录树结构无关的部分,如“uid=tom”或“cn= Thomas Johansson”
    c Country 国家,如“CN”或“US”等。
    o Organization 组织名,如“Example, Inc.”

    这里,我们把dn当做用户唯一主键, cn是common name,应该等同于用户名,因为用户名必须唯一,通常为邮箱前缀
    ,比如ryan.miao. sn作为姓氏, uid作为用户id。通常用户id也是唯一的。所以在使用ldap做认证的时候,
    大概逻辑如下:

    • 配置ldap host, admin, admin pass
    • 用户登录时传递username
    • 读取配置的ldap信息,查询cn或者uid等于username的数据
    • 取出第一个记录, 获得dn, 根据dn和password再次去ldap服务器认证。即我们必须保证cn或uid是全局唯一的,
      认证通常需要进行两次。原因就在于dn没办法根据用户名计算出来。

    一个ldap用户组织可能是这样的:



    一个倒桩树组成结构。

    centos7上的安装

    不同软件不同版本在不同环境的安装和结构是不太一样的。以下所有行为都是基于cento7openLDAP-2.4.44-21.el7_6

    为了方便测试,我们采用docker-centos7作为容器环境: https://github.com/Ryan-Miao/docker-china-source/tree/master/docker-centos7

    以特权模式启动

    docker run -d -it --rm --name=ldap  --privileged -p 8070:80  ryan/centos:7 /usr/sbin/init
    docker exec -it ldap /bin/bash
    

    安装openldap

    yum install -y openldap openldap-clients openldap-servers   
    
    • openldap-servers – This is the main LDAP server
    • openldap-clients – This contains all required LDAP client utilities
    • openldap – This packages contains the LDAP support libraries

    启动

    systemctl start slapd
    

    开机启动

    systemctl enable slapd
    

    添加防火墙允许

    firewall-cmd --add-service=ldap --permanent
    firewall-cmd --reload 
    

    ldap的配置文件

    安装后的配置目录是: /etc/openldap, 内容包括下列这些文件

    .
    |-- certs
    |   |-- cert8.db
    |   |-- key3.db
    |   |-- password
    |   `-- secmod.db
    |-- check_password.conf
    |-- ldap.conf
    |-- schema
    |   |-- collective.ldif
    |   |-- collective.schema
    |   |-- corba.ldif
    |   |-- corba.schema
    |   |-- core.ldif
    |   |-- core.schema
    |   |-- cosine.ldif
    |   |-- cosine.schema
    |   |-- duaconf.ldif
    |   |-- duaconf.schema
    |   |-- dyngroup.ldif
    |   |-- dyngroup.schema
    |   |-- inetorgperson.ldif
    |   |-- inetorgperson.schema
    |   |-- java.ldif
    |   |-- java.schema
    |   |-- misc.ldif
    |   |-- misc.schema
    |   |-- nis.ldif
    |   |-- nis.schema
    |   |-- openldap.ldif
    |   |-- openldap.schema
    |   |-- pmi.ldif
    |   |-- pmi.schema
    |   |-- ppolicy.ldif
    |   `-- ppolicy.schema
    `-- slapd.d
        |-- cn=config
        |   |-- cn=schema
        |   |   `-- cn={0}core.ldif
        |   |-- cn=schema.ldif
        |   |-- olcDatabase={-1}frontend.ldif
        |   |-- olcDatabase={0}config.ldif
        |   |-- olcDatabase={1}monitor.ldif
        |   `-- olcDatabase={2}hdb.ldif
        `-- cn=config.ldif
    

    cn=config.ldif

    默认配置文件,位于/etc/openldap/slapd.d, 文件格式为LDAP Input Format (LDIF), ldap目录特定的格式。

    cat cn=config.ldif 
    # AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
    # CRC32 094432b2
    dn: cn=config
    objectClass: olcGlobal
    cn: config
    olcArgsFile: /var/run/openldap/slapd.args
    olcPidFile: /var/run/openldap/slapd.pid
    olcTLSCACertificatePath: /etc/openldap/certs
    olcTLSCertificateFile: "OpenLDAP Server"
    olcTLSCertificateKeyFile: /etc/openldap/certs/password
    structuralObjectClass: olcGlobal
    entryUUID: 6cd8a04c-527e-1039-96b7-9fc5056bce1b
    creatorsName: cn=config
    createTimestamp: 20190814012729Z
    entryCSN: 20190814012729.299304Z#000000#000#000000
    modifiersName: cn=config
    modifyTimestamp: 20190814012729Z
    

    olcDatabase={2}hdb.ldif

    db存储格式,有bdb和hdb两种,这里是hdb. 可以直接查看文件,也可以查询:

    ldapsearch  -Y EXTERNAL -H ldapi:/// -b cn=config dn | grep olcDatabase
    

    核心配置文件,位于/etc/openldap/slapd.d/cn=config, 可以配置域名(olcSuffix), 管理员账号(olcRootDN)等。

    cat olcDatabase={2}hdb.ldif 
    # AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
    # CRC32 e3322b1b
    dn: olcDatabase={2}hdb
    objectClass: olcDatabaseConfig
    objectClass: olcHdbConfig
    olcDatabase: {2}hdb
    olcDbDirectory: /var/lib/ldap
    olcSuffix: dc=my-domain,dc=com
    olcRootDN: cn=Manager,dc=my-domain,dc=com
    olcDbIndex: objectClass eq,pres
    olcDbIndex: ou,cn,mail,surname,givenname eq,pres,sub
    structuralObjectClass: olcHdbConfig
    entryUUID: 6cd940ba-527e-1039-96bd-9fc5056bce1b
    creatorsName: cn=config
    createTimestamp: 20190814012729Z
    entryCSN: 20190814012729.304147Z#000000#000#000000
    modifiersName: cn=config
    modifyTimestamp: 20190814012729Z
    

    可以看到很多文件名和字段名都有前缀"olc" (OpenLDAP Configuration), 理解就好。

    创建olcRootDN作为管理员账号

    看到前面两个配置文件,官方不推荐我们直接修改配置文件,而是通过ldapmodify来更新配置。

    类似于update by pk, 这里的pk就是dn了。

    创建rootdn.ldif

    dn: olcDatabase={2}hdb,cn=config
    changetype: modify
    replace: olcRootDN
    olcRootDN: cn=admin,dc=demo,dc=com
    -
    replace: olcSuffix
    olcSuffix: dc=demo,dc=com
    -
    replace: olcRootPW
    olcRootPW: <pass>
    
    • 修改olcRootDN, 设置为我们的admin: cn=admin,dc=demo,dc=com
    • 修改olcSuffix, 设置为我们的域名dc=demo,dc=com
    • 修改olcRootPW, 设置我们的admin密码, 这个需要加密,所以暂时放一个占位符,等下替换
    • changetype变更类型, replace表示替换, add表示增加。

    cn=config是全局配置,必须包含objectClass: olcGlobal.

    然后创建changeroot.sh

    admin_pass=`slappasswd -s admin`
    echo "admin pass is:  ${admin_pass}"
    sed "s!<pass>!${admin_pass}!g"   rootdn.ldif > tmp.ldif
    
    echo "备份默认配置"
    
    cp /etc/openldap/slapd.d/cn=config/olcDatabase={2}hdb.ldif /etc/openldap/slapd.d/cn=config/olcDatabase={2}hdb.ldif.bak
    
    echo "将要修改的内容:"
    cat tmp.ldif
    
    ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f tmp.ldif
    
    echo "修改后的变化"
    diff /etc/openldap/slapd.d/cn=config/olcDatabase={2}hdb.ldif /etc/openldap/slapd.d/cn=config/olcDatabase={2}hdb.ldif.bak
    

    使用脚本进行变更,而不是直接命令行交互式变更,这样可以更容易梳理变更逻辑, 而且可以重复使用。

    验证

    通过diff,可以看到配置文件已经发生了变更

    修改后的变化
    2c2
    < # CRC32 b643556d
    ---
    > # CRC32 9b5dd3fc
    7a8,9
    > olcSuffix: dc=my-domain,dc=com
    > olcRootDN: cn=Manager,dc=my-domain,dc=com
    14,19c16,18
    < olcRootDN: cn=admin,dc=demo,dc=com
    < olcSuffix: dc=demo,dc=com
    < olcRootPW:: e1NTSEF9Q3puZEw4QzN4aWJNQTlHeEpYV2doNEN3NHJXSm5Fb0s=
    < entryCSN: 20190814074323.492640Z#000000#000#000000
    < modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
    < modifyTimestamp: 20190814074323Z
    ---
    > entryCSN: 20190814074118.406339Z#000000#000#000000
    > modifiersName: cn=config
    > modifyTimestamp: 20190814074118Z
    

    我们可以通过search语法来确定账号密码是否正确:

    # ldapsearch -H ldapi:///  -D "cn=admin,dc=demo,dc=com" -w admia 
    ldap_bind: Invalid credentials (49)
    
    
    # ldapsearch -H ldapi:///  -D "cn=admin,dc=demo,dc=com" -w admin 
    # extended LDIF
    #
    # LDAPv3
    # base <> (default) with scope subtree
    # filter: (objectclass=*)
    # requesting: ALL
    #
    
    # search result
    search: 2
    result: 32 No such object
    
    # numResponses: 1
    
    • ldapsearch 查询语法, -H指定host, -D指定admin的账号,即rootdn, -w指定密码, -x启用认证

    添加我们的base组织结构

    有了管理员,还需要配置组织结构base.ldif。在这之前,我们需要导入一些模板。schema类似数据库表定义,
    定义了字段名称和类型。

    schema地址:

    /etc/openldap/schema
    

    默认安装加载了core.ldif , 我们现在加载几个想要的schema:

    ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/cosine.ldif 
    ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/nis.ldif 
    ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/inetorgperson.ldif 
    

    然后

    创建文件base.ldif

    # cat base.ldif
    dn: dc=demo,dc=com
    objectClass: top
    objectClass: dcObject
    objectClass: organization
    o: ldap测试组织
    dc: demo
    
    dn: cn=Manager,dc=demo,dc=com
    objectClass: organizationalRole
    cn: Manager
    description: 组织管理人
    
    dn: ou=People,dc=demo,dc=com
    objectClass: organizationalUnit
    ou: People
    
    dn: ou=Group,dc=demo,dc=com
    objectClass: organizationalUnit
    ou: Group
    

    使用ldapadd添加base:

    ldapadd -x -D cn=admin,dc=demo,dc=com -w admin -f base.ldif 
    

    使用ldapsearch来检查内容

    [root@a1791f1044ba data]# ldapsearch -x -D cn=admin,dc=demo,dc=com -w admin -b "dc=demo,dc=com"
    # extended LDIF
    #
    # LDAPv3
    # base <dc=demo,dc=com> with scope subtree
    # filter: (objectclass=*)
    # requesting: ALL
    #
    
    # demo.com
    dn: dc=demo,dc=com
    objectClass: top
    objectClass: dcObject
    objectClass: organization
    o:: bGRhcOa1i+ivlee7hOe7hw==
    dc: demo
    
    # Manager, demo.com
    dn: cn=Manager,dc=demo,dc=com
    objectClass: organizationalRole
    cn: Manager
    description:: 57uE57uH566h55CG5Lq6
    
    # People, demo.com
    dn: ou=People,dc=demo,dc=com
    objectClass: organizationalUnit
    ou: People
    
    # Group, demo.com
    dn: ou=Group,dc=demo,dc=com
    objectClass: organizationalUnit
    ou: Group
    
    # search result
    search: 2
    result: 0 Success
    
    # numResponses: 5
    # numEntries: 4
    
    • -x 启用认证
    • -D bind admin的dn
    • -w admin的密码
    • -b basedn, 查询的基础dn
    • 可以看到中文被替换成hash, 后面可以通过其他方式看到

    添加人员

    ou并不能当做分组,而仅仅是组织架构的一个单元。ldap的分组都是通过单独的group来实现的。

    添加人员

    添加人员对应的是树的叶子节点,使用的oebjectClass: inetOrgPerson

    添加组织部门对应的是目录,使用的objectClass: organizationalUnit.

    我们要把人员添加到ou=People,dc=demo,dc=com下。

    创建adduser.ldif

    dn: ou=研发部门,ou=People,dc=demo,dc=com
    changetype: add
    objectClass: organizationalUnit
    ou: 研发部门
    
    dn: ou=后台组,ou=研发部门,ou=People,dc=demo,dc=com
    changetype: add
    objectClass: organizationalUnit
    ou: 后台组
    
    
    
    dn: cn=ryan.miao,ou=后台组,ou=研发部门,ou=People,dc=demo,dc=com
    changetype: add
    objectClass: inetOrgPerson
    cn: ryan.miao
    departmentNumber: 1
    sn: Miao
    title: 大牛
    mail: ryan.miao@demo.com
    uid: 10000
    displayName: 中文名
    
    
    
    dn: cn=someone,ou=后台组,ou=研发部门,ou=People,dc=demo,dc=com
    changetype: add
    objectClass: inetOrgPerson
    cn: someone
    departmentNumber: 1
    sn: someone
    title: Java工程师
    mail: someone@demo.com
    uid: 10001
    displayName: 某人
    
    
    dn: ou=测试组,ou=研发部门,ou=People,dc=demo,dc=com
    changetype: add
    objectClass: organizationalUnit
    ou: 测试组
    
    
    dn: cn=tester.miao,ou=测试组,ou=研发部门,ou=People,dc=demo,dc=com
    changetype: add
    objectClass: inetOrgPerson
    cn: tester.miao
    departmentNumber: 2
    sn: Miao
    title: 测试工程师
    mail: tester@demo.com
    uid: 10002
    displayName: 测试某人
    
    
    dn: ou=HR,ou=People,dc=demo,dc=com
    changetype: add
    objectClass: organizationalUnit
    ou: HR
    
    
    dn: cn=fang.huang,ou=HR,ou=People,dc=demo,dc=com
    changetype: add
    objectClass: inetOrgPerson
    cn: fang.huang
    departmentNumber: 3
    sn: Huang
    title: HRBP
    mail: fang.huang@demo.com
    uid: 10003
    displayName: 黄芳
    

    使用ldapadd来添加我们的用户

    [root@e6043aeb680e data]# ldapadd -x -D cn=admin,dc=demo,dc=com -w admin -f adduser.sh 
    adding new entry "ou=研发部门,ou=People,dc=demo,dc=com"
    
    adding new entry "ou=后台组,ou=研发部门,ou=People,dc=demo,dc=com"
    
    adding new entry "cn=ryan.miao,ou=后台组,ou=研发部门,ou=People,dc=demo,dc=com"
    
    adding new entry "cn=someone,ou=后台组,ou=研发部门,ou=People,dc=demo,dc=com"
    
    adding new entry "ou=测试组,ou=研发部门,ou=People,dc=demo,dc=com"
    
    adding new entry "cn=tester.miao,ou=测试组,ou=研发部门,ou=People,dc=demo,dc=com"
    
    adding new entry "ou=HR,ou=People,dc=demo,dc=com"
    
    adding new entry "cn=fang.huang,ou=HR,ou=People,dc=demo,dc=com"
    

    使用ldapsearch来查询用户

    指定唯一id来查询某个用户,比如cn唯一,则

    [root@e6043aeb680e data]# ldapsearch -x -D cn=admin,dc=demo,dc=com -w admin -b "dc=demo,dc=com" "cn=ryan.miao"  
    # extended LDIF
    #
    # LDAPv3
    # base <dc=demo,dc=com> with scope subtree
    # filter: cn=ryan.miao
    # requesting: ALL
    #
    
    # ryan.miao, E5908EE58FB0E7BB84, E7A094E58F91E983A8E997A8,
      People, demo.com
    dn:: Y249cnlhbi5taWFvLG91PeWQjuWPsOe7hCxvdT3noJTlj5Hpg6jpl6gsb3U9UGVvcGxlLGRjP
     WRlbW8sZGM9Y29t
    objectClass: inetOrgPerson
    cn: ryan.miao
    departmentNumber: 1
    sn: Miao
    title:: 5aSn54mb
    mail: ryan.miao@demo.com
    uid: 10000
    displayName:: 5Lit5paH5ZCN
    
    # search result
    search: 2
    result: 0 Success
    
    # numResponses: 2
    # numEntries: 1
    
    

    和前面的示例相比,多了一个参数filter

    ldapsearch -x -D "admin的dn" -w "admin的密码" -b "basedn, 最外层的分组" "search filter:"

    还可以指定返回的字段

    [root@e6043aeb680e data]# ldapsearch -x -D cn=admin,dc=demo,dc=com -w admin -b "ou=HR,ou=People,dc=demo,dc=com"  cn uid  displayName
    # extended LDIF
    #
    # LDAPv3
    # base <ou=HR,ou=People,dc=demo,dc=com> with scope subtree
    # filter: (objectclass=*)
    # requesting: cn uid displayName 
    #
    
    # HR, People, demo.com
    dn: ou=HR,ou=People,dc=demo,dc=com
    
    # fang.huang, HR, People, demo.com
    dn: cn=fang.huang,ou=HR,ou=People,dc=demo,dc=com
    cn: fang.huang
    uid: 10003
    displayName:: 6buE6Iqz
    
    # search result
    search: 2
    result: 0 Success
    
    # numResponses: 3
    # numEntries: 2
    

    在配置第三方认证的时候,比如airflow, 就是通过这样userfilter来search用户的。

    添加用户密码

    刚才添加用户太快,忘记添加用户密码了。这就涉及到添加用户的同时指定密码和admin修改密码以及用户
    自己修改密码三个情况了。

    添加用户的时候指定密码

    一个hr肯定太累了,添加一个新的hr hr-ryan

    创建文件addone.ldif

    dn: cn=hr-ryan,ou=HR,ou=People,dc=demo,dc=com
    changetype: add
    objectClass: inetOrgPerson
    cn: hr-ryan
    userPassword: 123456
    departmentNumber: 3
    sn: hr-ryan
    title: HRBP
    mail: hr-ryan@demo.com
    uid: 10004
    displayName: 我是猎头
    

    执行添加

    ldapadd -x -D cn=admin,dc=demo,dc=com -w admin -f addone.ldif
    

    查询验证

    root@e6043aeb680e data]# ldapsearch -x -D cn=admin,dc=demo,dc=com -w admin -b dc=demo,dc=com "cn=hr-*"
    # extended LDIF
    #
    # LDAPv3
    # base <dc=demo,dc=com> with scope subtree
    # filter: cn=hr-*
    # requesting: ALL
    #
    
    # hr-ryan, HR, People, demo.com
    dn: cn=hr-ryan,ou=HR,ou=People,dc=demo,dc=com
    objectClass: inetOrgPerson
    cn: hr-ryan
    userPassword:: MTIzNDU2
    departmentNumber: 3
    sn: hr-ryan
    title: HRBP
    mail: hr-ryan@demo.com
    uid: 10004
    displayName:: 5oiR5piv54yO5aS0
    
    # search result
    search: 2
    result: 0 Success
    
    # numResponses: 2
    # numEntries: 1
    

    可以看到,filter里可以使用通配符。并且,用户密码被加密了。

    我们前文说,第三方系统第一步通过search拿到dn,也就是上面这一步。然后第二个是验证密码,验证密码是怎么做的呢?直接通过search语法连接ldap,通过则证明密码正确。

    [root@a1791f1044ba data]# ldapsearch -x -D cn=hr-ryan,ou=HR,ou=People,dc=demo,dc=com -w 123456
    # extended LDIF
    #
    # LDAPv3
    # base <> (default) with scope subtree
    # filter: (objectclass=*)
    # requesting: ALL
    #
    
    # search result
    search: 2
    result: 32 No such object
    
    # numResponses: 1
    

    修改用户密码

    管理员权限最大,可以修改任意密码。使用ldapmodify

    创建文件updatepass.ldif

    dn: cn=ryan.miao,ou=后台组,ou=研发部门,ou=People,dc=demo,dc=com
    changetype: modify
    replace: userPassword
    userPassword: ryanmiao
    

    执行修改

    ldapmodify -a -H ldap://172.17.0.2:389 -D "cn=admin,dc=demo,dc=com" -w admin -f updatepass.ldif 
    

    查询确认

    ldapsearch -x -D cn=ryan.miao,ou=后台组,ou=研发部门,ou=People,dc=demo,dc=com -w ryanmiao -b dc=demo,dc=com "cn=ryan.miao"
    

    可以确认密码修改成功了,同时也暴露了一个问题,任意一个人都可以bind登录,然后查询所有用户的信息。后面我
    们将关注acl权限问题,让每个人只能查询自己的信息,让指定的group可以查询所有人的信息。

    注意到,我们使用的明文作为密码存储, 这样的传输方式是不推荐的, 可以使用sha1来存储。

    slappasswd -s ryanmiao
    {SSHA}r5yzPeESGLsvX7oxQetVEpel9LhygFef
    
    
    dn: cn=ryan.miao,ou=后台组,ou=研发部门,dc=demo,dc=com
    changetype: modify
    replace: userPassword
    userPassword: {SSHA}r5yzPeESGLsvX7oxQetVEpel9LhygFef
    
    
    [root@a1791f1044ba schema]# slappasswd -h {sha} -s ryanmiao
    {SHA}vMV4cx3BhPVf0dRvEur3NOWIDEw=
    [root@a1791f1044ba schema]# slappasswd -h {md5} -s ryanmiao
    {MD5}J3sqNCJFas5wgycX4lJPsg==
    
    或者sha1
    userPassword: {SHA}vMV4cx3BhPVf0dRvEur3NOWIDEw=
    

    值得注意的是sha1的结果并不是通常我们用的hex结果,而是通过utf8转换的:

    public static String sha1(String str)
        throws NoSuchAlgorithmException, UnsupportedEncodingException {
        if (null == str || str.length() == 0) {
            return null;
        }
        MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
        mdTemp.update(str.getBytes("UTF-8"));
        byte[] md = mdTemp.digest();
    
        return "{SHA}" + Utf8.decode(java.util.Base64.getEncoder().encode(md));
    }
    

    Springboot提供了LdapShaPasswordEncoder, 但标记为deprecated, 理由是明文的加密算法不够安全。
    我们的ldap由于属于同步服务,即ldap不负责用户信息的维护,只负责查询。需要由用户中心来同步给ldap信息。
    这就涉及到密码的问题,用户中心没有存储用户明文的,也就是不能直接同步到ldap。好在可以获得用户密码的sha,
    通过sha来同步ldap的密码,即ldap中的密码是一个sha的方式存储的。虽然不够安全,容易被撞,但用着也还行。
    如果不信任这种算法,那就不用ldap。可以使用oauth的方式认证第三方系统,大部分系统已支持这种认证方案。

    @deprecated Digest based password encoding is not considered secure. Instead use an
    adaptive one way function like BCryptPasswordEncoder, Pbkdf2PasswordEncoder, or
    password upgrades. There are no plans to remove this support. It is deprecated to indicate
    that this is a legacy implementation and using it is considered insecure.

    前面提到用户已知个人密码的情况下,如何自己修改密码。

    ldappasswd -x -h 172.17.0.2 -p 389 -D "cn=Barbara Jensen,dc=example,dc=org" -w VSzhfbwA -s 123456
    

    我们先不关注这种行为吧,默认所有第三方系统只有登录权限。关于组织架构的维护,即ldap组织的更新,我们采用
    其他的方案去管理,ldap只是用来辅助第三方登录的。即,其他系统想要修改密码之类的,统一到我们的用户中心服务
    去修改变更,用户中心负责把信息同步给ldap。

    添加组Group

    有人会问,我之前添加人员的时候添加了很多部门的ou,不就是group吗。

    是的,理论上应该是group。但是由于我们丢了一步,没有设置ou的objectClass为group。所以,这里单独讲group的故事。

    ldap的group是一种单独的类型objectClass: groupOfNames, 有个字段叫做member, value就是entry的dn。如此,
    实现了group-user的映射关系。

    我们可以通过group来查询member,然而,并不能通过user直接获取到group。这在配置第三方系统的时候,没办法做group认证,
    比如airflow要求输入group filter, 默认通过memberof的属性值来获取group。所以,理论上user应该有个字段叫做memberof,
    value是group。

    大家可能会觉得dn已经很明显的分组了好吧,为啥还要这么复杂。事实上,ldap也提供了Reverse Group Membership Maintenance.
    由系统来维护二者的映射关系。即

    • group添加member的时候会自动给对应的entry添加memberof字段
    • 当删除entry的时候,也会从group里删除member字段

    这个需要单独配置,默认是不支持的。

    添加memberof模块

    创建add_module_group.sh

    dn: cn=module,cn=config
    cn: module
    objectClass: olcModuleList
    olcModulePath: /usr/lib64/openldap
    
    dn: cn=module{0},cn=config
    changetype: modify
    add: olcModuleLoad
    olcModuleLoad: memberof.la
    

    执行添加

    ldapadd -Q -Y EXTERNAL -H ldapi:/// -f  add_module_group.sh 
    

    创建add_group_objectClass.sh

    dn: olcOverlay=memberof,olcDatabase={2}hdb,cn=config
    objectClass: olcConfig
    objectClass: olcMemberOf
    objectClass: olcOverlayConfig
    objectClass: top
    olcOverlay: memberof
    olcMemberOfDangling: ignore
    olcMemberOfRefInt: TRUE
    olcMemberOfGroupOC: groupOfNames
    olcMemberOfMemberAD: member     
    olcMemberOfMemberOfAD: memberOf
    

    执行添加

    ldapadd -Q -Y EXTERNAL -H ldapi:/// -f  add_group_objectClass.sh
    

    添加一个group

    创建addgroup.ldif

    dn: cn=g-admin,ou=Group,dc=demo,dc=com
    objectClass: groupOfNames
    cn: g-admin
    member: cn=ryan.miao,ou=后台组,ou=研发部门,ou=People,dc=demo,dc=com
    member: cn=hr-ryan,ou=HR,ou=People,dc=demo,dc=com
    

    执行

    ldapmodify -a -H ldap://172.17.0.2:389 -D "cn=admin,dc=demo,dc=com" -w admin -f addgroup.ldif
    

    查看组

    [root@e6043aeb680e data]# ldapsearch -H ldapi:/// -x -D "cn=admin,dc=demo,dc=com" -w admin -b "ou=Group,dc=demo,dc=com" 
    # extended LDIF
    #
    # LDAPv3
    # base <ou=Group,dc=demo,dc=com> with scope subtree
    # filter: (objectclass=*)
    # requesting: ALL
    #
    
    # Group, demo.com
    dn: ou=Group,dc=demo,dc=com
    objectClass: organizationalUnit
    ou: Group
    
    # g-admin, Group, demo.com
    dn: cn=g-admin,ou=Group,dc=demo,dc=com
    objectClass: groupOfNames
    cn: g-admin
    member:: Y249cnlhbi5taWFvLG91PeWQjuWPsOe7hCxvdT3noJTlj5Hpg6jpl6gsb3U9UGVvcGxlL
     GRjPWRlbW8sZGM9Y29t
    member: cn=hr-ryan,ou=HR,ou=People,dc=demo,dc=com
    
    # search result
    search: 2
    result: 0 Success
    
    # numResponses: 3
    # numEntries: 2
    

    再来查看entry是否添加了memberof, ldapsearch当不指定字段的时候,默认返回全部强制字段,memberof不属于强制,需要单独指明

    [root@e6043aeb680e data]# ldapsearch -H ldapi:/// -x -D "cn=admin,dc=demo,dc=com" -w admin -b "ou=People,dc=demo,dc=com" "(|(cn=ryan.miao)(cn=hr-*))" memberof
    # extended LDIF
    #
    # LDAPv3
    # base <ou=People,dc=demo,dc=com> with scope subtree
    # filter: (|(cn=ryan.miao)(cn=hr-*))
    # requesting: memberof 
    #
    
    # ryan.miao, E5908EE58FB0E7BB84, E7A094E58F91E983A8E997A8,
      People, demo.com
    dn:: Y249cnlhbi5taWFvLG91PeWQjuWPsOe7hCxvdT3noJTlj5Hpg6jpl6gsb3U9UGVvcGxlLGRjP
     WRlbW8sZGM9Y29t
    memberOf: cn=g-admin,ou=Group,dc=demo,dc=com
    
    # hr-ryan, HR, People, demo.com
    dn: cn=hr-ryan,ou=HR,ou=People,dc=demo,dc=com
    memberOf: cn=g-admin,ou=Group,dc=demo,dc=com
    
    # search result
    search: 2
    result: 0 Success
    
    # numResponses: 3
    # numEntries: 2
    

    可以看到,这两个人都link到了admin组。如此实现了我们的组添加和管理。

    同时,再次引入了新的查询语法,filter的正则匹配。

    • (|(cn=ryan.miao)(cn=hr-*)) 表示或者满足某个条件,这里就是为了查询这两个人,另外*表示通配符
    • (&(objectClass=inetOrgPerson)(cn=ryan.miao)) 第三方系统,比如Python集成ldap的配置,通常会有一个basedn, 就是我们的域名了,然后userfilter,
      这个filter就是这个。我们通常填写objectClass=inetOrgPerson。然后让我们配置user_name_attr, 这就是唯一属性,我们说我们的cn唯一。

    所以,一个Python的ldap配置,通常是这个样子的。

    [ldap]
    # set this to ldaps://<your.ldap.server>:<port>
    uri = ldap://172.17.0.2:389
    user_filter = objectClass=inetOrgPerson
    user_name_attr = cn
    group_member_attr = memberof
    superuser_filter =
    data_profiler_filter =
    bind_user = cn=admin,dc=demo,dc=com
    bind_password = admin
    basedn = dc=demo,dc=com
    cacert = 
    search_scope = SUBTREE
    

    源码 https://github.com/apache/airflow/blob/master/airflow/contrib/auth/backends/ldap_auth.py#L101

    search_filter = "(&({0})({1}={2}))".format(user_filter, user_name_att, username)
    

    添加用户到group

    我们来创建一个common group, 表示所有人都应该在的一个group。

    # commongroup.sh
    dn: cn=g-users,ou=Group,dc=demo,dc=com
    objectClass: groupOfNames
    cn: g-users
    member: cn=ryan.miao,ou=后台组,ou=研发部门,ou=People,dc=demo,dc=com
    member: cn=hr-ryan,ou=HR,ou=People,dc=demo,dc=com
    
    ldapmodify -a -H ldap://172.17.0.2:389 -D "cn=admin,dc=demo,dc=com" -w admin -f commongroup.sh
    

    到目前为止,我们添加了2个group:

    [root@40e6bf0b50dc data]# ldapsearch -H ldap:/// -D cn=admin,dc=demo,dc=com -w admin -b dc=demo,dc=com -s sub "objectClass=groupOfNames"  dn member
    # extended LDIF
    #
    # LDAPv3
    # base <dc=demo,dc=com> with scope subtree
    # filter: objectClass=groupOfNames
    # requesting: dn member 
    #
    
    # g-admin, Group, demo.com
    dn: cn=g-admin,ou=Group,dc=demo,dc=com
    member:: Y249cnlhbi5taWFvLG91PeWQjuWPsOe7hCxvdT3noJTlj5Hpg6jpl6gsb3U9UGVvcGxlL
     GRjPWRlbW8sZGM9Y29t
    member: cn=hr-ryan,ou=HR,ou=People,dc=demo,dc=com
    
    # g-users, Group, demo.com
    dn: cn=g-users,ou=Group,dc=demo,dc=com
    member:: Y249cnlhbi5taWFvLG91PeWQjuWPsOe7hCxvdT3noJTlj5Hpg6jpl6gsb3U9UGVvcGxlL
     GRjPWRlbW8sZGM9Y29t
    member: cn=hr-ryan,ou=HR,ou=People,dc=demo,dc=com
    

    接下来,我们把剩下的用户加入到g-users这个group, 以后所有人加入的默认group。

    创建addUserToGroup.sh

    dn: cn=g-users,ou=Group,dc=demo,dc=com
    changetype: modify
    add: member
    member: cn=fang.huang,ou=HR,ou=People,dc=demo,dc=com
    member: cn=hr-ryan,ou=HR,ou=People,dc=demo,dc=com
    member: cn=someone,ou=后台组,ou=研发部门,ou=People,dc=demo,dc=com
    member: cn=tester.miao,ou=测试组,ou=研发部门,ou=People,dc=demo,dc=com
    

    执行

    ldapmodify -H ldap:/// -x -D cn=admin,dc=demo,dc=com -w admin -f addUserToGroup.sh
    

    查看

    [root@40e6bf0b50dc data]# ldapsearch -H ldap:/// -D cn=admin,dc=demo,dc=com -w admin -b dc=demo,dc=com -s sub "objectClass=groupOfNames"  
    # g-admin, Group, demo.com
    dn: cn=g-admin,ou=Group,dc=demo,dc=com
    objectClass: groupOfNames
    cn: g-admin
    member:: Y249cnlhbi5taWFvLG91PeWQjuWPsOe7hCxvdT3noJTlj5Hpg6jpl6gsb3U9UGVvcGxlL
     GRjPWRlbW8sZGM9Y29t
    member: cn=hr-ryan,ou=HR,ou=People,dc=demo,dc=com
    
    # g-users, Group, demo.com
    dn: cn=g-users,ou=Group,dc=demo,dc=com
    objectClass: groupOfNames
    cn: g-users
    member:: Y249cnlhbi5taWFvLG91PeWQjuWPsOe7hCxvdT3noJTlj5Hpg6jpl6gsb3U9UGVvcGxlL
     GRjPWRlbW8sZGM9Y29t
    member: cn=hr-ryan,ou=HR,ou=People,dc=demo,dc=com
    member: cn=fang.huang,ou=HR,ou=People,dc=demo,dc=com
    member: cn=hr-miao,ou=HR,ou=People,dc=demo,dc=com
    member:: Y249c29tZW9uZSxvdT3lkI7lj7Dnu4Qsb3U956CU5Y+R6YOo6ZeoLG91PVBlb3BsZSxkY
     z1kZW1vLGRjPWNvbQ==
    member:: Y249dGVzdGVyLm1pYW8sb3U95rWL6K+V57uELG91PeeglOWPkemDqOmXqCxvdT1QZW9wb
     GUsZGM9ZGVtbyxkYz1jb20=
    

    从Group中移除user

    g-admin是一个管理员分组,我们去掉普通用户cn=ryan.miao,ou=后台组,ou=研发部门,ou=People,dc=demo,dc=com

    创建removeUserFromGroup.sh

    dn: cn=g-admin,ou=Group,dc=demo,dc=com
    changetype: modify
    delete: member
    member: cn=ryan.miao,ou=后台组,ou=研发部门,ou=People,dc=demo,dc=com
    

    执行

    ldapmodify -H ldap:/// -x -D cn=admin,dc=demo,dc=com -w admin -f removeUserFromGroup.sh
    

    最终Group和user的关系

    group可以有多个user, user可以归属于多个group,是多对多的关系。

    group有多个member字段, user有多个memberof字段。

    ACL权限控制

    Access Control List (ACL) 表示权限控制。从前面的测试可以看到,默认是没开启权限的。任何人都可以连接查询和操作。

    acl的设置方式很多,鉴于我们并没有将ldap作为主要的数据存储方案,即不做过多的权限设置了,只要关掉匿名访问,只允许read,
    允许个人修改个人信息就好了。更多设置方案可以参照官网。

    acl的配置文件

    配置文件还是开始提到的,我们可以查看现有的配置:

    [root@e6043aeb680e data]# ldapsearch  -Y EXTERNAL -H ldapi:/// -b cn=config dn
    SASL/EXTERNAL authentication started
    SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
    SASL SSF: 0
    # extended LDIF
    #
    # LDAPv3
    # base <cn=config> with scope subtree
    # filter: (objectclass=*)
    # requesting: dn 
    #
    
    # config
    dn: cn=config
    
    # module{0}, config
    dn: cn=module{0},cn=config
    
    # schema, config
    dn: cn=schema,cn=config
    
    # {0}core, schema, config
    dn: cn={0}core,cn=schema,cn=config
    
    # {1}cosine, schema, config
    dn: cn={1}cosine,cn=schema,cn=config
    
    # {2}nis, schema, config
    dn: cn={2}nis,cn=schema,cn=config
    
    # {3}inetorgperson, schema, config
    dn: cn={3}inetorgperson,cn=schema,cn=config
    
    # {-1}frontend, config
    dn: olcDatabase={-1}frontend,cn=config
    
    # {0}config, config
    dn: olcDatabase={0}config,cn=config
    
    # {1}monitor, config
    dn: olcDatabase={1}monitor,cn=config
    
    # {2}hdb, config
    dn: olcDatabase={2}hdb,cn=config
    
    # {0}memberof, {2}hdb, config
    dn: olcOverlay={0}memberof,olcDatabase={2}hdb,cn=config
    
    # search result
    search: 2
    result: 0 Success
    
    # numResponses: 13
    # numEntries: 12
    

    acl就在dn: olcDatabase={2}hdb,cn=config, 我们可以查看具体的配置:

    [root@e6043aeb680e data]#  ldapsearch  -Y EXTERNAL -H ldapi:/// -b cn=config 'olcDatabase={2}hdb'
    SASL/EXTERNAL authentication started
    SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
    SASL SSF: 0
    # extended LDIF
    #
    # LDAPv3
    # base <cn=config> with scope subtree
    # filter: olcDatabase={2}hdb
    # requesting: ALL
    #
    
    # {2}hdb, config
    dn: olcDatabase={2}hdb,cn=config
    objectClass: olcDatabaseConfig
    objectClass: olcHdbConfig
    olcDatabase: {2}hdb
    olcDbDirectory: /var/lib/ldap
    olcDbIndex: objectClass eq,pres
    olcDbIndex: ou,cn,mail,surname,givenname eq,pres,sub
    olcRootDN: cn=admin,dc=demo,dc=com
    olcSuffix: dc=demo,dc=com
    olcRootPW: {SSHA}kSEbfJCLQXtHKe+O/HoIXD0k50e7SubL
    
    # search result
    search: 2
    result: 0 Success
    
    # numResponses: 2
    # numEntries: 1
    

    接下来,使用modify添加acl即可。

    创建addacl.sh

    dn: olcDatabase={2}hdb,cn=config
    changetype: modify
    # 只有自己可以修改密码, 不允许匿名访问, 允许g-admin组修改
    add: olcAccess
    olcAccess: {0}to attrs=userPassword by self write by anonymous auth by group.exact="cn=g-admin,ou=Group,dc=demo,dc=com"  write by * none
    -
    # 自己可以修改自己的信息,g-admin可以修改任何信息
    add: olcAccess
    olcAccess: {1}to * by self write by group.exact="cn=g-admin,ou=Group,dc=demo,dc=com"  write   by * none
    
    

    执行

    ldapmodify -H ldapi://  -Y EXTERNAL -f addacl.sh
    

    验证权限

    添加一个普通用户;

    # addtwo.sh
    dn: cn=hr-miao,ou=HR,ou=People,dc=demo,dc=com
    changetype: add
    objectClass: inetOrgPerson
    cn: hr-miao
    userPassword: 123456
    departmentNumber: 3
    sn: hr-miao
    title: HRBP
    mail: hr-miao@demo.com
    uid: 10006
    displayName: 我是HR
    
    ldapmodify -a -H ldapi:// -D "cn=admin,dc=demo,dc=com" -w admin -f addtwo.sh
    

    现在我们有两个用户来比较

    • cn=hr-miao,ou=HR,ou=People,dc=demo,dc=com 是普通用户
    • cn=hr-ryan,ou=HR,ou=People,dc=demo,dc=com 是g-admin用户

    分别来查询:

    [root@e6043aeb680e data]# ldapsearch -H ldap:/// -x -D cn=hr-ryan,ou=HR,ou=People,dc=demo,dc=com -w 123456 -b dc=demo,dc=com "cn=hr-ryan"  dn memberof
    # extended LDIF
    #
    # LDAPv3
    # base <dc=demo,dc=com> with scope subtree
    # filter: cn=hr-ryan
    # requesting: dn memberof 
    #
    
    # hr-ryan, HR, People, demo.com
    dn: cn=hr-ryan,ou=HR,ou=People,dc=demo,dc=com
    memberOf: cn=g-admin,ou=Group,dc=demo,dc=com
    
    # search result
    search: 2
    result: 0 Success
    
    # numResponses: 2
    # numEntries: 1
    
    [root@e6043aeb680e data]# ldapsearch -H ldap:/// -x -D cn=hr-miao,ou=HR,ou=People,dc=demo,dc=com -w 123456 -b dc=demo,dc=com "cn=hr-miao"  dn memberof
    # extended LDIF
    #
    # LDAPv3
    # base <dc=demo,dc=com> with scope subtree
    # filter: cn=hr-miao
    # requesting: dn memberof 
    #
    
    # search result
    search: 2
    result: 32 No such object
    
    # numResponses: 1
    

    可以看到,g-admin成员可以查询其他所有, 普通用户只能连接。

    比较更新密码能力:

    # updateselfpass.sh
    dn: cn=hr-miao,ou=HR,ou=People,dc=demo,dc=com
    changetype: modify
    replace: userPassword
    userPassword: ryanmiao
    
    # 修改自己的密码ok
    [root@e6043aeb680e data]# ldapmodify -H ldap:/// -x -D cn=hr-miao,ou=HR,ou=People,dc=demo,dc=com -w 123456 -f updateselfpass.sh 
    modifying entry "cn=hr-miao,ou=HR,ou=People,dc=demo,dc=com"
    
    # 确认密码被修改
    [root@e6043aeb680e data]# ldapmodify -H ldap:/// -x -D cn=hr-miao,ou=HR,ou=People,dc=demo,dc=com -w 123456 -f updateselfpass.sh 
    ldap_bind: Invalid credentials (49)
    
    # 修改dn为别人后不能修改密码
    [root@e6043aeb680e data]# ldapmodify -H ldap:/// -x -D cn=hr-miao,ou=HR,ou=People,dc=demo,dc=com -w ryanmiao -f updateselfpass.sh 
    modifying entry "cn=hr-ryan,ou=HR,ou=People,dc=demo,dc=com"
    ldap_modify: Insufficient access (50)
    

    总结ldap命令

    ldap主要命令有ldapadd, ldapmodify, ldapsearch. 我们用到的操作项有

    option 含义
    -H ldap server地址, 可以是ldap://192.168.12.18:389 表示tcp, 可以是ldap:/// 表示本地的tcp, 可以是ldapi:/// 本地unix socket连接
    -x 启用简单认证,通过-D dn -w 密码的方式认证
    -f 指定要修改的文件
    -a 使用ldapmodify增加一个entry的时候等同于ldapadd
    -b basedn 根目录, 将在此目录下查询
    -Y EXTERNAL 本地执行,修改配置文件,比如basedn, rootdn,rootpw,acl, module等信息

    ldapadd

    添加一个entry. 可以

    添加schema配置

    ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/cosine.ldif 
    

    添加额外的module

    ldapadd -Q -Y EXTERNAL -H ldapi:/// -f  add_module_group.sh 
    

    添加普通entry

    ldapadd -x -D cn=admin,dc=demo,dc=com -w admin -f base.ldif 
    

    ldapmodify

    修改entry, 可以

    更新配置信息

    ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f tmp.ldif
    

    对应的更新文件语法

    dn: 要更新的entry的dn, 配置为olcDatabase={2}hdb,cn=config, 用户为用户dn
    changetype: modify
    replace: olcRootDN   replace替换的字段
    olcRootDN: cn=admin,dc=demo,dc=com
    -
    replace: olcSuffix
    olcSuffix: dc=demo,dc=com
    -
    add: olcRootPW   add添加新的字段
    olcRootPW: <pass>
    

    ldapsearch

    search查询,主要集中在filter的使用上。

    ldapsearch -H ldapi:/// -D cn=admin,cn=demo,cn=com -w admin -s sub "filter"  attr
    

    -s scope 指定查询范围, 有base|one|sub|children 主要用sub表示base之下的所有子目录。对应Python里的SUBTREE

    filter语法,正则语法。因为使用的时候传递过来的通常是username, 需要比较username在ldap中的字段, 比如

    (|(cn=Steve*)(sn=Steve*)(mail=Steve*)(givenName=Steve*)(uid=Steve*))
    

    attr要返回的字段, 必须返回的字段可以在配置文件里查看。memberof非必须。

    安装phpldapadmin

    ldap提供了一个可视化的php web phpldapadmin.

    yum install httpd phpldapadmin -y
    

    备份配置文件

     cp /etc/httpd/conf.d/phpldapadmin.conf  /etc/httpd/conf.d/phpldapadmin.conf.bak
     cp /etc/phpldapadmin/config.php /etc/phpldapadmin/config.php.bak
    

    修改配置文件/etc/httpd/conf.d/phpldapadmin.conf,允许其他人访问

    Alias /phpldapadmin /usr/share/phpldapadmin/htdocs
    Alias /ldapadmin /usr/share/phpldapadmin/htdocs
    
    <Directory /usr/share/phpldapadmin/htdocs>
         <RequireAny>
           #Require ip 127.0.0.1
           #Require ip ::1
           Require all granted
         </RequireAny>
    </Directory>
    

    修改配置文件/etc/phpldapadmin/config.php,使用dn登录

    //$servers->setValue('login','attr','uid');      #注释掉这一行
    $servers->setValue('login','attr','dn');         #添加这一行
    

    启动httpd

    systemctl start httpd
    

    默认80端口访问,由于本测试已经映射端口为本地的8070,直接访问
    http://localhost:8071/phpldapadmin 或http://localhost:8071/ldapadmin即可。

    登录dn: cn=admin,dc=demo,dc=com

    登录密码: admin

    docker ldap

    将我们上述的docker提交即可获取一个ldap的docker镜像。

    docker commit -a "Ryan Miao" -m "这是一个ldap demo学习镜像"  ldap ldap-demo
    

    然而,docker镜像推荐使用dockerfile来构建,
    也就是说,需要把我们上述的操作都变成脚本去执行,把需要修改的内容变成环境变量。考验脚本编写能力。

    我们先来看看网络现有的docker镜像: https://github.com/osixia/docker-openldap

    ACL 附录

    关于acl语法

    olcAccess: <access directive>
        <access directive> ::= to <what>
            [by <who> [<access>] [<control>] ]+
        <what> ::= * |
            [dn[.<basic-style>]=<regex> | dn.<scope-style>=<DN>]
            [filter=<ldapfilter>] [attrs=<attrlist>]
        <basic-style> ::= regex | exact
        <scope-style> ::= base | one | subtree | children
        <attrlist> ::= <attr> [val[.<basic-style>]=<regex>] | <attr> , <attrlist>
        <attr> ::= <attrname> | entry | children
        <who> ::= * | [anonymous | users | self
                | dn[.<basic-style>]=<regex> | dn.<scope-style>=<DN>]
            [dnattr=<attrname>]
            [group[/<objectclass>[/<attrname>][.<basic-style>]]=<regex>]
            [peername[.<basic-style>]=<regex>]
            [sockname[.<basic-style>]=<regex>]
            [domain[.<basic-style>]=<regex>]
            [sockurl[.<basic-style>]=<regex>]
            [set=<setspec>]
            [aci=<attrname>]
        <access> ::= [self]{<level>|<priv>}
        <level> ::= none | disclose | auth | compare | search | read | write | manage
        <priv> ::= {=|+|-}{m|w|r|s|c|x|d|0}+
        <control> ::= [stop | continue | break]
    

    关于acl中的who

    Table 6.3: Access Entity Specifiers

    Specifier Entities
    * All, including anonymous and authenticated users
    anonymous Anonymous (non-authenticated) users
    users Authenticated users
    self User associated with target entry
    dn[.]= Users matching a regular expression
    dn.= Users within scope of a DN

    关于dn的授权

    For example, if the directory contained entries named:
    
        0: o=suffix
        1: cn=Manager,o=suffix
        2: ou=people,o=suffix
        3: uid=kdz,ou=people,o=suffix
        4: cn=addresses,uid=kdz,ou=people,o=suffix
        5: uid=hyc,ou=people,o=suffix
    Then:
    
    dn.base="ou=people,o=suffix" match 2; 
    dn.one="ou=people,o=suffix" match 3, and 5; 
    dn.subtree="ou=people,o=suffix" match 2, 3, 4, and 5; and 
    dn.children="ou=people,o=suffix" match 3, 4, and 5.
    

    一个acl示例

    
    # ACL1
    access to attrs=userpassword
           by self       write
           by anonymous  auth
           by group.exact="cn=itpeople,ou=groups,dc=example,dc=com"
                         write
           by *          none
    # ACL2
    access to attrs=carlicense,homepostaladdress,homephone
           by self       write
           by group.exact="cn=hrpeople,ou=groups,dc=example,dc=com"
                         write
           by *          none
    # ACL3
    access to *
           by self       write
           by group.exact="cn=hrpeople,ou=groups,dc=example,dc=com"
                         write
           by users      read
           by *          none
    

    参考

  • 相关阅读:
    关于android listview去掉分割线
    关于android在Service中弹出Dialog对话框
    Java SimpleDateFormat 函数
    关于Android使TextView可以滚动的设置
    关于Android(Java)创建匿名线程
    关于解决 Failed to prepare partial IU:
    毕业设计进度:2月4日
    毕业设计进度:2月3日
    毕业设计进度:2月2日
    毕业设计进度:2月1日
  • 原文地址:https://www.cnblogs.com/woshimrf/p/ldap.html
Copyright © 2020-2023  润新知