semaphore icon indicating copy to clipboard operation
semaphore copied to clipboard

panic 'runtime error: index out of range [0] with length 0' while prepare db record from LDAP search query when trying ldap login

Open flybyray opened this issue 3 years ago • 1 comments

I got 2022/09/08 16:14:43 http: panic serving 127.0.0.1:37156: runtime error: index out of range [0] with length 0 when logging in with LDAP credentials. Semaphore is running as backend with a haproxy frontend

semaphore version: v2.8.65

bin/semaphore server --config /home/flybyray/git/github.com/ansible-semaphore/semaphore/.config/config.json

MySQL [email protected]:3306 semaphore
Tmp Path (projects home) /var/lib/semaphore
Semaphore v2.8.65
Interface
Port :3000
Server is running
2022/09/08 16:14:43 http: panic serving 127.0.0.1:37156: runtime error: index out of range [0] with length 0
goroutine 71 [running]:
net/http.(*conn).serve.func1()
        /usr/lib/golang/src/net/http/server.go:1825 +0xbf
panic({0x9b7aa0, 0xc00002a930})
        /usr/lib/golang/src/runtime/panic.go:844 +0x258
github.com/ansible-semaphore/semaphore/api.tryFindLDAPUser({0xc00002a438, 0x13}, {0xc00002c880, 0xc})
        /home/flybyray/git/github.com/ansible-semaphore/semaphore/api/login.go:88 +0xfb9
github.com/ansible-semaphore/semaphore/api.login({0x1fd69a0, 0xc00014c1c0}, 0xc0008a1340?)
        /home/flybyray/git/github.com/ansible-semaphore/semaphore/api/login.go:157 +0xeb
net/http.HandlerFunc.ServeHTTP(0x9a9a20?, {0x1fd69a0?, 0xc00014c1c0?}, 0xc?)
        /usr/lib/golang/src/net/http/server.go:2084 +0x2f
github.com/ansible-semaphore/semaphore/api.JSONMiddleware.func1({0x1fd69a0, 0xc00014c1c0}, 0xc0004dd750?)
        /home/flybyray/git/github.com/ansible-semaphore/semaphore/api/router.go:23 +0xf4
net/http.HandlerFunc.ServeHTTP(0xc001f60f00?, {0x1fd69a0?, 0xc00014c1c0?}, 0x98f960?)
        /usr/lib/golang/src/net/http/server.go:2084 +0x2f
github.com/ansible-semaphore/semaphore/cli/cmd.runService.func1.1({0x1fd69a0, 0xc00014c1c0}, 0xc0004dd778?)
        /home/flybyray/git/github.com/ansible-semaphore/semaphore/cli/cmd/root.go:81 +0x124
net/http.HandlerFunc.ServeHTTP(0x7fb37ada65b8?, {0x1fd69a0?, 0xc00014c1c0?}, 0xc000798630?)
        /usr/lib/golang/src/net/http/server.go:2084 +0x2f
github.com/gorilla/mux.CORSMethodMiddleware.func1.1({0x1fd69a0, 0xc00014c1c0}, 0xc0001dc7b0?)
        /home/flybyray/git/github.com/ansible-semaphore/semaphore/vendor/github.com/gorilla/mux/middleware.go:51 +0xaa
net/http.HandlerFunc.ServeHTTP(0xc001f60e00?, {0x1fd69a0?, 0xc00014c1c0?}, 0x9d8760?)
        /usr/lib/golang/src/net/http/server.go:2084 +0x2f
github.com/gorilla/mux.(*Router).ServeHTTP(0xc0002c2000, {0x1fd69a0, 0xc00014c1c0}, 0xc001f60d00)
        /home/flybyray/git/github.com/ansible-semaphore/semaphore/vendor/github.com/gorilla/mux/mux.go:212 +0x202
github.com/gorilla/handlers.ProxyHeaders.func1({0x1fd69a0, 0xc00014c1c0}, 0xc001f60d00)
        /home/flybyray/git/github.com/ansible-semaphore/semaphore/vendor/github.com/gorilla/handlers/proxy_headers.go:59 +0x142
net/http.HandlerFunc.ServeHTTP(0x3b?, {0x1fd69a0?, 0xc00014c1c0?}, 0x0?)
        /usr/lib/golang/src/net/http/server.go:2084 +0x2f
github.com/ansible-semaphore/semaphore/cli/cmd.cropTrailingSlashMiddleware.func1({0x1fd69a0?, 0xc00014c1c0?}, 0xc001f60d00?)
        /home/flybyray/git/github.com/ansible-semaphore/semaphore/cli/cmd/server.go:27 +0xf7
net/http.HandlerFunc.ServeHTTP(0x0?, {0x1fd69a0?, 0xc00014c1c0?}, 0x413885?)
        /usr/lib/golang/src/net/http/server.go:2084 +0x2f
net/http.serverHandler.ServeHTTP({0x1fd4eb0?}, {0x1fd69a0, 0xc00014c1c0}, 0xc001f60d00)
        /usr/lib/golang/src/net/http/server.go:2916 +0x43b
net/http.(*conn).serve(0xc000620320, {0x1fd6d78, 0xc0004d7c80})
        /usr/lib/golang/src/net/http/server.go:1966 +0x5d7
created by net/http.(*Server).Serve
        /usr/lib/golang/src/net/http/server.go:3071 +0x4db

It looks like sr.Entries[0] has no Entries at all ( at https://github.com/ansible-semaphore/semaphore/blob/v2.8.65/api/login.go#L88 ).

The config.json

{
  "mysql": {
    "host": "10.0.0.14:3306",
...
  },
...
  "dialect": "mysql",
...
  "ldap_binddn": "uid=LDAP_BINDING_USER,ou=Users,o=YOUR_ORG_ID,dc=jumpcloud,dc=com",
  "ldap_bindpassword": "LDAP_BINDING_PASSWORD",
  "ldap_server": "ldap.jumpcloud.com:636",
  "ldap_searchdn": "ou=Users,o=YOUR_ORG_ID,dc=jumpcloud,dc=com",
  "ldap_searchfilter": "(&(objectClass=inetOrgPerson)(memberOf=cn=a0-admin-ansible-semaphore-test,ou=Users,o=YOUR_ORG_ID,dc=jumpcloud,dc=com)(uid=%s))",
  "ldap_mappings": {
    "dn": "dn",
    "mail": "mail",
    "uid": "uid",
    "cn": "cn"
  },
...
  "ldap_enable": true,
  "ldap_needtls": true,
...
  "demo_mode": false
}

The corresponding ldapsearch query /bin/ldapsearch -H ldaps://ldap.jumpcloud.com:636 -x -b "ou=Users,o=YOUR_ORG_ID,dc=jumpcloud,dc=com" -D "uid=LDAP_BINDING_USER,ou=Users,o=YOUR_ORG_ID,dc=jumpcloud,dc=com" -W "(&(objectClass=inetOrgPerson)(memberOf=cn=a0-admin-ansible-semaphore-test,ou=Users,o=YOUR_ORG_ID,dc=jumpcloud,dc=com))"

results:

# extended LDIF
#
# LDAPv3
# base <ou=Users,o=YOUR_ORG_ID,dc=jumpcloud,dc=com> with scope subtree
# filter: (&(objectClass=inetOrgPerson)(memberOf=cn=a0-admin-ansible-semaphore-test,ou=Users,o=YOUR_ORG_ID,dc=jumpcloud,dc=com))
# requesting: ALL
#

# admin.flybyray, Users, YOUR_ORG_ID, jumpcloud.com
dn: uid=admin.flybyray,ou=Users,o=YOUR_ORG_ID,dc=jumpcloud,dc=com
...
mail: [email protected]
...
cn: Admin Robert Rettig
...
uid: admin.flybyray
...

# search result
search: 2
result: 0 Success

# numResponses: 5
# numEntries: 4

Ref:

  • https://support.jumpcloud.com/support/s/article/using-jumpclouds-ldap-as-a-service1
  • https://support.jumpcloud.com/support/s/article/using-ldapsearch-with-jumpcloud1

flybyray avatar Sep 08 '22 14:09 flybyray

Further investigation reveals that the current implementation assumes that the login user has additional LDAP permissions.

https://github.com/ansible-semaphore/semaphore/blob/v2.8.65/api/login.go#L67-L71

With JumpCloud there is a additional flag with the description Allows user to bind to and search the JumpCloud LDAP service. image

It would be better to use whoami operation.

see man ldapwhoami

ldapwhoami opens a connection to an LDAP server, binds, and performs a whoami operation.

https://github.com/go-ldap/ldap/blob/v3.4.4/whoami.go https://github.com/go-ldap/ldap/blob/v3.4.4/v3/examples_test.go#L396-L416

There is a special command to "Testing Client Authentication"

/bin/ldapwhoami\
  -H ldaps://ldap.jumpcloud.com:636\
  -D "uid=admin.flybyray,ou=Users,o=YOUR_ORG_ID,dc=jumpcloud,dc=com"\
  -x\
  -W
Enter LDAP Password:
dn:uid=admin.flybyray,ou=Users,o=YOUR_ORG_ID,dc=jumpcloud,dc=com
$ echo $?
0

see reference https://support.jumpcloud.com/support/s/article/using-ldapsearch-with-jumpcloud1

flybyray avatar Sep 08 '22 15:09 flybyray