網站首頁 編程語言 正文
1.Go連接LDAP服務
通過go操作的ldap,這里使用到的是go-ldap包,該包基本上實現了ldap v3的基本功能. 比如連接ldap服務、新增、刪除、修改用戶信息等,支持條件檢索的ldap庫中存儲的數據信息。
2.下載
go?get?github.com/go-ldap/ldap/v3 go?get?github.com/wxnacy/wgo/arrays
使用go-ldap包,可以在gopkg.in/ldap.v3@v3.1.0#section-readme查看說明文檔
3.準備LDAP環境
這里通過docker-compose
運行一個臨時的ldap實驗環境,
version:?"3" services: ??ldap: ????image:?osixia/openldap:latest ????container_name:?openldap ????hostname:?openldap ????restart:?always ????environment: ??????-?"LDAP_ORGANISATION=devopsman" ??????-?"LDAP_DOMAIN=devopsman.cn" ??????-?"LDAP_BASE_DN=dc=devopsman,dc=cn" ??????-?"LDAP_ADMIN_PASSWORD=admin123" ????ports: ??????-?389:389 ??????-?636:636
可以按需修改對應的環境變量信息.可以在hub.docker.com找到指定版本的鏡像信息. 現在創建一下openldap并且檢查一下服務的是否正常:
4.GO-LDAP案例實踐
創建用戶
在pkg.go.dev文檔中查看,有一個Add
方法可以完成創建用戶的操作,但是需要一個AddRequest
參數,而NewAddRequest
方法可以返回AddRequest
,于是按照此思路梳理一下。
首先要建立與openldap之間的連接,驗證賬號是否正常,同時此賬號要有創建的權限。
//?LoginBind??connection?ldap?server?and?binding?ldap?server func?LoginBind(ldapUser,?ldapPassword?string)?(*ldap.Conn,?error)?{ ?l,?err?:=?ldap.DialURL(ldapURL) ?if?err?!=?nil?{ ??return?nil,?err ?} ?_,?err?=?l.SimpleBind(&ldap.SimpleBindRequest{ ??Username:?fmt.Sprintf("cn=%s,dc=devopsman,dc=cn",?ldapUser), ??Password:?ldapPassword, ?}) ?if?err?!=?nil?{ ??fmt.Println("ldap?password?is?error:?",?ldap.LDAPResultInvalidCredentials) ??return?nil,?err ?} ?fmt.Println(ldapUser,"登錄成功") ?return?l,?nil }
其次,創建用戶,需要準備用戶的姓名、密碼、sn、uid、gid等信息,可以創建一個struct
結構
type?User?struct?{ ?username????string ?password????string ?telephone???string ?emailSuffix?string ?snUsername??string ?uid?????????string ?gid?????????string }
通過go-ldap包提供的NewAddRequest
方法,可以返回新增請求
func?(user?*User)?addUser(conn?*ldap.Conn)?error?{ ?ldaprow?:=?ldap.NewAddRequest(fmt.Sprintf("cn=%s,dc=devopsman,dc=cn",?user.username),?nil) ?ldaprow.Attribute("userPassword",?[]string{user.password}) ?ldaprow.Attribute("homeDirectory",?[]string{fmt.Sprintf("/home/%s",?user.username)}) ?ldaprow.Attribute("cn",?[]string{user.username}) ?ldaprow.Attribute("uid",?[]string{user.username}) ?ldaprow.Attribute("objectClass",?[]string{"shadowAccount",?"posixAccount",?"account"}) ?ldaprow.Attribute("uidNumber",?[]string{"2201"}) ?ldaprow.Attribute("gidNumber",?[]string{"2201"}) ?ldaprow.Attribute("loginShell",?[]string{"/bin/bash"}) ?if?err?:=?conn.Add(ldaprow);?err?!=?nil?{ ??return?err ?} ?return?nil }
最后,我們就可以通過實例化User
這個對象,完成用戶的創建了:
func?main()?{ ?con,?err?:=?LoginBind("admin",?"admin123") ?fmt.Println(con.IsClosing()) ?if?err?!=?nil?{ ??fmt.Println("V") ??fmt.Println(err) ?} ?var?user?User ?user.username="marionxue" ?user.password="admin123" ?user.snUsername="Marionxue" ?user.uid="1000" ?user.gid="1000" ?user.emailSuffix="@qq.com" ?if?err=user.addUser(con);err!=nil{ ??fmt.Println(err) ?} ?fmt.Println(user.username,"創建完成!") }
最后運行就可以創建用戶
... /private/var/folders/jl/9zk5nj316rlg_0svp07w6btc0000gn/T/GoLand/___go_build_github_com_marionxue_go30_tools_go_openldap admin登錄成功 marionxue?創建完成!
遍歷用戶
遍歷用戶依舊需要與openLDAP建立連接,因此我們復用LoginBind
函數,創建一個獲取賬號的函數GetEmployees
func?GetEmployees(con?*ldap.Conn)?([]string,?error)?{ ?var?employees?[]string ?sql?:=?ldap.NewSearchRequest("dc=devopsman,dc=cn", ??ldap.ScopeWholeSubtree, ??ldap.NeverDerefAliases, ??0, ??0, ??false, ??"(objectClass=*)", ??[]string{"dn",?"cn",?"objectClass"}, ??nil) ?cur,?err?:=?con.Search(sql) ?if?err?!=?nil?{ ??return?nil,?err ?} ?if?len(cur.Entries)?>?0?{ ??for?_,?item?:=?range?cur.Entries?{ ???cn?:=?item.GetAttributeValues("cn") ???for?_,?iCn?:=?range?cn?{ ????employees?=?append(employees,?strings.Split(iCn,?"[")[0]) ???} ??} ??return?employees,?nil ?} ?return?nil,?nil }
我們通過NewSearchRequest
檢索BaseDB
為dc=devopsman,dc=cn
下的賬號信息,最后將用戶名cn
打印出來
func?main()?{ ?con,?err?:=?LoginBind("admin",?"admin123") ?if?err?!=?nil?{ ??fmt.Println("V") ??fmt.Println(err) ?} ?employees,?err?:=?GetEmployees(con) ?if?err?!=?nil?{ ??fmt.Println(err) ?} ?for?_,?employe?:=?range?employees?{ ??fmt.Println(employe) ?} }
結果就是我們前面創建的一個用戶
marionxue
刪除賬號
同樣的思路,然后創建一個刪除方法delUser
//?delUser?刪除用戶 func?(user?*User)?delUser(conn?*ldap.Conn)?error{ ?ldaprow?:=?ldap.NewDelRequest(fmt.Sprintf("cn=%s,dc=devopsman,dc=cn",user.username),nil) ?if?err:=?conn.Del(ldaprow);err!=nil{ ??return?err ?} ?return?nil }
然后在main函數中調用
func?main()?{ ?con,?err?:=?LoginBind("admin",?"admin123") ?if?err?!=?nil?{ ??fmt.Println("V") ??fmt.Println(err) ?} ?employees,?err?:=?GetEmployees(con) ?if?err?!=?nil?{ ??fmt.Println(err) ?} ?var?user?User ?user.username="marionxue" ?if?err:=user.delUser(con);err!=nil{ ??fmt.Println("用戶刪除失敗") ?} ?fmt.Println(user.username,"用戶刪除成功!") }
運行結果:
admin登錄成功
marionxue 用戶刪除成功!
弱密碼檢查
默認情況下,在ldap中創建用戶,并沒有密碼復雜度的約束,因此對已存在ldap服務中使用弱密碼的賬號有什么好辦法能獲取出來嗎?ldap的賬號一旦創建,就看不到密碼了,如果用弱密碼字典模擬登錄的話,是否可行呢?
創建一個檢查密碼的函數CheckPassword
,通過逐行讀取弱密碼詞典的數據進行的模擬登錄,從而找到ldap中使用弱密碼的賬號:
func?CheckPassword(employe?string)?{ ?//?遍歷的弱密碼字典 ?f,?err?:=?os.Open("~/dict.txt") ?if?err?!=?nil?{ ??fmt.Println("reading?dict.txt?error:?",?err) ?} ?defer?f.Close() ?scanner?:=?bufio.NewScanner(f) ?for?scanner.Scan()?{ ??weakpassword?:=?scanner.Text() ??_,?err?:=?LoginBind(employe,?weakpassword) ??if?err?==?nil?{ ???fmt.Println(employe?+?"?使用的密碼為:?"?+?weakpassword) ??} ?} ?if?err?:=?scanner.Err();?err?!=?nil?{ ??fmt.Println(err) ?} ?fmt.Println(employe?+?"?check?have?aleardy?finished.?and?the?password?is?stronger?well.") }
結合前面說的遍歷賬號,拿到所有的賬號的信息,然后模擬登錄,如果命中了弱密碼字典中的密碼,就打印出來
func?main()?{ ?con,?err?:=?LoginBind("admin",?"admin123") ?if?err?!=?nil?{ ??fmt.Println("V") ??fmt.Println(err) ?} ?employees,?err?:=?GetEmployees(con) ?if?err?!=?nil?{ ??fmt.Println(err) ?} ?Whitelist?:=?[]string{"zhangsan","lisi"} ?for?_,?employe?:=?range?employees?{ ??fmt.Println("Starting?check:?",?employe) ??index?:=?arrays.ContainsString(Whitelist,?employe) ??if?index?==?-1?{ ???CheckPassword(employe) ??}?else?{ ???fmt.Println(employe?+?"?in?whitelist.?skiping...") ??} ??fmt.Println(employe) ?} }
但是這樣實際就是在攻擊自己的服務,這里就會產生兩個問題:
- 用戶越多,弱密碼字典里面的密碼越多,檢查的次數也就越多,耗時也就越長
- 每次模擬登錄,實際上就會創建一個連接,雖然連接認證失敗,但是也無疑加重服務器連接創建和銷毀的資源損耗,你有調優思路沒?
原文鏈接:https://mp.weixin.qq.com/s/KlMQcrocJxAuQQX9J8BwfA
相關推薦
- 2022-09-21 python裝飾器底層原理詳解_python
- 2022-08-05 基于redis+lua進行限流
- 2022-06-14 Python?torch.fft.rfft()函數用法示例代碼_python
- 2022-03-21 導出與導入Docker的容器實現示例_docker
- 2022-07-25 C++超詳細講解內存空間分配與this指針_C 語言
- 2022-03-24 shell腳本查看k8s日志介紹_linux shell
- 2022-11-01 golang連接MongoDB數據庫及數據庫操作指南_Golang
- 2022-06-12 C語言sizeof和strlen的指針和數組面試題詳解_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支