From a98556d5c1241f9150202d72ea096a775d97a582 Mon Sep 17 00:00:00 2001 From: MrArmonius Date: Wed, 7 Jul 2021 01:49:33 +0200 Subject: Test End-to-end V1.0, testing Bottin's behavior Tests wrote in golang without framework Testing on the Bottin's behavior Tests made: - crated random Users and Group - LDAP ADD - check the match between Consul's data and Test's data- LDAP Search - modify attributes and check them - LDAP Modify --- .drone.yml | 25 +- .gitignore | 1 + go.mod | 1 + go.sum | 14 ++ main.go | 17 +- test_automatic/Scan_Bad_Packets.pcapng | Bin 0 -> 646080 bytes test_automatic/Scan_Good_Packets.pcapng | Bin 0 -> 1303556 bytes test_automatic/go.mod | 8 + test_automatic/go.sum | 23 ++ test_automatic/integration.go | 430 ++++++++++++++++++++++++++++++++ test_automatic/rapport_beug_bottin.txt | 40 +++ test_automatic/start_test.sh | 11 + 12 files changed, 558 insertions(+), 12 deletions(-) create mode 100644 test_automatic/Scan_Bad_Packets.pcapng create mode 100644 test_automatic/Scan_Good_Packets.pcapng create mode 100644 test_automatic/go.mod create mode 100644 test_automatic/go.sum create mode 100644 test_automatic/integration.go create mode 100644 test_automatic/rapport_beug_bottin.txt create mode 100755 test_automatic/start_test.sh diff --git a/.drone.yml b/.drone.yml index 9eea880..b83a87d 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,13 +1,24 @@ --- -pipeline: - build: - image: golang:stretch - commands: - - go get -d -v - - go build -v +kind: pipeline +name: bottin + +steps: +- name: build + image: golang:stretch + commands: + - go get -d -v + - go build -v + - cd test_automatic + - go get -d -v + - go build -v + +- name: test_bottin + image: consul:latest + commands: + - ./test_automatic/start_test.sh --- kind: signature -hmac: 8f49fdf0e4abb0790827eed7cac8eedd5e11705d1fa01954a84929933eb7b254 +hmac: 939fca00ff84d40e9364cd936c18c40c5becafa05e0f887bc04cf6336a4913a2 ... diff --git a/.gitignore b/.gitignore index d739b6f..9edb3e5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ bottin bottin.static config.json +test_automatic/integration diff --git a/go.mod b/go.mod index 75156e0..22c2023 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module bottin go 1.13 require ( + github.com/go-ldap/ldap/v3 v3.3.0 github.com/google/uuid v1.1.1 github.com/hashicorp/consul/api v1.3.0 github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 diff --git a/go.sum b/go.sum index c2db0e1..e4825c4 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28= +github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -7,6 +9,11 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8= +github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-ldap/ldap v2.5.1+incompatible h1:Opaoft5zMW8IU/VRULB0eGMBQ9P5buRvCW6sFTRmMn8= +github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ= +github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= @@ -79,12 +86,19 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3 h1:KYQXGkl6vs02hK7pK4eIbw0NpNPedieTSTEiJ//bwGs= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM= +golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsqD5/3mLDhx2NcNqyW+0= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/main.go b/main.go index 13d3da6..91c3bbd 100644 --- a/main.go +++ b/main.go @@ -320,12 +320,19 @@ func (server *Server) init() error { return err } - admin_pass := make([]byte, 8) - _, err = rand.Read(admin_pass) - if err != nil { - return err + + admin_pass_str, environnement_variable_exist := os.LookupEnv("BOTTIN_DEFAULT_ADMIN_PW") + if !environnement_variable_exist { + admin_pass := make([]byte, 8) + _, err = rand.Read(admin_pass) + if err != nil { + return err + } + admin_pass_str = base64.RawURLEncoding.EncodeToString(admin_pass) + } else { + server.logger.Printf("It seems that exists a password in environnement variable") } - admin_pass_str := base64.RawURLEncoding.EncodeToString(admin_pass) + admin_pass_hash := SSHAEncode([]byte(admin_pass_str)) admin_dn := "cn=admin," + server.config.Suffix diff --git a/test_automatic/Scan_Bad_Packets.pcapng b/test_automatic/Scan_Bad_Packets.pcapng new file mode 100644 index 0000000..cc209f5 Binary files /dev/null and b/test_automatic/Scan_Bad_Packets.pcapng differ diff --git a/test_automatic/Scan_Good_Packets.pcapng b/test_automatic/Scan_Good_Packets.pcapng new file mode 100644 index 0000000..5c87f82 Binary files /dev/null and b/test_automatic/Scan_Good_Packets.pcapng differ diff --git a/test_automatic/go.mod b/test_automatic/go.mod new file mode 100644 index 0000000..74ed1ce --- /dev/null +++ b/test_automatic/go.mod @@ -0,0 +1,8 @@ +module bottin/integration + +go 1.14 + +require ( + github.com/go-ldap/ldap/v3 v3.3.0 + github.com/sirupsen/logrus v1.4.2 +) diff --git a/test_automatic/go.sum b/test_automatic/go.sum new file mode 100644 index 0000000..8e94420 --- /dev/null +++ b/test_automatic/go.sum @@ -0,0 +1,23 @@ +github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28= +github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8= +github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ= +github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM= +golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/test_automatic/integration.go b/test_automatic/integration.go new file mode 100644 index 0000000..e5a4737 --- /dev/null +++ b/test_automatic/integration.go @@ -0,0 +1,430 @@ +package main + +import ( + "github.com/go-ldap/ldap/v3" + "fmt" + log "github.com/sirupsen/logrus" + "math/rand" + "strings" + "errors" + "os" +) + + +const bindusername = "cn=admin,dc=deuxfleurs,dc=fr" +const adresse = "127.0.0.1" +const port = 1389 +var bindpassword string + +var all_names = make(map[string]struct{}) + + + +func printError(LDAPError error) { + if LDAPError != nil { + log.Fatal(LDAPError) + } +} + +func createOU(l *ldap.Conn) error { + + req := ldap.NewAddRequest("ou=groups,dc=deuxfleurs,dc=fr",nil) + req.Attribute("description",[]string{"OrganizationalUnit qui regroupe tous les groupes"}) + req.Attribute("objectclass",[]string{"organizationalUnit", "top"}) + req.Attribute("ou",[]string{"groups"}) + req.Attribute("structuralobjectclass", []string{"organizationalUnit"}) + + err := l.Add(req) + if err != nil { + return err + } + + req = ldap.NewAddRequest("ou=users,dc=deuxfleurs,dc=fr",nil) + req.Attribute("description",[]string{"OrganizationalUnit qui regroupe tous les utilisateurs"}) + req.Attribute("objectclass",[]string{"organizationalUnit", "top"}) + req.Attribute("ou",[]string{"users"}) + req.Attribute("structuralobjectclass", []string{"organizationalUnit"}) + + err = l.Add(req) + return err +} + +func generateName(r *rand.Rand) (name string) { + for only_one := true; only_one; _, only_one = all_names[name]{ + name = fmt.Sprintf("%d",r.Int()) + } + all_names[name] = struct{}{} + log.Debug(fmt.Sprintf("Name generated: %s.\n", name)) + return +} + +func createGroup(r *rand.Rand, l *ldap.Conn) (tab_AddRequest []ldap.AddRequest, err error) { + StructuralObjectClass := []string{"groupOfNames"} + ObjectClass := []string{"groupOfNames","top"} + + + + for i := 0; i<20; i++ { + //Generate name and check if he is unique + name := generateName(r) + + req := ldap.NewAddRequest(fmt.Sprintf("cn=%s,ou=groups,dc=deuxfleurs,dc=fr",name),nil) + req.Attribute("description",[]string{generateName(r)}) + req.Attribute("objectclass",ObjectClass) + req.Attribute("structuralobjectclass",StructuralObjectClass) + + err = l.Add(req) + if err != nil { + log.Warn(fmt.Sprintf("Erreur survenue sur la création du [%d] groupe.\n",i)) + return nil, err + } + tab_AddRequest = append(tab_AddRequest, *req) + + } + return +} + +func createUser(r *rand.Rand, l *ldap.Conn) (tab_AddRequest []ldap.AddRequest, err error) { + StructuralObjectClass := []string{"inetOrgPerson"} + ObjectClass := []string{"inetOrgPerson","organizationalPerson","person","top"} + + for i := 0; i<20; i++ { + name := generateName(r) + + req := ldap.NewAddRequest(fmt.Sprintf("cn=%s,ou=users,dc=deuxfleurs,dc=fr",name),nil) + req.Attribute("displayname",[]string{generateName(r)}) + req.Attribute("objectclass",ObjectClass) + req.Attribute("structuralobjectclass",StructuralObjectClass) + + err = l.Add(req) + if err != nil { + log.Warn(fmt.Sprintf("Erreur survenue sur la création du [%d] user.\n",i)) + return nil, err + } + tab_AddRequest = append(tab_AddRequest, *req) + + } + return +} + +func search_attributes(tab_Attributes []ldap.Attribute, tipe string) (*ldap.Attribute) { + for _,att := range tab_Attributes { + if att.Type == tipe { + return &att + } + } + return nil +// return ldap.Attribute{} +} + +func test_attributes(l *ldap.Conn, tab_AddRequest []ldap.AddRequest, filter_objectclass, user_or_group string) (err error) { + for _, addRequest := range tab_AddRequest { + + //On prend le cn en supposant qu'il est unique + cn := strings.Split(addRequest.DN,",")[0] + + //On crée la requête pour la recherche + search_req := ldap.NewSearchRequest( + fmt.Sprintf("ou=%s,dc=deuxfleurs,dc=fr",user_or_group), + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + fmt.Sprintf("(&(objectclass=%s)(%s))", filter_objectclass,cn), + []string{"displayname","objectclass","structuralobjectclass"}, + nil, + ) + + //On lance la recherche + result, err := l.Search(search_req) + if err != nil { + return err + } + if len(result.Entries) != 1 { + return errors.New("Test a trouvé plusieurs displaynames en commun ou en a trouvé aucun") + } + + //On compare les attributs qu'on a reçu avec les attributs qu'on a envoyé + result_attributes := result.Entries[0].Attributes + log.Debug(fmt.Sprintf("La longueur est de %d, contient : \n %s.\n",len(result_attributes), result_attributes)) + + //Notre recherche crée un attribut par valeur, même si les valeurs viennent du même nom d'attribut + //Par exemple: objectclass possède 4 valeurs. Alors on aura 4 EntryAttribute qui contient chacune une des 4 valeurs de l'attribut objectclass + + //j est l'indice qui représente la j-ème valeur de notre attribut + var j int + var att *ldap.Attribute + for i,attributes := range result_attributes { + //On cherche l'attribut de l'user i qui a le même nom que celui qu'on a reçu et qu'on traite dans cette boucle + if j == 0 { + att = search_attributes(addRequest.Attributes, attributes.Name) + if att == nil { + return errors.New(fmt.Sprintf("Error: test_attributes - Don't find match name attributes. We search %s.\n", attributes.Name)) + } + } + log.Debug(fmt.Sprintf("Le nom de l'attribut est %s, sa valeur est: \n %s.", att.Type, att)) + + if j >= len(att.Vals) || att.Vals[j] != attributes.Values[0] { + return errors.New(fmt.Sprintf("Error: test_attributes - Theses values aren't the same: %d, %d",att.Vals, attributes.Values)) + } + + if i+1 < len(result_attributes) && result_attributes[i+1].Name == attributes.Name { + j += 1 + } else { j = 0} + } + + } + return nil +} + +func clean(l *ldap.Conn, AddReq_users, AddReq_groups []ldap.AddRequest,user, group bool) (err error){ + log.Debug("Debut clean") + if(user) { + for _,req := range AddReq_users { + delReq := ldap.NewDelRequest(req.DN,nil) + err = l.Del(delReq) + if err != nil { + return + } + } + } + if group { + for _,req := range AddReq_groups { + delReq := ldap.NewDelRequest(req.DN, nil) + err = l.Del(delReq) + if err != nil { + return + } + } + } + defer log.Debug("Fin clean") + return +} + +func test_modify_attributes(l *ldap.Conn, r *rand.Rand, tab_AddReq []ldap.AddRequest, tab_type_name []string) (err error) { + for _, AddReq := range tab_AddReq { + modReq := ldap.NewModifyRequest(AddReq.DN,nil) + for _, type_name := range tab_type_name { + newName := generateName(r) + modReq.Replace(type_name, []string{newName}) + att := search_attributes(AddReq.Attributes, type_name) + att.Vals[0] = newName + } + err = l.Modify(modReq) + if err != nil { + return + } + + } + return +} + +func add_user_in_groups(l *ldap.Conn, r *rand.Rand, users, groups []ldap.AddRequest) (err error) { + for _,group := range groups { + numberUsers := r.Intn(19) + 1 //Always a minimum of 1 user + list_users := []string{} + for i:=0; i < numberUsers; i++ { + list_users = append(list_users, users[i].DN) + } + modifyReq := ldap.NewModifyRequest( group.DN, nil) + modifyReq.Add("member", list_users) + + err = l.Modify(modifyReq) + if err != nil { + log.Warn(fmt.Sprintf("Error: ModifyReq failed, func:add_users_in_groups from group:\n %d",group)) + return + } + } + return +} + +func delete_groups(l *ldap.Conn, groups []ldap.AddRequest) (list map[string][]string ,err error) { + list = make(map[string][]string) + for _, group := range groups { + //Get lists_users + cn := strings.Split(group.DN,",")[0] + search_req := ldap.NewSearchRequest( + "ou=groups,dc=deuxfleurs,dc=fr", + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + fmt.Sprintf("(&(objectclass=groupOfNames)(%s))",cn), + []string{"member"}, + nil, + ) + res , err := l.Search(search_req) + if err != nil { + log.Warn(fmt.Sprintf("Error Search: func: delete_groups_and_check_memberOf, from group: \n %d", group)) + return list, err + } + if len(res.Entries) != 1 { + err = errors.New(fmt.Sprintf("SearchResult get: %s, SearchResult wanted: 1", len(res.Entries))) + return list, err + } + EntryAtt := res.Entries[0].Attributes + list_users := []string{} + for _, att := range EntryAtt { + list_users = append(list_users ,att.Values[0]) + } + + //Del group + del := ldap.NewDelRequest( group.DN, nil) + err = l.Del(del) + if err != nil { + return list, err + } + list[group.DN] = list_users + } + return +} + +func check_memberOf(l *ldap.Conn, list map[string][]string) (err error) { + //Check the memberOf of all users + for groupeDN,_ := range list{ + search_req := ldap.NewSearchRequest( + "ou=users,dc=deuxfleurs,dc=fr", + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases,0 ,0, false, + fmt.Sprintf("(&(objectclass=inetOrgPerson)(memberOf=%s))",groupeDN), + []string{"cn"}, + nil, + ) + res, err := l.Search(search_req) + if err != nil { + return err + } + if len(res.Entries) != 0 { + err = errors.New(fmt.Sprintf("L'user '%s' a encore le DN d'un groupe supprimé: %s",res.Entries[0].Attributes[0].Values[0],groupeDN)) + return err + } + } + return err +} + +func reconnect(l *ldap.Conn) (l_nouv *ldap.Conn, err error){ + l.Close() + l_nouv, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d",adresse,port)) + if err != nil { + return + } + err = l_nouv.Bind(bindusername, bindpassword) + return +} + + +func main() { + var ok bool + bindpassword, ok = os.LookupEnv("BOTTIN_DEFAULT_ADMIN_PW") + if !ok { + if len(os.Args) == 2 { + bindpassword = os.Args[1] + } else { + bindpassword = "" + } + } + + log.Info(fmt.Sprintf("Password selected: %s",bindpassword)) + //log.SetLevel(log.TraceLevel) + + //Create a connection with Bottin server + l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", adresse, port)) + //l.Debug = true + printError(err) + + //Bind with the admin account generated + err = l.Bind(bindusername, bindpassword) + printError(err) + + //Create our object Rand, it's important to always have the same values + source := rand.NewSource(666475745) + r := rand.New(source) + log.Info(fmt.Sprintf("The seed of the rand object is %d.\n",r.Seed)) + + //Create user and groups OrgaUnit + err = createOU(l) + if ldap.IsErrorWithCode(err, uint16(68)) { + log.Warn("Les OrganizationalUnit users et groups sont déjà présents.") + }else { + printError(err) + log.Info("Création des OU de groups et users") + } + + //Create random groups + tab_AddRequest_groups, err := createGroup(r, l) + printError(err) + log.Info(fmt.Sprintf("Création des groupes aléatoirement réussi: %d\n", len(tab_AddRequest_groups))) + + //Create random users + tab_AddRequest_users, err := createUser(r, l) + printError(err) + log.Info(fmt.Sprintf("Création des users aléatoirement réussi: %d\n", len(tab_AddRequest_users))) + + //Search and compare attribute Users. (We keep Attribute object from 'Create random users' and compare with the result of our search) + err = test_attributes(l,tab_AddRequest_users, "inetOrgPerson","users") + printError(err) + log.Info("Tous les attributs users insérés dans Consul ont été vérifiés..\n") + + //Search and compare attributes Groups + err = test_attributes(l,tab_AddRequest_groups, "groupOfNames","groups") + printError(err) + log.Info("Tous les attributs groups insérés dans Consul ont été vérifiés.\n") + + + //Close the connection and open an other. If we don't do this, bottin server send a wrong answer. Comment this part if you want to try this + l,err = reconnect(l) + printError(err) + //Modify attributes users and groups. + + //Modify users' attributes and check them + + log.Debug(fmt.Sprintf("Les valeurs sont:\n %s", tab_AddRequest_users)) + err = test_modify_attributes(l, r, tab_AddRequest_users, []string{"displayname"}) + printError(err) + log.Debug("Modifications users faites") + + //Check if the attributes are correct: + err = test_attributes(l,tab_AddRequest_users, "inetOrgPerson", "users") + printError(err) + log.Info("Les modifications ont bien été prises en compte") + log.Debug(fmt.Sprintf("Les nouvelles valeurs sont:\n %s", tab_AddRequest_users)) + + + + + //Modify users' attributes and check them + err = test_modify_attributes(l, r, tab_AddRequest_groups, []string{"description"}) + printError(err) + log.Info("Modifications groups faites") + + //Check if the attributes are correct: + err = test_attributes(l,tab_AddRequest_groups, "groupOfNames", "groups") + printError(err) + log.Info("Les modifications ont bien été prises en compte") + + //Close the connection + l, err = reconnect(l) + printError(err) + + //Add users in group, search them, delete several samples and search again to be sur it's good + err = add_user_in_groups(l, r, tab_AddRequest_users, tab_AddRequest_groups) + printError(err) + log.Info("Ajout d'users dans les groupes fait") + + //Close the connection + l, err = reconnect(l) + printError(err) + + list, err := delete_groups(l, tab_AddRequest_groups) + printError(err) + log.Info("groupe supprimé") + + + l,err = reconnect(l) + printError(err) + + err = check_memberOf(l, list) + printError(err) + log.Info("Le memberOf a été correctement vidé") + + //Clean: Delete all users and groups (not OU users and groups) + err = clean(l, tab_AddRequest_users, tab_AddRequest_groups, true, false) + printError(err) + log.Info("Clean succes") + + return + +} diff --git a/test_automatic/rapport_beug_bottin.txt b/test_automatic/rapport_beug_bottin.txt new file mode 100644 index 0000000..1669e14 --- /dev/null +++ b/test_automatic/rapport_beug_bottin.txt @@ -0,0 +1,40 @@ +Introduction et Observation premières: + + Lors de la réalisation de mon code Go, j'ai trouvé un beug qui provoquait l'arrêt de mon programme car Bottin envoyé une réponse erronée à mon programme. + Dans logs de Bottin je ne voyais aucune erreur, Bottin n'avait pas cessé de fonctionner. Il s'arrêtait à chaque fois sur mes requêtes Del (mes dernières). + + +Reproduction du beug: + + Pour reproduire le beug, il suffit de lancer le programme interrogation.go et de commenter les lignes suivantes : + +260 //Close the connection and open an other. If we don't do this, bottin server send a wrong answer. Comment this part if you want to try this +261 l.Close() +262 l, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d",adresse, port)) +263 printError(err) +264 err = l.Bind(bindusername, bindpassword) +265 printError(err) + + Ainsi on obtient l'erreur suivante: + +2021/07/07 01:25:51 Received unexpected message -128, false + + +Test réalisé pour comprendre la source du problème: + + Ma première hypothèses fut que j'envoyais trop de requêtes dans un court laps de temps et ainsi Bottin ou Consul ne pouvait pas suivre. +J'ai placé un sleep de 50 puis de 100 Millisecondes entre chaque requête, j'obtenais toujours la même erreur. +J'ai essayé de mettre un sleep de 10 secondes avant mes requêtes de suppression mais j'obtenais toujours la même chose. + +Hack pour résoudre: + + La première solution qui a fonctionné était de réduire le nombre de requêtes en n'exécutant pas certains tests random. + La dernière solutions qui est utilisée: + Fermée la connexion puis la réouvrir permet de palier à ce problème + +Hypothèses du problème ?: + + Existerait-il un Buffer par Bind qui se remplirait ? Et lorsque celui-ci est plein, ne renvoie pas d'erreur juste n'arrive plus à répondre. + +Erwan DUFOUR +Deuxfleurs diff --git a/test_automatic/start_test.sh b/test_automatic/start_test.sh new file mode 100755 index 0000000..38fe706 --- /dev/null +++ b/test_automatic/start_test.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +trap "kill 0" EXIT + +export BOTTIN_DEFAULT_ADMIN_PW=$(openssl rand -base64 24) +echo $BOTTIN_DEFAULT_ADMIN_PW +consul agent -dev > /dev/null 2>&1 & +sleep 2 +./bottin > /dev/null 2>&1 & +sleep 1 +./test_automatic/integration -- cgit v1.2.3