1Go连接LDAP服务,通过go操作ldap。这里使用了go-ldap[1]包,基本实现了ldapv3的基本功能。例如连接ldap服务,增加、删除、修改用户信息等,支持条件检索存储在ldap库中的数据信息。2下载gogetgithub.com/go-ldap/ldap/v3gogetgithub.com/wxnacy/wgo/arrays使用go-ldap包,可以查看文档gopkg.in/ldap.v3@v3.1.0#section-readme[2]3准备LDAP环境这里通过docker-compose运行一个临时的ldap实验环境,version:"3"services:ldap:image:osixia/openldap:latestcontainer_name:openldaphostname:openldaprestart:alwaysenvironment:-"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[3]找到指定版本的镜像信息。现在创建openldap,查看服务是否正常:4GO-LDAP创建用户案例实践查看pkg.go.dev文档,有一个Add方法可以完成创建用户的操作,但是需要一个AddRequest参数,而NewAddRequest方法可以返回AddRequest,所以我按照这个思路整理了一下。首先与openldap建立连接,验证账号是否正常,同时这个账号必须有创建权限。//LoginBindconnectionldapserverandbindingldapserverfuncLoginBind(ldapUser,ldapPasswordstring)(*ldap.Conn,error){l,err:=ldap.DialURL(ldapURL)iferr!=nil{returnnil,err}_,err=l.SimpleBind(&ldap.SimpleBindRequest{用户名:fmt.Sprintf("cn=%s,dc=devopsman,dc=cn",ldapUser),密码:ldapPassword,})iferr!=nil{fmt.Println("ldappasswordiserror:",ldap.LDAPResultInvalidCredentials)returnnil,err}fmt.Println(ldapUser,"登录成功")returnl,nil}其次,创建用户需要准备用户名、密码、sn、uid、gid等信息,可以创建一个struct结构体typeUserstruct{usernamestringpasswordstringtelephonestringemailSuffixstringsnUsernamestringuidstringgidstring}通过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("homeDi教区长",[]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"})iferr:=conn.Add(ldaprow);err!=nil{returnerr}returnil}最后我们可以实例化User对象来完成用户创建:funcmain(){con,err:=LoginBind("admin","admin123")fmt.Println(con.IsClosing())iferr!=nil{fmt.Println("V")fmt.Println(err)}varuserUseruser。username="marionxue"user.password="admin123"user.snUsername="Marionxue"user.uid="1000"user.gid="1000"user.emailSuffix="@qq.com"iferr=user.addUser(con);err!=nil{fmt.Println(err)}fmt.Println(user.username,"Creationcompleted!")}终于运行创建用户了.../private/var/folders/jl/9zk5nj316rlg_0svp07w6btc0000gn/T/GoLand/___go_build_github_com_marionxue_go30_tools_go_openldapadmin登录成功marionxue创建成功!遍历用户还是需要和openLDAP建立连接,所以我们复用LoginBind函数创建函数GetEmployeesfuncGetEmployees(con*ldap.Conn)([]string,error){varemployees[]stringsql:=ldap.NewSearchRequest("dc=devopsman,dc=cn",ldap.ScopeWholeSubtree,ldap.NeverDerefAliases,0,0,false,"(objectClass=*)",[]string{"dn","cn","objectClass"},nil)cur,错误:=con.Search(sql)iferr!=nil{returnnil,err}iflen(cur.Entries)>0{for_,item:=rangecur.Entries{cn:=item.GetAttributeValues("cn")for_,iCn:=rangecn{employees=append(employees,strings.Split(iCn,"[")[0])}}returnemployees,nil}returnnil,nil}我们通过检索dc=devopsman,dc=cn下BaseDB的账户信息NewSearchRequest,最后把用户名cn打印出来funcmain(){con,err:=LoginBind("admin","admin123")iferr!=nil{fmt.Println("V")Println(err)}employees,err:=GetEmployees(con)iferr!=nil{fmt.Println(err)}for_,employe:=rangeemployees{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)iferr:=conn.Del(ldaprow);err!=nil{returnerr}returnnil}然后调用funcmain(){con,err:=在主函数中LoginBind("admin","admin123")iferr!=nil{fmt.Println("V")fmt.Println(err)}employees,err:=GetEmployees(con)iferr!=nil{fmt.Println(err)}varuserUseruser.username="marionxue"iferr:=user.delUser(con);err!=nil{fmt.Println("用户删除失败")}fmt.Println(user.username,"用户删除成功!”)}运行结果:admin登录成功Marionxue用户删除成功!弱口令检查默认情况下,用户是在ldap中创建的,没有口令复杂度约束,那么在现有的ldap服务中,弱口令的账户最好通过什么方式获取出来呢?创建ldap帐户后,密码将不可见。用弱口令字典模拟登录可行吗?创建一个函数CheckPassword来检查密码,通过逐行读取弱密码字典的数据来模拟登录在ldap中查找弱密码的账户:funcCheckPassword(employesting){//遍历弱口令字典f,err:=os.Open("~/dict.txt")iferr!=nil{fmt.Println("readingdict.txterror:",err)}deferf.Close()scanner:=bufio.NewScanner(f)forscanner.Scan(){weakpassword:=scanner.Text()_,err:=LoginBind(employe,weakpassword)iferr==nil{fmt.Println(employe+"使用的密码是:"+weakpassword)}}iferr:=scanner.Err();err!=nil{fmt.Println(err)}fmt.Println(employe+"checkhavealeardyfinished.andthepasswordisstrongerwell.")}结合上面提到的遍历账号,得到All账号信息,然后模拟登录,如果弱密码字典里的密码被命中,会打印出funcmain(){con,err:=LoginBind("admin","admin123")iferr!=nil{fmt.println("V")fmt.Println(err)}employees,err:=GetEmployees(con)iferr!=nil{fmt.Println(err)}Whitelist:=[]string{"zhangsan","lisi"}for_,employee:=rangeemployees{fmt.Println("Startingcheck:",employe)index:=arrays.ContainsString(Whitelist,employe)ifindex==-1{CheckPassword(employe)}else{fmt.Println(employe+"inwhitelist.skipping...“)}Fmt.Println(employe)}}但这实际上是在攻击自己的服务,而且这里会出现两个问题:用户越多,弱密码字典中的密码越多,检查的次数越多,耗时越长时间越长,每次模拟登录都会实际创建一个连接。虽然连接认证失败,但无疑会增加服务器连接创建和销毁的资源消耗。你有什么调整的想法吗?参考[1]go-ldap:https://github.com/go-ldap/ldap[2]go-ldapv3@3.1.0:https://pkg.go.dev/gopkg.in/ldap.v3@v3.1.0#section-readme[3]osixia/openldap镜像仓库:https://hub.docker.com/r/osixia/openldap/tags
