openldap介绍以及使用

参考文献:openldap介绍和使用

基本概念

官网:https://www.openldap.org

官方文档:https://www.openldap.org/doc

LDAP是一个开放的,中立的,工业标准的应用协议,通过IP协议提供访问控制和维护分布式信息的目录信息。

OpenLDAP是轻型目录访问协议(Lightweight Directory Access ProtocolLDAP)的自由和开源的实现,提供了目录服务的所有功能,包括目录搜索、身份认证、安全通道、过滤器等等。在其OpenLDAP许可证下发行,并已经被包含在众多流行的Linux发行版中。

openldapAD域其实都是LDAP的一种实现!

OpenLDAP它本身也是一个小型文件数据库。Ldap是树形结构的,能够通过server + client(服务端+客户端)的方式,进行统一的用户(账号)管理

OpenLDAP 默认以 Berkeley DB 作为后端数据库,Berkeley DB 数据库主要以散列的数据类型进行数据存储,如以键值对的方式进行存储。Berkeley DB 是一类特殊的数据库,主要作用于搜索、浏览、更新查询操作,一般用于一次写入数据、多次查询和搜索有很好的效果。Berkeley DB 数据库时面向查询进行优化,面向读取进行优化的数据库。Berkeley DB 不支持事务性数据库(MySQL、MariDB、Oracle 等)所支持的高并发的吞吐量以及复杂的事务操作。

介绍图:

在这里插入图片描述

结构图:

在这里插入图片描述

通常我们在ldap中维护的数据大概会像如下目录树:

在这里插入图片描述

在一个组织中,为了简化各种内部系统的账号和密码的管理,往往就需要ldap来进行管理了。

以下则是他的信息模型

目录树概念

  • 目录树

    在一个目录服务系统中,整个目录信息集可以表示为一个目录信息树,树中的每个节点是一个条目(Entry)。

  • 条目(Entry)

    条目,也叫记录项,是LDAP中最基本的颗粒,就像字典中的词条,或者是数据库中的记录。通常对LDAP的添加、删除、更改、检索都是以条目为基本对象的。

    LDAP目录的条目(entry)由属性(attribute)的一个聚集组成,并由一个唯一性的名字引用,即专有名称distinguished name,DN)。例如,DN能取这样的值:“cn=group,dc=eryajf,dc=net”。

  • 对象类(ObjectClass)

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

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

    每个条目都可以有很多属性(Attribute),比如常见的人都有姓名、地址、电话等属性。每个属性都有名称及对应的值,属性值可以有单个、多个。

基础字段

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

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

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

应用场景

早期,公司是没有统一认证这个东西的,所以各自玩各自的。于是, confluence一个用户体系,gitlab一个用户体系,Jenkins一个用户体系等等, 开发中要用到的开源软件数不胜数,每个软件都要认证, 必须想办法统一账号

与AD域的区别

其实工作中很多小伙伴有接触过ldap。但是不知道ldap具体是什么东西。与公司域账号(或统一登录账号)的区别是什么。相信最开始接触这些账号系统的小伙伴都是一脸闷逼的吧。那么我们先了解下几种账号体系的区别:

对比项Ldap账号域账号统一登录体系(如:平安UM/同程OA)
简称ldap账号AD域账号UM账号/OA账号/邮箱账号 等等
管理方向机器登录/应用登录机器登录/应用登录针对应用网站登录(例如: 管理网站,办公网站等等)
节点部署类型主服务 + 客户端主服务 + 客户端主服务
常见部署环境Linux系统下window系统下根据开发语种选择linux或window
安装软件openLdapAD自研程序
可部署系统环境Linux/windowLinux/windowLinux/window
入职时是否自动开通一般不会一般不会一般会开通

说明:一般情况下,AD域用来登录工作的电脑;Ldap用来登录linux机器或者应用管理网站;自研的账号系统用来登录应用管理网站。

用ldap管理机器认证(图)

在这里插入图片描述

用ldap做应用登录认证(图)

在这里插入图片描述

AD域的介绍

AD域的详细介绍

部署

openldap docker部署

采用docker的方式进行部署,镜像来源与dockerhub

docker run -dit --name openldap \
-u root \
--restart=always \
-p 389:1389 -p 636:1636 \
-e LDAP_ADMIN_USERNAME='admin' \
-e LDAP_ROOT='dc=wlong,dc=com' \
-e LDAP_ADMIN_PASSWORD='wl123456' \
bitnami/openldap:latest

其中389和636端口分别为openldap的非加密与加密端口,类似于http和https的80和443端口。

LDAP_ADMIN_USERNAME: LDAP数据库管理员用户,默认:admin

LDAP_ROOT:LDAP 树的 LDAP baseDN (或后缀)。默认值: dc = example,dc = org

LDAP_ADMIN_PASSWORD:为这颗树的默认管理用户admin的密码

执行后用docker logs命令看一下日志。

bitnami/openldap的镜像地址为: bitnami/containers

其中:

openldap的配置目录为:opt/bitnami/openldap/etc,配置讲解可以查看 centos系统部署 的内容

openldap centos系统部署

yum命令安装

yum -y install openldap compat-openldap openldap-clients openldap-servers openldap-servers-sql openldap-devel migrationtools

查看OpenLDAP版本:

slapd -VV

@(#) $OpenLDAP: slapd 2.4.44 (Feb 23 2022 17:11:27) $
        mockbuild@x86-01.bsys.centos.org:/builddir/build/BUILD/openldap-2.4.44/openldap-2.4.44/servers/slapd

OpenLDAP的相关配置文件信息

  • /etc/openldap/slapd.d/*:这下面是/etc/openldap/slapd.conf配置信息生成的文件,每修改一次配置信息,这里的东西就要重新生成
  • /etc/openldap/schema/*:OpenLDAP的schema存放的地方
  • /var/lib/ldap/*:OpenLDAP的数据文件
  • /usr/share/openldap-servers/DB_CONFIG.example 模板数据库配置文件

OpenLDAP监听的端口:

  • 默认监听端口:389(明文数据传输)
  • 加密监听端口:636(密文数据传输)

启动OpenLDAP

# 开机启动
systemctl enable --now slapd
# 启动
systemctl start slapd
# 查看状态
systemctl status slapd.service

# 显示
slapd.service - OpenLDAP Server Daemon
   Loaded: loaded (/usr/lib/systemd/system/slapd.service; enabled; vendor preset: disabled)
   Active: active (running) since Thu 2023-02-16 11:36:26 CST; 2h 36min ago
   ...
   
# 查看端口   
ss -natup | grep 389

tcp    LISTEN     0      128       *:389                   *:*                   users:(("slapd",pid=16691,fd=8))
tcp    LISTEN     0      128    [::]:389                [::]:*                   users:(("slapd",pid=16691,fd=9))

添加防火墙允许(防火墙关闭可忽略)

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

配置文件目录

openldap的配置目录为:/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 ab47d062
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: b05cd796-4c6e-103d-9ab4-997fd8d796b2
creatorsName: cn=config
createTimestamp: 20230301111934Z
entryCSN: 20230301111934.333783Z#000000#000#000000
modifiersName: cn=config
modifyTimestamp: 20230301111934Z

olcDatabase={2}hdb.ldif

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

[root@iZ slapd.d]# ldapsearch  -Y EXTERNAL -H ldapi:/// -b cn=config dn | grep olcDatabase
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0
dn: olcDatabase={-1}frontend,cn=config
dn: olcDatabase={0}config,cn=config
dn: olcDatabase={1}monitor,cn=config
dn: olcDatabase={2}hdb,cn=config

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

cat olcDatabase\=\{2\}hdb.ldif
# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
# CRC32 dcc1ece8
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: b05d1706-4c6e-103d-9aba-997fd8d796b2
creatorsName: cn=config
createTimestamp: 20230301111934Z
entryCSN: 20230301111934.335429Z#000000#000#000000
modifiersName: cn=config
modifyTimestamp: 20230301111934Z

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

创建管理员账号

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

创建rootdn.ldif文件

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

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

由于这里我们密码用了一个占位符,所以接下来我们使用一个脚本,来替换占位符以及创建管理员,脚本如下:

changeroot.sh

admin_pass=`slappasswd -s wl123456`
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
  • slappasswd -s admin 密码为"wl123456",这个命令一执行,则是加密后的密文;
  • 备份原始文件
  • ldapmodify 更新命令, -H指定host,这里ldapi:/// 表示IPC (Unix-domain socket)协议, -f指定变更的内容。 命令文档: http://man7.org/linux/man-pages/man1/ldapmodify.1.html

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

执行脚本:

[root@iZ slapd.d]# bash changeroot.sh 
admin pass is:  {SSHA}sKvZltfAtdTcwUiGTDn9lvd2VVbbk/zd
备份默认配置
将要修改的内容:
dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcRootDN
olcRootDN: cn=admin,dc=wlong,dc=com
-
replace: olcSuffix
olcSuffix: dc=wlong,dc=com
-
replace: olcRootPW
olcRootPW: {SSHA}sKvZltfAtdTcwUiGTDn9lvd2VVbbk/zd
modifying entry "olcDatabase={2}hdb,cn=config"

修改后的变化
2c2
< # CRC32 e63c0a84
---
> # CRC32 dcc1ece8
7a8,9
> olcSuffix: dc=my-domain,dc=com
> olcRootDN: cn=Manager,dc=my-domain,dc=com
14,19c16,18
< olcRootDN: cn=admin,dc=wlong,dc=com
< olcSuffix: dc=wlong,dc=com
< olcRootPW:: e1NTSEF9c0t2Wmx0ZkF0ZFRjd1VpR1REbjlsdmQyVlZiYmsvemQ=
< entryCSN: 20230302023857.029105Z#000000#000#000000
< modifiersName: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
< modifyTimestamp: 20230302023857Z
---
> entryCSN: 20230301111934.335429Z#000000#000#000000
> modifiersName: cn=config
> modifyTimestamp: 20230301111934Z

我们可以通过以下命令来验证密码

[root@iZ slapd.d]# ldapwhoami -x -H ldapi:/// -D cn=admin,dc=wlong,dc=com -w wl123456
dn:cn=admin,dc=wlong,dc=com

[root@iZ slapd.d]# ldapwhoami -x -H ldapi:/// -D cn=admin,dc=wlong,dc=com -w 1111
ldap_bind: Invalid credentials (49)

或者

[root@iZ slapd.d]# ldapsearch -H ldapi:///  -D "cn=admin,dc=wlong,dc=com" -w 1111
ldap_bind: Invalid credentials (49)
[root@iZ slapd.d]# ldapsearch -H ldapi:///  -D "cn=admin,dc=wlong,dc=com" -w wl123456
# 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启用认证,ldapi:///指的是本地

添加组织结构

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

有了管理员,还需要配置组织结构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=wlong,dc=com
objectClass: top
objectClass: dcObject
objectClass: organization
o: ldap测试组织
dc: wlong

dn: cn=Manager,dc=wlong,dc=com
objectClass: organizationalRole
cn: Manager
description: 组织管理人

dn: ou=People,dc=wlong,dc=com
objectClass: organizationalUnit
ou: People

dn: ou=Group,dc=wlong,dc=com
objectClass: organizationalUnit
ou: Group

使用ldapadd添加base:

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

使用ldapsearch来检查内容

[root@iZ openldap]# ldapsearch -x -D cn=admin,dc=wlong,dc=com -w wl123456 -b "dc=wlong,dc=com"
# extended LDIF
#
# LDAPv3
# base <dc=wlong,dc=com> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

# wlong.com
dn: dc=wlong,dc=com
objectClass: top
objectClass: dcObject
objectClass: organization
o:: bGRhcOa1i+ivlee7hOe7hw==
dc: wlong

# Manager, wlong.com
dn: cn=Manager,dc=wlong,dc=com
objectClass: organizationalRole
cn: Manager
description:: 57uE57uH566h55CG5Lq6

# People, wlong.com
dn: ou=People,dc=wlong,dc=com
objectClass: organizationalUnit
ou: People

# Group, wlong.com
dn: ou=Group,dc=wlong,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=wlong,dc=com下。

创建adduser.ldif

dn: ou=研发部门,ou=People,dc=wlong,dc=com
changetype: add
objectClass: organizationalUnit
ou: 研发部门

dn: ou=后台组,ou=研发部门,ou=People,dc=wlong,dc=com
changetype: add
objectClass: organizationalUnit
ou: 后台组



dn: cn=ryan.miao,ou=后台组,ou=研发部门,ou=People,dc=wlong,dc=com
changetype: add
objectClass: inetOrgPerson
cn: ryan.miao
departmentNumber: 1
sn: Miao
title: 大牛
mail: ryan.miao@wlong.com
userPassword: 123456
uid: 10000
displayName: 中文名



dn: cn=someone,ou=后台组,ou=研发部门,ou=People,dc=wlong,dc=com
changetype: add
objectClass: inetOrgPerson
cn: someone
departmentNumber: 1
sn: someone
title: Java工程师
mail: someone@wlong.com
userPassword: 123456
uid: 10001
displayName: 某人


dn: ou=测试组,ou=研发部门,ou=People,dc=wlong,dc=com
changetype: add
objectClass: organizationalUnit
ou: 测试组


dn: cn=tester.miao,ou=测试组,ou=研发部门,ou=People,dc=wlong,dc=com
changetype: add
objectClass: inetOrgPerson
cn: tester.miao
departmentNumber: 2
sn: Miao
title: 测试工程师
userPassword: 123456
mail: tester@wlong.com
uid: 10002
displayName: 测试某人


dn: ou=HR,ou=People,dc=wlong,dc=com
changetype: add
objectClass: organizationalUnit
ou: HR


dn: cn=fang.huang,ou=HR,ou=People,dc=wlong,dc=com
changetype: add
objectClass: inetOrgPerson
cn: fang.huang
departmentNumber: 3
sn: Huang
title: HRBP
userPassword: 123456
mail: fang.huang@wlong.com
uid: 10003
displayName: 黄芳

使用ldapadd来添加我们的用户

[root@iZ openldap]# ldapadd -x -D cn=admin,dc=wlong,dc=com -w wl123456 -f adduser.ldif
adding new entry "ou=研发部门,ou=People,dc=wlong,dc=com"

adding new entry "ou=后台组,ou=研发部门,ou=People,dc=wlong,dc=com"

adding new entry "cn=ryan.miao,ou=后台组,ou=研发部门,ou=People,dc=wlong,dc=com"

adding new entry "cn=someone,ou=后台组,ou=研发部门,ou=People,dc=wlong,dc=com"

adding new entry "ou=测试组,ou=研发部门,ou=People,dc=wlong,dc=com"

adding new entry "cn=tester.miao,ou=测试组,ou=研发部门,ou=People,dc=wlong,dc=com"

adding new entry "ou=HR,ou=People,dc=wlong,dc=com"

adding new entry "cn=fang.huang,ou=HR,ou=People,dc=wlong,dc=com"

使用ldapsearch来查询用户

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

[root@iZ openldap]# ldapsearch -x -D cn=admin,dc=wlong,dc=com -w admin -b "dc=wlong,dc=com" "cn=ryan.miao"
# extended LDIF
#
# LDAPv3
# base <dc=wlong,dc=com> with scope subtree
# filter: cn=ryan.miao
# requesting: ALL
#

# ryan.miao, \E5\90\8E\E5\8F\B0\E7\BB\84, \E7\A0\94\E5\8F\91\E9\83\A8\E9\97\A8,
  People, wlong.com
dn:: Y249cnlhbi5taWFvLG91PeWQjuWPsOe7hCxvdT3noJTlj5Hpg6jpl6gsb3U9UGVvcGxlLGRjP
 Xdsb25nLGRjPWNvbQ==
objectClass: inetOrgPerson
cn: ryan.miao
departmentNumber: 1
sn: Miao
title:: 5aSn54mb
mail: ryan.miao@wlong.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@iZ openldap]# ldapsearch -x -D cn=admin,dc=wlong,dc=com -w admin -b "ou=HR,ou=People,dc=wlong,dc=com"  cn uid  displayName
# extended LDIF
#
# LDAPv3
# base <ou=HR,ou=People,dc=wlong,dc=com> with scope subtree
# filter: (objectclass=*)
# requesting: cn uid displayName 
#

# HR, People, wlong.com
dn: ou=HR,ou=People,dc=wlong,dc=com

# fang.huang, HR, People, wlong.com
dn: cn=fang.huang,ou=HR,ou=People,dc=wlong,dc=com
cn: fang.huang
uid: 10003
displayName:: 6buE6IqzOg==

# 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=wlong,dc=com
changetype: add
objectClass: inetOrgPerson
cn: hr-ryan
userPassword: 123456
departmentNumber: 3
sn: hr-ryan
title: HRBP
mail: hr-ryan@wlong.com
uid: 10004
displayName: 我是猎头

执行添加

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

查询验证

root@e6043aeb680e data]# ldapsearch -x -D cn=admin,dc=wlong,dc=com -w admin -b dc=wlong,dc=com "cn=hr-*"
# extended LDIF
#
# LDAPv3
# base <dc=wlong,dc=com> with scope subtree
# filter: cn=hr-*
# requesting: ALL
#

# hr-ryan, HR, People, wlong.com
dn: cn=hr-ryan,ou=HR,ou=People,dc=wlong,dc=com
objectClass: inetOrgPerson
cn: hr-ryan
userPassword:: MTIzNDU2
departmentNumber: 3
sn: hr-ryan
title: HRBP
mail: hr-ryan@wlong.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=wlong,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=wlong,dc=com
changetype: modify
replace: userPassword
userPassword: ryanmiao

执行修改

ldapmodify -a -H ldapi:/// -D "cn=admin,dc=wlong,dc=com" -w wl123456 -f updatepass.ldif 

查询确认

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

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

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

slappasswd -s ryanmiao
{SSHA}r5yzPeESGLsvX7oxQetVEpel9LhygFef


dn: cn=ryan.miao,ou=后台组,ou=研发部门,dc=wlong,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并不能当做分组,而仅仅是组织架构的一个单元。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.ldif

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.ldif

创建add_group_objectClass.ldif

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.ldif

添加一个group

创建addgroup.ldif

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

执行

ldapmodify -a -H ldapi:/// -D "cn=admin,dc=wlong,dc=com" -w wl123456 -f addgroup.ldif

查看组

[root@iZ2ze9mi8xjyj68byrel2cZ openldap]# ldapsearch -H ldapi:/// -x -D "cn=admin,dc=wlong,dc=com" -w wl123456 -b "ou=Group,dc=wlong,dc=com"
# extended LDIF
#
# LDAPv3
# base <ou=Group,dc=wlong,dc=com> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

# Group, wlong.com
dn: ou=Group,dc=wlong,dc=com
objectClass: organizationalUnit
ou: Group

# g-admin, Group, wlong.com
dn: cn=g-admin,ou=Group,dc=wlong,dc=com
objectClass: groupOfNames
cn: g-admin
member:: Y249cnlhbi5taWFvLG91PeWQjuWPsOe7hCxvdT3noJTlj5Hpg6jpl6gsb3U9UGVvcGxlL
 GRjPXdsb25nLGRjPWNvbQ==
member: cn=hr-ryan,ou=HR,ou=People,dc=wlong,dc=com

# search result
search: 2
result: 0 Success

# numResponses: 3
# numEntries: 2

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

[root@iZ2ze9mi8xjyj68byrel2cZ openldap]# ldapsearch -H ldapi:/// -x -D "cn=admin,dc=wlong,dc=com" -w wl123456 -b "ou=People,dc=wlong,dc=com" "(|(cn=ryan.miao)(cn=hr-*))" memberof
# extended LDIF
#
# LDAPv3
# base <ou=People,dc=wlong,dc=com> with scope subtree
# filter: (|(cn=ryan.miao)(cn=hr-*))
# requesting: memberof 
#

# ryan.miao, \E5\90\8E\E5\8F\B0\E7\BB\84, \E7\A0\94\E5\8F\91\E9\83\A8\E9\97\A8,
  People, wlong.com
dn:: Y249cnlhbi5taWFvLG91PeWQjuWPsOe7hCxvdT3noJTlj5Hpg6jpl6gsb3U9UGVvcGxlLGRjP
 Xdsb25nLGRjPWNvbQ==
memberOf: cn=g-admin,ou=Group,dc=wlong,dc=com

# hr-ryan, HR, People, wlong.com
dn: cn=hr-ryan,ou=HR,ou=People,dc=wlong,dc=com
memberOf: cn=g-admin,ou=Group,dc=wlong,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=wlong,dc=com
bind_password = admin
basedn = dc=wlong,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.ldif
dn: cn=g-users,ou=Group,dc=wlong,dc=com
objectClass: groupOfNames
cn: g-users
member: cn=ryan.miao,ou=后台组,ou=研发部门,ou=People,dc=wlong,dc=com
member: cn=hr-ryan,ou=HR,ou=People,dc=wlong,dc=com

ldapmodify -a -H ldapi:/// -D "cn=admin,dc=wlong,dc=com" -w wl123456 -f commongroup.ldif

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

[root@iZ2ze9mi8xjyj68byrel2cZ openldap]# ldapsearch -H ldapi:/// -D cn=admin,dc=wlong,dc=com -w wl123456 -b dc=wlong,dc=com -s sub "objectClass=groupOfNames"  dn member
# extended LDIF
#
# LDAPv3
# base <dc=wlong,dc=com> with scope subtree
# filter: objectClass=groupOfNames
# requesting: dn member 
#

# g-admin, Group, wlong.com
dn: cn=g-admin,ou=Group,dc=wlong,dc=com
member:: Y249cnlhbi5taWFvLG91PeWQjuWPsOe7hCxvdT3noJTlj5Hpg6jpl6gsb3U9UGVvcGxlL
 GRjPXdsb25nLGRjPWNvbQ==
member: cn=hr-ryan,ou=HR,ou=People,dc=wlong,dc=com

# g-users, Group, wlong.com
dn: cn=g-users,ou=Group,dc=wlong,dc=com
member:: Y249cnlhbi5taWFvLG91PeWQjuWPsOe7hCxvdT3noJTlj5Hpg6jpl6gsb3U9UGVvcGxlL
 GRjPXdsb25nLGRjPWNvbQ==
member: cn=hr-ryan,ou=HR,ou=People,dc=wlong,dc=com

# search result
search: 2
result: 0 Success

# numResponses: 3
# numEntries: 2

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

创建addUserToGroup.ldif

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

执行

ldapmodify -H ldapi:/// -x -D cn=admin,dc=wlong,dc=com -w wl123456 -f addUserToGroup.ldif

查看

[root@iZ2ze9mi8xjyj68byrel2cZ openldap]# ldapsearch -H ldap:/// -D cn=admin,dc=wlong,dc=com -w wl123456 -b dc=wlong,dc=com -s sub "objectClass=groupOfNames"
# extended LDIF
#
# LDAPv3
# base <dc=wlong,dc=com> with scope subtree
# filter: objectClass=groupOfNames
# requesting: ALL
#

# g-admin, Group, wlong.com
dn: cn=g-admin,ou=Group,dc=wlong,dc=com
objectClass: groupOfNames
cn: g-admin
member:: Y249cnlhbi5taWFvLG91PeWQjuWPsOe7hCxvdT3noJTlj5Hpg6jpl6gsb3U9UGVvcGxlL
 GRjPXdsb25nLGRjPWNvbQ==
member: cn=hr-ryan,ou=HR,ou=People,dc=wlong,dc=com

# g-users, Group, wlong.com
dn: cn=g-users,ou=Group,dc=wlong,dc=com
objectClass: groupOfNames
cn: g-users
member:: Y249cnlhbi5taWFvLG91PeWQjuWPsOe7hCxvdT3noJTlj5Hpg6jpl6gsb3U9UGVvcGxlL
 GRjPXdsb25nLGRjPWNvbQ==
member: cn=hr-ryan,ou=HR,ou=People,dc=wlong,dc=com
member: cn=fang.huang,ou=HR,ou=People,dc=wlong,dc=com
member:: Y249c29tZW9uZSxvdT3lkI7lj7Dnu4Qsb3U956CU5Y+R6YOo6ZeoLG91PVBlb3BsZSxkY
 z13bG9uZyxkYz1jb20=
member:: Y249dGVzdGVyLm1pYW8sb3U95rWL6K+V57uELG91PeeglOWPkemDqOmXqCxvdT1QZW9wb
 GUsZGM9d2xvbmcsZGM9Y29t

# search result
search: 2
result: 0 Success

# numResponses: 3
# numEntries: 2

从Group中移除user

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

创建removeUserFromGroup.ldif

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

执行

ldapmodify -H ldapi:/// -x -D cn=admin,dc=wlong,dc=com -w wl123456 -f removeUserFromGroup.ldif

最终Group和user的关系

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

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

在这里插入图片描述

ACL权限控制

从前面的测试可以看到,默认是没开启权限的。任何人都可以连接查询和操作。包括匿名访问,可以查看所有人的信息!!!

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

acl就在olcDatabase={2}hdb文件中配置, 我们可以查看具体的配置:

[root@iZ2ze9mi8xjyj68byrel2cZ openldap]# 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=wlong,dc=com
olcSuffix: dc=wlong,dc=com
olcRootPW: {SSHA}sKvZltfAtdTcwUiGTDn9lvd2VVbbk/zd

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

语法

DN指定语法
  • 使用正则表达式(dn.regex)或精确匹配(dn.style)的方式来匹配符合条件的记录

    例如:dn.regex=“ou=(.+),dc=wlong,dc=com$”;意思为:匹配用户的所有ou;

  • access to dn.(base|one|subtree|children)=DN(“ou=People,dc=koudai,dc=com”)

    第二种方法通过“区域”选择的方法进行目标记录的选取,对以指定的DN开始的目录树区域进行目标记录匹配。匹配区域的方式共有四种:

    • base 只匹配DN本身一条记录
    • one 匹配以给定DN为父目录的所有记录
    • subtree 匹配以给定DN为根目录的所有子树内的记录
    • children 匹配给定DN下的所有记录,但应该不包括以DN直接命名的那条记录(参见例子的解释)

    例如:对于

    0: dc=mydomain,dc=org
    1: cn=root,dc=mydomain,dc=org
    2: ou=users,dc=mydomain,dc=org
    3: uid=samba,ou=users,dc=mydomain,dc=org
    4: cn=Administator,uid=samba,ou=users,dc=mydomain,dc=org
    5: uid=guest,ou=users,dc=mydomain,dc=org
    

    规则 dn.base=”ou=users,dc=mydomain,dc=org” 只会匹配记录2
    规则 dn.one=”ou=users,dc=mydomain,dc=org” 匹配记录3和记录5,记录4是记录3的子目录,故不算在内
    规则 dn.subtree=”ou=users,dc=mydomain,dc=org” 匹配记录2、3、4、5
    规则 dn.children=”ou=users,dc=mydomain,dc=org” 匹配记录3、4、5,因为记录0、1和2都是以DN直接命名的,故不匹配

  • 通过filter指定过滤规则进行记录过虑,语法如下:
    access to filter=ldap filter
    其中filter指定的为search的过滤规则,这类同于linux系统中grep的匹配方式。如:

    access to filter=(objectClass=sambaSamAccount)
    也可以结合使用DN和filter进行记录的匹配,例如:
    access to dn.subtree=”ou=users,dc=mydomain,dc=org” filter=(objectClass=posixAccount)
    
  • 通过attrs选取匹配记录语法:
    access to attrs=uid,uidNumber,gidNumber
    也可以结合使用DN和attrs进行记录的匹配,例如:

    access to dn.subtree="ou=users,dc=mydomain,dc=org" attrs=uid
    
用来授权的访问者

指定被授权的用户范围的方法大致有以下几种:

* 所有的访问者,包括匿名的用户 
anonymous 非认证的匿名用户 
users 认证的用户 
self 目标记录的用户自身 
dn[.<basic-style>]=<regex> 在指定目录内匹配正则表达式的用户 
dn.<scope-style>=<DN> 指定DN内的用户
被授予的权限access

当选取好ACL作用的目标记录并选取好用户范围后,就该给这些用户授予他们应该得到的权限了。大致的权限(由低到高)有以下几类:

none 无权限,即拒绝访问 
auth 访问bind(认证)设置的权限;前提是需要用户提交一个DN形式的用户名并能通过认证 
compare 比较属性的权限;(例如:对照查看某用户的telephoneNumber值是不是158 8888 8888),但并不具有搜索的权限 
search 利用过虑条件进行搜索的权限,但这并不一定具有可读取搜索结果的权限 
read 读取搜索结果的权限 
write 更改记录属性值的权限

上述只是部分内容,具体还需要参考官方文档,这里面至说了基础,具体怎么配置,还是参考官方文档: Access Control

ACL示例

创建acl.ldif

dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcAccess
olcAccess: to attrs=userPassword 
  by self write 
  by anonymous auth 
  by group.exact="cn=g-admin,ou=Group,dc=wlong,dc=com" write 
  by * none
olcAccess: to * 
  by self write 
  by group.exact="cn=g-admin,ou=Group,dc=wlong,dc=com"  write   
  by * none
  • access to attrs=userPassword 通过属性找到访问范围密码;
  • 管理员可能不止一个,创建管理员组"cn=g-admin,ou=Group,dc=wlong,dc=com"把管理员统一都放到这个组下,管理员组下的所有用户(dn.children)有写权限;
  • 匿名用户(anonymous)要通过验证(auth)
  • 自己(self)有对自己密码的写(write)权限,其他人(*)都没有权限(none)
  • access to * 所有其他属性
  • 管理员"cn=g-admin,ou=Group,dc=wlong,dc=com"成员有写(write)权限;
    其他人(*)只有读(read)权限
    自己(self)有读的权限

执行

ldapmodify -H ldapi:///  -Y EXTERNAL -f acl.ldif

验证权限

添加一个普通用户:

# addtwo.ldif
dn: cn=hr-miao,ou=HR,ou=People,dc=wlong,dc=com
changetype: add
objectClass: inetOrgPerson
cn: hr-miao
userPassword: 123456
departmentNumber: 3
sn: hr-miao
title: HRBP
mail: hr-miao@wlong.com
uid: 10006
displayName: 我是HR

ldapmodify -a -H ldapi:// -D "cn=admin,dc=wlong,dc=com" -w admin -f addtwo.sh

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

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

分别来查询:

[root@iZ2ze9mi8xjyj68byrel2cZ ~]# ldapsearch -H ldap:/// -x -D cn=hr-ryan,ou=HR,ou=People,dc=wlong,dc=com -w 123456 -b dc=wlong,dc=com "cn=hr-ryan"  dn memberof
# extended LDIF
#
# LDAPv3
# base <dc=wlong,dc=com> with scope subtree
# filter: cn=hr-ryan
# requesting: dn memberof 
#

# hr-ryan, HR, People, wlong.com
dn: cn=hr-ryan,ou=HR,ou=People,dc=wlong,dc=com
memberOf: cn=g-admin,ou=Group,dc=wlong,dc=com
memberOf: cn=g-users,ou=Group,dc=wlong,dc=com

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1
[root@iZ ~]# ldapsearch -H ldap:/// -x -D cn=hr-miao,ou=HR,ou=People,dc=wlong,dc=com -w 123456 -b dc=wlong,dc=com "cn=hr-miao"  dn memberof
# extended LDIF
#
# LDAPv3
# base <dc=wlong,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.ldif
dn: cn=hr-miao,ou=HR,ou=People,dc=wlong,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=wlong,dc=com -w 123456 -f updateselfpass.ldif
modifying entry "cn=hr-miao,ou=HR,ou=People,dc=wlong,dc=com"

# 确认密码被修改
[root@e6043aeb680e data]# ldapmodify -H ldap:/// -x -D cn=hr-miao,ou=HR,ou=People,dc=wlong,dc=com -w 123456 -f updateselfpass.ldif 
ldap_bind: Invalid credentials (49)

# 修改dn为别人后不能修改密码
[root@e6043aeb680e data]# ldapmodify -H ldap:/// -x -D cn=hr-miao,ou=HR,ou=People,dc=wlong,dc=com -w ryanmiao -f updateselfpass.ldif
modifying entry "cn=hr-ryan,ou=HR,ou=People,dc=wlong,dc=com"
ldap_modify: Insufficient access (50)

实践过的ACL

只有管理员能修改密码,及管理员能查看所有目录
其他账号不能修改密码,不能查看所有目录

dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcAccess
olcAccess: to attrs=userPassword
  by anonymous auth
  by dn.base="cn=Manager-Fxd,dc=fxd-ldap,dc=com" write
  by * none
olcAccess: to *
  by anonymous auth
  by dn.base="cn=Manager-Fxd,dc=fxd-ldap,dc=com" write
  by dn.base="cn=Manager-Fxd,dc=fxd-ldap,dc=com" read
  by * none

所有用户需要权限校验
超管账号可以查看和编辑所有目录和属性
ou=People,dc=fxd-ldap,dc=com下的用户,可以查看除超管外的其他目录,查看不到密码属性

dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcAccess
olcAccess: to attrs=userPassword
  by anonymous auth
  by dn.base="cn=Manager-Fxd,dc=fxd-ldap,dc=com" write
  by * none
olcAccess: to dn.base="cn=Manager-Fxd,dc=fxd-ldap,dc=com"
  by anonymous auth
  by self write
  by self read
  by * none
olcAccess: to *
  by anonymous auth
  by dn.subtree="ou=People,dc=fxd-ldap,dc=com" write
  by dn.subtree="ou=People,dc=fxd-ldap,dc=com" read
  by * none 

我们希望如 dc=ali,dc=example,dc=com 仅能够被 “ali” 这个节点下的用户看到,而“dc=ali” 这个节点下的用户又仅能访问本节点下的信息,而无法看到如 dc=huawei 节点下的信息。即使 dc=baidu, dc=ali, dc=huawei 之间具有隔离性,各个公司仅能访问自己公司的节点下的 subtree, 而其他公司无法访问或是被访问
在这里插入图片描述

# acl.ldif 内容
dn: olcDatabase={2}mdb,cn=config
changetype: modify
replace: olcAccess
olcAccess: to attrs=userPassword
  by dn="cn=admin,dc=example,dc=com" write
  by anonymous auth
  by self write
  by * none
olcAccess: to dn.regex="^cn=system_user,ou=system_user"
  by dn="cn=admin,dc=example,dc=com" write
  by * read
olcAccess: to dn.regex="dc=([^,]+),dc=example,dc=com$"
  by dn.regex="cn=system_user,ou=system_user,dc=$1,dc=example,dc=com" write 
  by dn.regex="dc=$1,dc=example,dc=com$" read
  by * none
olcAccess: to *
  by dn="cn=admin,dc=example,dc=com" write
  by anonymous auth
  by self write
  by * read

acl.ldif 内容解读:
内容中第一个 olcAccess 设置的密码规则;
第二个 olcAccess 设置的各个公司下的 cn=system_user,ou=system_user…不可更改 ,仅超管可改;
第三个 olcAccess 设置的各个公司用户隔离,仅看到本公司下的节点;
第四个 olcAccess 设置的所有属性的可见性,匿名用户隔离;

删除ACL

dn: olcDatabase={2}hdb,cn=config
changetype: modify
delete: olcAccess
olcAccess: {0}

执行:

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

objectClass的使用方法

LDAP使用说明文档

JAVA/OpenLDAP使用心得

JAVA,OpenLDAP使用心得

停止openldap

systemctl stop slapd
systemctl disable slapd

#显示
Removed symlink /etc/systemd/system/multi-user.target.wants/slapd.service.

卸载

yum -y remove openldap-servers openldap-clients

删除残留文件

rm -rf /var/lib/ldap

删除ldap用户及组

userdel ldap
userdel 曾创建的其他ldap相关用户
groupdel 曾创建的其他ldap相关组

注意:若用户及组不删除干净会影响后面的重装。

删除openldap目录

rm -rf /etc/openldap

注意:此处删除前,需要将该路径下的certs文件夹备份留存,重装时,此文件夹不会再下载,由此会引起OpenLDAP重装后服务无法启动

开启OpenLDAP日志访问功能

新建日志配置ldif文件,如下:

cat > /root/openldap/loglevel.ldif << EOF
dn: cn=config
changetype: modify
replace: olcLogLevel
olcLogLevel: stats
EOF

ldapmodify -Y EXTERNAL -H ldapi:/// -f /root/openldap/loglevel.ldif

修改rsyslog配置文件,并重启rsyslog服务,如下:

cat >> /etc/rsyslog.conf << EOF
local4.* /var/log/slapd.log
EOF

重启服务

systemctl restart rsyslog
systemctl restart slapd

openldap控制台部署(phpldapAdmin)

phpldapAdmin官方网站:https://phpldapadmin.sourceforge.net/wiki/index.php

通过上面的步骤完成了对OpenLDAP安装。接下来就可以对ldap用默认账户和咱们设置的密码进行登录测试了。
OpenLDAP的管理工具也非常众多,如客户端版本的ldapadmin

在这里插入图片描述

也有灵活方便的在线web版本的PHPLdapAdmin。

在这里插入图片描述

咱们这里为了普适性,也选择PHPLdapAdmin作为OpenLDAP的控制台来进行部署。

其dockerhub中下载量最多的docker版本为osixia/phpldapadmin版本,其详细说明为:

在docker输入此命令启动

docker run -p 6443:443 \
        --env PHPLDAPADMIN_LDAP_HOSTS=172.17.222.14 \
        --volume /home/wanglong/phpldapadmin/config/:/container/service/phpldapadmin/assets/config/ \
        --detach osixia/phpldapadmin:latest

其中PHPLDAPADMIN_LDAP_HOSTS为OpenLDAP服务部署的ip,可以为域名或者ip。

6443端口为映射出来的访问phpLDAPadmin的https端口。

--volume是将phpldapadmin的配置文件挂在出来,以方便修改

比如,默认登录是以DN去登录的,你想变成uid,或者cn登录,就需要修改配置文件

#在/container/service/phpldapadmin/assets/config/config.php文件中增加:
#配置文件默认不写,默认的是dn
# 咱们可以修改为uid登录或者cn登录,一定要用唯一性的字段去登录

$servers->setValue('login','attr','uid');

# 上面那一个配置,其实普通用户都可以使用uid登录了,但是,超级管理员登录不了,
# 增加下面这3个配置,超级管理员也可以登录了,登录方式就是 `cn=admin,dc=wlong,dc=com`    密码:`wl123456`
$servers->setValue('login','bind_id','cn=admin,dc=wlong,dc=com');
$servers->setValue('login','bind_pass','wl123456');
$servers->setValue('login','fallback_dn',true);


# 上述配置我是用的是docker 启动的openldap,如果你是自身centos7 安装的,如果出现密码死活不对,检查一下防火墙以及selinux 是否关闭!!!

执行后,看一下日志。

之后在浏览器中进行登录测试一下。

由于docker里所在ip为192.168.1.7,所映射端口为6443,直接访问

https://192.168.1.7:6443

点击login。
其中的DN输入可以admin用户所对应的DN。
如上面安装时所对应的:

CN=admin,DC=wlong,DC=com

密码也为安装时所对应的wl123456

踩坑点

  1. 执行 systemctl start slapd 报错:sasl_auxprop_plug_init for plugin: ldapdb

    # systemctl status slapd.service
    Aug 31 22:38:52 master slapd[47714]: auxpropfunc error invalid parameter supplied
    Aug 31 22:38:52 master slapd[47714]: _sasl_plugin_load failed on sasl_auxprop_plug_init for plugin: ldapdb
    Aug 31 22:38:52 master slapd[47714]: ldapdb_canonuser_plug_init() failed in sasl_canonuser_add_plugin(): inval...pplied
    Aug 31 22:38:52 master slapd[47714]: _sasl_plugin_load failed on sasl_canonuser_init for plugin: ldapdb
    

    解决方案:

    yum remove cyrus-sasl-sql
    yum remove cyrus-sasl-ldap
    
  2. 执行 systemctl start slapd 报错:connections_destroy: nothing to destroy.

[root@iZ ~]# systemctl start slapd
Job for slapd.service failed because the control process exited with error code. See "systemctl status slapd.service" and "journalctl -xe" for details.
[root@iZ ~]# systemctl status slapd.service
● slapd.service - OpenLDAP Server Daemon
   Loaded: loaded (/usr/lib/systemd/system/slapd.service; disabled; vendor preset: disabled)
   Active: failed (Result: exit-code) since Wed 2023-03-01 19:45:35 CST; 8s ago
     Docs: man:slapd
           man:slapd-config
           man:slapd-hdb
           man:slapd-mdb
           file:///usr/share/doc/openldap-servers/guide.html
  Process: 23443 ExecStart=/usr/sbin/slapd -u ldap -h ${SLAPD_URLS} $SLAPD_OPTIONS (code=exited, status=1/FAILURE)
  Process: 23427 ExecStartPre=/usr/libexec/openldap/check-config.sh (code=exited, status=0/SUCCESS)

Mar 01 19:45:35 iZ runuser[23430]: pam_unix(runuser:session): session opened for user ldap by (uid=0)
Mar 01 19:45:35 iZ runuser[23430]: pam_unix(runuser:session): session closed for user ldap
Mar 01 19:45:35 iZ slapd[23443]: @(#) $OpenLDAP: slapd 2.4.44 (Feb 23 2022 17:11:27) $
                                                              mockbuild@x86-01.bsys.centos.org:/builddir/build/BUILD/openldap-2.4.44/openldap-2.4.44/servers/slapd
Mar 01 19:45:35 iZ slapd[23443]: main: TLS init def ctx failed: -1
Mar 01 19:45:35 iZ slapd[23443]: slapd stopped.
Mar 01 19:45:35 iZ slapd[23443]: connections_destroy: nothing to destroy.
Mar 01 19:45:35 iZ systemd[1]: slapd.service: control process exited, code=exited status=1
Mar 01 19:45:35 iZ systemd[1]: Failed to start OpenLDAP Server Daemon.
Mar 01 19:45:35 iZ systemd[1]: Unit slapd.service entered failed state.
Mar 01 19:45:35 iZ systemd[1]: slapd.service failed.

解决方案:

其实上述的错误是你之前装过openldap,后来又卸载的,连同把cert(路径为:/etc/openldap/certs)都删了,然后重装openldap的时候,是不会再次安装cert的,所以咱们得安装一下cert

[root@iZ ~]# slapd -d 2 -F /etc/openldap/slapd.d/ -u ldap
63ff3bcd @(#) $OpenLDAP: slapd 2.4.44 (Feb 23 2022 17:11:27) $
        mockbuild@x86-01.bsys.centos.org:/builddir/build/BUILD/openldap-2.4.44/openldap-2.4.44/servers/slapd
TLSMC: MozNSS compatibility interception begins.
tlsmc_open_nssdb: WARN: could not initialize MozNSS context - error -8015.
tlsmc_convert: INFO: cannot open the NSS DB, expecting PEM configuration is present.
tlsmc_intercept_initialization: INFO: successfully intercepted TLS initialization. Continuing with OpenSSL only.
TLSMC: MozNSS compatibility interception ends.
TLS: could not use certificate `OpenLDAP Server'.
TLS: error:0200100D:system library:fopen:Permission denied bss_file.c:402
TLS: error:20074002:BIO routines:FILE_CTRL:system lib bss_file.c:404
TLS: error:140AD002:SSL routines:SSL_CTX_use_certificate_file:system lib ssl_rsa.c:468
63ff3bcd main: TLS init def ctx failed: -1
63ff3bcd slapd stopped.
63ff3bcd connections_destroy: nothing to destroy.
[root@iZ ~]# mkdir -p /etc/openldap/certs
[root@iZ ~]# bash /usr/libexec/openldap/create-certdb.sh
Creating certificate database in '/etc/openldap/certs'.
[root@iZ ~]# bash /usr/libexec/openldap/generate-server-cert.sh
Creating new server certificate in '/etc/openldap/certs'.
[root@iZ ~]# ll /etc/openldap/certs
total 100
-rw-r--r-- 1 root root 65536 Mar  1 19:49 cert8.db
-rw-r--r-- 1 root root 16384 Mar  1 19:49 key3.db
-r--r----- 1 root ldap    45 Mar  1 19:49 password
-rw-r--r-- 1 root root 16384 Mar  1 19:49 secmod.db
[root@iZ ~]# systemctl start slapd
  1. 添加人员的时候出现下列错误:

    ldap_add: Invalid syntax (21)
            additional info: objectClass: value #0 invalid per syntax
    

    原因:

    • 编写ldif文件时要注意规范,每一行后面不能留有空格,否则就会出现错误,所以去掉每一之后的空格便可以了。

    • 没有导入相对应schema文件,导致没有相关属性!

      schema地址:

      /etc/openldap/schema
      

      添加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
      
  2. 执行命令:ldapmodify -H ldapi:/// -Y EXTERNAL -f acl.ldif 只要包含EXTERNAL关键字的 报错:

    ldap_sasl_interactive_bind_s: Authentication method not supported (7)
            additional info: SASL(-4): no mechanism available: Couldn't find mech EXTERNAL
    

    这种是因为引入了sasl2后,在sasl2配置了

    mech_list: plain
    pwcheck_method: saslauthd
    saslauthd_path: /var/run/saslauthd/mux
    

    mech_list 添加一个 EXTERNAL即可

    最终即:

    mech_list: plain EXTERNAL
    pwcheck_method: saslauthd
    saslauthd_path: /var/run/saslauthd/mux
    

常用命令

参考链接:OpenLDAP 管理命令总结

查询

# 查找目录树所有条目
ldapsearch -x -H ldap://localhost:1389 -b "dc=wlong,dc=com"
 
# 过滤查询
ldapsearch -x -b "dc=wlong,dc=com" 'uid=knight.zhou'
 
# 正则查询
ldapsearch -x -b "dc=wlong,dc=cn" "(|(uid=*tom*)(cn=*Ada Cather*))"

# 使用管理员查询
ldapsearch -x -H ldap://localhost:1389 -D cn=admin,dc=wlong,dc=com -w wl123456 -b "dc=wlong,dc=com" "cn=user02"

# 指定返回字段查询
ldapsearch -x -H ldap://localhost:1389 -D cn=admin,dc=wlong,dc=com -w wl123456 -b "dc=wlong,dc=com" "cn=user02" cn uid

添加

ldapadd -x -H ldap://127.0.0.1:1389 -w "wl123456" -D "cn=admin,dc=wlong,dc=com" -f user1.ldif

user1.ldif内容如下:

cat >> user1.ldif <<EOF
dn: uid=long.wang,ou=users,dc=wlong,dc=com
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: top
uid: long.wang
cn: long.wang
sn: wang
userpassword: {SASL}15712890761
givenname: long.wang
mail: long.wang@yingxiong.com
EOF

ldapwhoami

ldapwhoami -x -H ldap://127.0.0.1:1389 -D cn=admin,dc=wlong,dc=com -w wl123456
# 返回内容
dn:cn=admin,dc=wlong,dc=com

删除用户

连接:使用 ldapdelete 删除条目

使用 ldapmodify 删除条目

无法删除具有子条目的条目。LDAP 协议不允许出现子条目不再具有父条目的情形

ldapdelete -x -H ldap://127.0.0.1:1389 -D "cn=admin,dc=wlong,dc=com" -w wl123456 "uid=long.wang,ou=users,dc=wlong,dc=com"

如果想删除ou,并且ou下有子用户,请执行一下命令

ldapdelete -r <dn>

其中除了ldapdelete可以删除,ldapmodify也可以删除

使用 ldapmodify 实用程序时,还可以使用 changetype: delete 关键字删除条目

准备好文件:

dn: uid=bjensen,ou=People,dc=example,dc=com
changetype: delete

dn: ou=People,dc=example,dc=com
changetype: delete

然后执行

ldapmodify -x -H ldapi:/// -c -w "${LDAP_ADMIN_PWD}" -D "${LDAP_ADMIN_DN}" -f  /file.ldif

修改用户密码

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

创建文件updatepass.ldif

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

执行修改

ldapmodify -a -H ldap://127.0.0.1:1389 -D "cn=admin,dc=wlong,dc=com" -w wl123456 -f updatepass.ldif 

查询确认

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

可以确认密码修改成功了.

命令总结

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/21871.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】进程信号“疑问?坤叫算信号吗?“

鸡叫当然也算信号啦~ 文章目录 前言一、认识信号量二、信号的产生 1.调用系统函数向进程发信号2.由软件条件产生信号3.硬件异常产生信号总结 前言 信号在我们生活中很常见&#xff0c;下面我们举一举生活中信号的例子&#xff1a; 你在网上买了很多件商品&#xff0c;再等待不…

统计一个数的二进制中1的个数(三种方法)

那么好了好了&#xff0c;宝子们&#xff0c;今天给大家分享一篇经典例题的三种实现方法&#xff0c;来吧&#xff0c;开始整活&#xff01;⛳️ 一、基础法 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int number_of_one(int n) {int count 0;while(n){if…

VTKmimics Calculate Parts

前言&#xff1a;本博文主要研究mimics中Calculate Parts所采用的方法以及VTK中三维重建的方法&#xff0c;希望对各位小伙伴有所帮助&#xff0c;谢谢&#xff01; mimics-Calculate parts - Interpolation Gray Interpolation 灰度值插值是一种真正的3D插值&#xff0c;它考…

宝塔面板快速搭建贪吃蛇小游戏web网站 - 无需云服务器,网站发布上线

文章目录 前言视频教程1. 环境安装2. 安装cpolar内网穿透3. 内网穿透4. 固定http地址5. 配置二级子域名6. 创建一个测试页面&#x1f34e;总结 转载自远程内网穿透的文章&#xff1a;Linux使用宝塔面板搭建网站&#xff0c;并内网穿透实现公网访问 前言 宝塔面板作为简单好用的…

RoyalScope-总线工作状况和信号质量“体检”

海量存储、洞悉细微 无间断连续采样、波形和报文记录 内置100MHz示波器&#xff0c;采样率高达100MS/s&#xff0c;精确查看、分析和统计信号波形细节。 无间断连续采样总线上信号&#xff0c;全部无遗漏的解析成报文(包括数据帧、遥控帧、过载帧、错误帧、帧间隔和错误报文)和…

Vue3-黑马(十四)

目录&#xff1a; &#xff08;1&#xff09;vue3-进阶-router-令牌-前端路由 &#xff08;2&#xff09;vue3-进阶-router-令牌-前端路由 &#xff08;3&#xff09;vue3-进阶-pinia1 &#xff08;4&#xff09;vue3-进阶-pinia2 &#xff08;1&#xff09;vue3-进阶-rout…

ARM学习笔记_2 模式,寄存器,流水线

ARM arm体积小功耗低性能高&#xff0c;支持thumb ARM双指令集&#xff0c;兼容8/16位器件&#xff1b;大量使用寄存器&#xff0c;指令定长&#xff0c;寻址简单。 ARM是32位架构&#xff0c;Word 32bit&#xff0c; half Word 16bit. 模式 用户模式是用户程序的模式&#…

GPT-2(Transformer Decoder)的TensorFlow实现(附源码)

文章目录 一、GPT2实现步骤二、源码 一、GPT2实现步骤 机器学习模型的开发实现步骤一般都包含以下几个部分&#xff1a;   1. 遵照模型的网络架构&#xff0c;实现每一层&#xff08;Layer/Block&#xff09;的函数&#xff1b;   2. 将第1步中的函数组合在一起&#xff0c…

Typecho搭建和美化

概述 Typecho是一款轻量级的开源PHP博客系统&#xff0c;它简单易用&#xff0c;界面整洁&#xff0c;性能高效&#xff0c;主题、插件众多。我使用的是腾讯云轻量服务器&#xff0c;Typecho的应用模版&#xff0c;一键安装环境。构建自己的博客网站&#xff0c;记录生活、分享…

容器简单介绍

目录 一、容器简介​编辑 二、容器和虚拟化技术差异 三、容器基本概念 四、容器技术对企业优势 五、容器的工具 一、容器简介 docker只是容器工具&#xff0c;真正容器技术是LXC &#xff08;linux container&#xff09; 二、容器和虚拟化技术差异 虚拟机模式&#xff…

AI人工智能逻辑回归的原理、优缺点、应用场景和实现方法

逻辑回归&#xff08;Logistic Regression&#xff09;是一种常见的机器学习算法&#xff0c;它被广泛应用于分类问题。在人工智能&#xff08;Artificial Intelligence&#xff0c;简称AI&#xff09;领域中&#xff0c;逻辑回归是一种简单而有效的算法&#xff0c;可以用于许…

vue+elementui+nodejs高校校园在线打印预约系统

在线提交文档进行打印 首页简单介绍系统 语言 node.js 框架&#xff1a;Express 前端:Vue.js 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat 开发软件&#xff1a;VScode 顶部或主页按钮转到打印 用户可以登录 查看历史打印记录 用户分学生和非学生 学生可以享有优惠…

【C#】GridControl增加选择列(不用二次点击)

系列文章 【C#】单号生成器&#xff08;编号规则、固定字符、流水号、产生业务单号&#xff09; 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/129129787 【C#】日期范围生成器&#xff08;开始日期、结束日期&#xff09; 本文链接&#xff1a;h…

黑马Redis原理篇

黑马Redis原理篇 1、数据结构1.1、动态字符串SDS1.2、IntSet1.3、Dict1.4、ZipList1.5、QuickList1.6、SkipList1.7、RedisObject1.8、五种数据结构1. String&#xff08;小EMBSTR,大RAW (SDS),少量整数INT&#xff09;2. List&#xff08;Redis3.2之后使用QuickList实现&#…

electron 使用electron-packager打linux-x64包与linux-arm64包,解决打包缓慢问题

使用electron-packager打linux-x64包与linux-arm64包,解决下载zip打包缓慢问题 在使用electron-packager打包的过程中&#xff0c;需要在第一次下载electron版本对应的zip文件&#xff0c;下载很缓慢&#xff0c;而且还可能出现每次都在下载zip的情况 解决思路是提前下载好zip文…

【Jmeter第三章】Jmeter给请求添加请求头

给请求加上请求头最常见的场景就是在请求头上添加token了&#xff0c;这里也拿添加token来举例 1、添加某个请求的请求头 1、选中HTTP请求&#xff0c;右键添加 2、添加请求头 2、添加公共的请求头信息 其实步骤和上面是一样的&#xff0c;只不过是选择&#xff1a;线程组…

Midjourney提示词资源、使用技巧、艺术家资源网站收录

为了帮助艺术家们使用Midjourney更专业、准确地创作更完美的艺术作品&#xff0c;我们收录了一些Midjourney提示词资源分享、提示词书写技巧、相关专业工具&#xff0c;同时还有一些相关艺术家资源帮艺术家们找到创作灵感。有很多是社区内资深玩家分享的云文档&#xff0c;资源…

2023彩虹易支付最新原版安装教程(内附源码)

此源码已通过检查&#xff0c;确认无后门&#xff0c;且所有代码开源&#xff0c;无加密文件。 测试日期 2023年5月21日 源码已扫描无后门&#xff0c;不放心的也可以自己再去扫描一遍 2023年5月22日 各个功能接口测试完毕&#xff0c;均可用 选中下方可查看下载链接 http…

初识linux之网络基础概念

目录 一、网络发展 1. 独立模式 2. 网络互联 二、认识协议 1. 为什么要有协议 2. 什么是协议 三、网络协议初识 1. 协议分层 2. 协议分层的优点 3. 理解分层 4. OSI七层模型 4.1 概念 4.2 模型形式 4.3 各层的作用 5. TCP/IP五层&#xff08;或四层&#xff09…

23 KVM管理虚拟机-使用VNC密码登录虚拟机

文章目录 23 KVM管理虚拟机-使用VNC密码登录虚拟机23.1 概述23.2 前提条件23.3 操作步骤 23 KVM管理虚拟机-使用VNC密码登录虚拟机 本章介绍使用VNC密码登录虚拟机的方法。 23.1 概述 当虚拟机操作系统安装部署完成之后&#xff0c;用户可以通过VNC协议远程登录虚拟机&#…