7

金瓯无缺江河一统|Win10系统基于Docker和Python3搭建并维护统一认证系统OpenLdap

 3 years ago
source link: https://v3u.cn/a_id_180
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

金瓯无缺江河一统|Win10系统基于Docker和Python3搭建并维护统一认证系统OpenLdap

首页 - Python /2020-11-24
金瓯无缺江河一统|Win10系统基于Docker和Python3搭建并维护统一认证系统OpenLdap

    OpenLdap(Lightweight Directory Access Protocol)是什么?它其实是一个开源的、具备工业标准特性的应用协议,可以使用TCP协议提供访问控制和维护分布式信息的目录信息。这是一个传统意义上的书面解释,是的,毫无疑问,你会一脸懵逼。好吧,让我们变得感性一点,假如我每天早上使用Twitter想听听懂王又吹了什么牛,登录Twitter账号密码,紧接着又想上Instagram看看女神又post了什么新靓照,好的,登录Instagram账号密码,摸了一上午的鱼之后,突然想起来要登录公司的邮箱,看看有没有新需求,是的,又需要那该死的账号和密码,甚至于查询社保、公积金提取、交罚款都需要各自系统的账号和密码。想象一下,如果有一套系统可以统一管理和维护所有下游应用的账号和权限,我们不需要花时间重复的注册新应用的账号,而只需要关注应用本身,从而实现账号集中认证管理,此时作为账号管理员的我们只须维护OpenLDAP 服务器条目即可,金瓯无缺江山一统,这就是openladp能够带给我们的好处。

    LDAP是非常典型的层级结构,信息模型是建立在属性条目(entries)的基础上。一个属性条目是一些属性的集合,并且具有一个全局唯一的"可区分名称"DN,一个条目可以通过DN来引用。每一个条目的属性具有一个类型和一个或者多个值。类型通常是容易记忆的名称,比如"cn"是通用名称(common name) ,或者"mail"是电子邮件地址。条目的值的语法取决于属性类型。比如,cn属性可能具有一个值"jack joe" 。一个mail属性可能包含"[email protected]" 。一个pngphoto属性可能包含一幅PNG(二进制)格式的图片。

    这里简单介绍一下openldap常用的层级关键字的解释:

    dc:Domain Component 域名的范围,其格式是将完整的域名分成几部分,如域名为v3u.cn则写成dc=v3u,dc=cn。

    uid:User Id 用户ID,比如自增长“1”。

    ou:Organization Unit 组织单位,类似于文件系统中的子目录,它是一个容器对象,组织单位可以包含其他各种对象(包括其他组织单元),如“newgroup”。
    cn:Common Name 公共名称,如“jack joe”。
    sn: Surname 姓,如“joe”。
    dn :Distinguished Name 惟一辨别名,类似于文件系统中的绝对路径,每个对象都有一个惟一的名称,类似于mysql的全局唯一索引,如“uid= tom,ou=market,dc=example,dc=com”,记住在一个目录树中DN总是惟一的。

    理解了概念,让我们来实操一把,因为实践永远是检验真理的唯一标准,首先安装Docker,参照:win10系统下把玩折腾DockerToolBox以及更换国内镜像源(各种神坑)

    随后拉取openldap镜像:

docker pull osixia/openldap:1.3.0

    这里我们使用1.3稳定版,拉取成功后查看本地镜像

docker images

    可以看到只有200mb左右,非常小巧:

liuyue:~ liuyue$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
osixia/openldap 1.3.0 faac9bb59f83 6 months ago 260MB

    启动容器:

docker run -p 389:389 --name myopenldap --network bridge --hostname openldap-host --env LDAP_ORGANISATION="v3u" --env LDAP_DOMAIN="v3u.cn" --env LDAP_ADMIN_PASSWORD="admin" --detach osixia/openldap:1.3.0

    这里我们通过端口映射将389端口作为链接桥梁,同时配置LDAP组织者:--env LDAP_ORGANISATION="v3u",配置LDAP域:--env LDAP_DOMAIN="v3u.cn",配置LDAP密码:--env LDAP_ADMIN_PASSWORD="admin",默认登录用户名:admin,并且开启后台守护进程。

    查看容器运行状态:

docker ps

    可以看到已经在后台启动了:

liuyue:~ liuyue$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b62d1f66c2b8 osixia/openldap:1.3.0 "/container/tool/run" 2 days ago Up 2 days 0.0.0.0:389->389/tcp, 636/tcp myopenldap
liuyue:~ liuyue$

    服务确认没问题之后,我们通过python来进行逻辑的编写,首先安装依赖

pip3 install ldap3

    随后编写测试脚本 test_ldap.py ,首先测试一下链接ldap服务器:

from ldap3 import Server, Connection, ALL,MODIFY_REPLACE

s = Server('localhost', get_info=ALL)

c = Connection(s, user='cn=admin,dc=v3u,dc=cn', password='admin')
c.bind()

print(c.extend.standard.who_am_i())

    这里的localhost是docker容器的ip,同时使用账号admin登录,注意账号(cn)以及域(dc)不要写错,不出意外的话,系统会返回当前验证的用户信息:

liuyue:mytornado liuyue$ python3 "/Users/liuyue/wodfan/work/mytornado/test_ldap.py"
dn:cn=admin,dc=v3u,dc=cn
liuyue:mytornado liuyue$

    初始状态下,LDAP是一个空目录,即没有任何数据。可通过程序代码向目录数据库中添加数据,也可使用ldap3库的ldapadd命令来完成添加数据的操作,该命令可将一个LDIF文件中的条目添加到目录:

    这里我们来添加一个OU,也就是组织(OrganizationalUnit)。

#添加组织

res = c.add('OU=v3u_users,dc=v3u,dc=cn', object_class='OrganizationalUnit')
print(res)
print(c.result)

    可以看到添加成功:

liuyue:mytornado liuyue$ python3 "/Users/liuyue/wodfan/work/mytornado/test_ldap.py"
True
{'result': 0, 'description': 'success', 'dn': '', 'message': '', 'referrals': None, 'type': 'addResponse'}

    随后可以为该组织添加一个群组(group):

# 添加群组
ldap_attr = {}
ldap_attr['objectClass'] = ['top', 'posixGroup']
ldap_attr['gidNumber'] = '1'

c.add('cn=mygroup,dc=v3u,dc=cn',attributes=ldap_attr)
print(c.result)
liuyue:mytornado liuyue$ python3 "/Users/liuyue/wodfan/work/mytornado/test_ldap.py"
{'result': 0, 'description': 'success', 'dn': '', 'message': '', 'referrals': None, 'type': 'addResponse'}
liuyue:mytornado liuyue$

    紧接着就是添加人员了:

#添加用户
ldap_attr = {}
ldap_attr['cn'] = "test user1"
ldap_attr['sn'] = "测试"
ldap_attr['userPassword'] = "1234"

user_dn = "cn=testuser1,cn=mygroup,dc=v3u,dc=cn"

c.add(dn=user_dn,object_class='inetOrgPerson',attributes=ldap_attr)
print(c.result)

    这里的cn可以理解为用户名,sn为姓,userPassword顾名思义就是该用户的密码,dn则是该用户在系统中的唯一标识,注意指定刚刚建立的群组mygroup,返回:

liuyue:mytornado liuyue$ python3 "/Users/liuyue/wodfan/work/mytornado/test_ldap.py"
{'result': 0, 'description': 'success', 'dn': '', 'message': '', 'referrals': None, 'type': 'addResponse'}
liuyue:mytornado liuyue$

    此时,我们可以查询一下刚刚建立好的用户:

print(c.search("dc=v3u,dc=cn", '(&(cn=testuser1))', attributes=['*']))
print(c.entries)

    就可以看到用户的具体信息:

liuyue:mytornado liuyue$ python3 "/Users/liuyue/wodfan/work/mytornado/test_ldap.py"
True
[DN: cn=testuser1,cn=mygroup,dc=v3u,dc=cn - STATUS: Read - READ TIME: 2020-11-23T17:58:08.569044
cn: test user1
testuser1
objectClass: inetOrgPerson
sn: 测试
userPassword: b'1234'
]
liuyue:mytornado liuyue$

    如果我们要修改用户信息,可以使用modify方法:

#修改用户
c.modify('cn=testuser1,cn=mygroup,dc=v3u,dc=cn',{'uid':[(MODIFY_REPLACE, ['1'])]})
print(c.result)

    这里修改用户的uid属性,返回:

liuyue:mytornado liuyue$ python3 "/Users/liuyue/wodfan/work/mytornado/test_ldap.py"
{'result': 0, 'description': 'success', 'dn': '', 'message': '', 'referrals': None, 'type': 'modifyResponse'}
liuyue:mytornado liuyue$

    再次搜索该用户:

print(c.search("dc=v3u,dc=cn", '(&(cn=testuser1))', attributes=['*']))
print(c.entries)

    可以看到uid已经被添加好了:

liuyue:mytornado liuyue$ python3 "/Users/liuyue/wodfan/work/mytornado/test_ldap.py"
True
[DN: cn=testuser1,cn=mygroup,dc=v3u,dc=cn - STATUS: Read - READ TIME: 2020-11-23T18:02:47.080555
cn: test user1
testuser1
objectClass: inetOrgPerson
sn: 测试
uid: 1
userPassword: b'1234'
]

    最后,如果员工离职的话,公司内所有账号和权限应该被回收,所以进行删除操作:

#删除用户
c.delete(dn='cn=testuser1,cn=mygroup,dc=v3u,dc=cn')
print(c.result)
{'result': 0, 'description': 'success', 'dn': '', 'message': '', 'referrals': None, 'type': 'delResponse'}

    再次查询已经获取不到记录:

print(c.search("dc=v3u,dc=cn", '(&(cn=testuser1))', attributes=['*']))
print(c.entries)

liuyue:mytornado liuyue$ python3 "/Users/liuyue/wodfan/work/mytornado/test_ldap.py"
False
[]

      至此,我们就基于openldap的树形结构将组织以及用户信息分别进行存储和CURD(增删改查)操作,在树的root(根)一般定义总域(c=v3u)或者域名后缀(dc=cn),其次往往定义一个或多个组织(organization,o)或组织单元(organization unit,ou)。一个组织单元可以包含人员、设备信息(服务器、电脑等)相关信息。例如uid=testuser1,ou=v3u_users,dc=v3u,dc=cn,如图所示:

20201124101107_90106.png

    除此以外,OpenLDAP 还是一种典型的分布式结构,提供复制同步,可将主服务器上的数据通过推或拉的机制实现在从服务器上更新,完成数据的同步,从而避免OpenLDAP 服务器出现单点故障,实现了高可用架构。

    OpenLdap目录层级结构是一个专门为搜索和浏览而设计的数据库,虽然也支持简单的插入、删除、修改功能。但是我们可以理解为它是为浏览和搜索而生的,它的查询速度很快,相反插入速度较慢,和关系型数据库相比,它并不支持事务和回滚以及复杂的插入、更新等连贯操作功能,这一点和Elasticsearch有几分相似,但是,古人云:“射不主皮,力不同科”,如果您的系统扩容频繁,下游应用层出不穷,那么您就可以考虑用它来做统一用户管理,为您的应用保驾护航。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK