金甌無缺江河一統|Win10系統基於Docker和Python3搭建並維護統一認證系統OpenLdap

劉悅的技術部落格發表於2020-11-24

原文轉載自「劉悅的技術部落格」https://v3u.cn/a_id_180

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屬性可能包含"admin@v3u.cn" 。一個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,如圖所示:

除此以外,OpenLDAP 還是一種典型的分散式結構,提供複製同步,可將主伺服器上的資料透過推或拉的機制實現在從伺服器上更新,完成資料的同步,從而避免OpenLDAP 伺服器出現單點故障,實現了高可用架構。

OpenLdap目錄層級結構是一個專門為搜尋和瀏覽而設計的資料庫,雖然也支援簡單的插入、刪除、修改功能。但是我們可以理解為它是為瀏覽和搜尋而生的,它的查詢速度很快,相反插入速度較慢,和關係型資料庫相比,它並不支援事務和回滾以及複雜的插入、更新等連貫操作功能,這一點和Elasticsearch有幾分相似,但是,古人云:“射不主皮,力不同科”,如果您的系統擴容頻繁,下游應用層出不窮,那麼您就可以考慮用它來做統一使用者管理,為您的應用保駕護航。

原文轉載自「劉悅的技術部落格」 https://v3u.cn/a_id_180

相關文章