Golang'de LDAP Authentication, Bind ve Search
Rıdvan Tülemen
Posted on June 30, 2021
Windows Active Directory veya Samba hakkında bilginiz varsa, LDAP hakkında halihazırda bilginiz olabilir. Ama yoksa, Wikipedia'daki açıklama şu şekildedir.
Lightweight Directory Access Protocol veya kısaca LDAP (Türkçe: Basit İndeks Erişim Protokolü) TCP/IP üzerinde çalışan indeks servislerini sorgulama ve değiştirme amacıyla kullanılan uygulama katmanı protokolü.
Projeyi Oluşturmak
İlk olarak, kütüphaneyi indirmemiz gerekiyor:
go get github.com/go-ldap/ldap
Değişkenler
Artık kütüphaneyi kullanmaya başlayabiliriz. İlk olarak, daha sonra kullanacağımız değişkenleri oluşturuyoruz(Eğer anonymous bind kullanacaksanız sadece Filter değişkeni yeterlidir):
const (
BindUsername = "user@example.com"
BindPassword = "password"
FQDN = "DC.example.com"
BaseDN = "cn=Configuration,dc=example,dc=com"
Filter = "(objectClass=*)"
)
Connect
LDAP'a bağlanmak için, ldap.DialURL()
fonksiyonunu kullanacağız. Aşağıdaki fonksiyon sadece bağlantı için kullanılacak bir fonksiyondur:
// Ldap Connection without TLS
func Connect() (*ldap.Conn, error) {
// You can also use IP instead of FQDN
l, err := ldap.DialURL(fmt.Sprintf("ldap://%s:389", FQDN))
if err != nil {
return nil, err
}
return l, nil
}
TLS Connect
Eğer TLS Connection kullanmak istiyorsanız, aşağıdaki fonksiyonu kullanabilirsiniz:
// Ldap Connection with TLS
func ConnectTLS() (*ldap.Conn, error) {
// You can also use IP instead of FQDN
l, err := ldap.DialURL(fmt.Sprintf("ldaps://%s:636", FQDN))
if err != nil {
return nil, err
}
return l, nil
}
Anonymous Bind ve Search
Eğer anonymous bind kullanmak istiyorsanız, bu fonksiyonu kullanabilirsiniz:
// Anonymous Bind and Search
func AnonymousBindAndSearch(l *ldap.Conn) (*ldap.SearchResult, error) {
l.UnauthenticatedBind("")
anonReq := ldap.NewSearchRequest(
"",
ldap.ScopeBaseObject, // you can also use ldap.ScopeWholeSubtree
ldap.NeverDerefAliases,
0,
0,
false,
Filter,
[]string{},
nil,
)
result, err := l.Search(anonReq)
if err != nil {
return nil, fmt.Errorf("Anonymous Bind Search Error: %s", err)
}
if len(result.Entries) > 0 {
result.Entries[0].Print()
return result, nil
} else {
return nil, fmt.Errorf("Couldn't fetch anonymous bind search entries")
}
}
Bind ve Search
Onun yerine, normal bind kullanmak isterseniz:
// Normal Bind and Search
func BindAndSearch(l *ldap.Conn) (*ldap.SearchResult, error) {
l.Bind(BindUsername, BindPassword)
searchReq := ldap.NewSearchRequest(
BaseDN,
ldap.ScopeBaseObject, // you can also use ldap.ScopeWholeSubtree
ldap.NeverDerefAliases,
0,
0,
false,
Filter,
[]string{},
nil,
)
result, err := l.Search(searchReq)
if err != nil {
return nil, fmt.Errorf("Search Error: %s", err)
}
if len(result.Entries) > 0 {
return result, nil
} else {
return nil, fmt.Errorf("Couldn't fetch search entries")
}
}
Main Fonksiyon
Son olarak, bu fonksiyonları kullanabileceğimiz bir main fonksiyona ihtiyacımız var.
TLS Connection ile Bind ve Search
func main() {
// TLS Connection
l, err := ConnectTLS()
if err != nil {
log.Fatal(err)
}
defer l.Close()
// Normal Bind and Search
result, err = BindAndSearch(l)
if err != nil {
log.Fatal(err)
}
result.Entries[0].Print()
}
Non-TLS Connection ile Anonymous Bind ve Search
func main() {
// Non-TLS Connection
l, err := Connect()
if err != nil {
log.Fatal(err)
}
defer l.Close()
// Anonymous Bind and Search
result, err := AnonymousBindAndSearch(l)
if err != nil {
log.Fatal(err)
}
result.Entries[0].Print()
}
Son Olarak
Bu fonksiyonlar ile LDAP ile Authentication, bind ve search'ün anlaşılabildiğini düşünüyorum. Sonunda kod aşağıdaki gibi bir hale geliyor:
package main
import (
"fmt"
"log"
"github.com/go-ldap/ldap/v3"
)
const (
BindUsername = "user@example.com"
BindPassword = "password"
FQDN = "DC.example.com"
BaseDN = "cn=Configuration,dc=example,dc=com"
Filter = "(objectClass=*)"
)
func main() {
// TLS Connection
l, err := ConnectTLS()
if err != nil {
log.Fatal(err)
}
defer l.Close()
// Non-TLS Connection
//l, err := Connect()
//if err != nil {
// log.Fatal(err)
//}
//defer l.Close()
// Anonymous Bind and Search
result, err := AnonymousBindAndSearch(l)
if err != nil {
log.Fatal(err)
}
result.Entries[0].Print()
// Normal Bind and Search
result, err = BindAndSearch(l)
if err != nil {
log.Fatal(err)
}
result.Entries[0].Print()
}
// Ldap Connection with TLS
func ConnectTLS() (*ldap.Conn, error) {
// You can also use IP instead of FQDN
l, err := ldap.DialURL(fmt.Sprintf("ldaps://%s:636", FQDN))
if err != nil {
return nil, err
}
return l, nil
}
// Ldap Connection without TLS
func Connect() (*ldap.Conn, error) {
// You can also use IP instead of FQDN
l, err := ldap.DialURL(fmt.Sprintf("ldap://%s:389", FQDN))
if err != nil {
return nil, err
}
return l, nil
}
// Anonymous Bind and Search
func AnonymousBindAndSearch(l *ldap.Conn) (*ldap.SearchResult, error) {
l.UnauthenticatedBind("")
anonReq := ldap.NewSearchRequest(
"",
ldap.ScopeBaseObject, // you can also use ldap.ScopeWholeSubtree
ldap.NeverDerefAliases,
0,
0,
false,
Filter,
[]string{},
nil,
)
result, err := l.Search(anonReq)
if err != nil {
return nil, fmt.Errorf("Anonymous Bind Search Error: %s", err)
}
if len(result.Entries) > 0 {
result.Entries[0].Print()
return result, nil
} else {
return nil, fmt.Errorf("Couldn't fetch anonymous bind search entries")
}
}
// Normal Bind and Search
func BindAndSearch(l *ldap.Conn) (*ldap.SearchResult, error) {
l.Bind(BindUsername, BindPassword)
searchReq := ldap.NewSearchRequest(
BaseDN,
ldap.ScopeBaseObject, // you can also use ldap.ScopeWholeSubtree
ldap.NeverDerefAliases,
0,
0,
false,
Filter,
[]string{},
nil,
)
result, err := l.Search(searchReq)
if err != nil {
return nil, fmt.Errorf("Search Error: %s", err)
}
if len(result.Entries) > 0 {
return result, nil
} else {
return nil, fmt.Errorf("Couldn't fetch search entries")
}
}
Ayrıca, oluşturduğum gist'e de göz atabilirsiniz.
Okduğunuz için teşekkürler. Umarım yardımcı olabilmişimdir.
Posted on June 30, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.