Infrastructure:LDAP
Contents
Server
We use 389 Directory Server, installed using this Chef cookbook. The server runs on ldap.kosmos.org. The future plan is to make the LDAP server only accessible to services that use it for authentication and authorization, as well as the upcoming Kosmos Accounts Web UI
Directory structure
Here is a diagram of the directory structure we use on ldap.kosmos.org:
------------------------ | dc=kosmos,dc=org | | (organizationalUnit) | ------------------------ | | ------------------------ | | ----------------------------- | cn=users |------| |----| cn=applications | | (organizationalRole) | | (organizationalRole) | ------------------------ ----------------------------- | | | | ------------------------ ------------------------ ---------------------------- ------------------------ | ou=kosmos.org | | ou=customdomain.com | | ou=kosmos.org | | ou=customdomain.com | | (organizationalUnit) | | (organizationalUnit) | | (organizationalUnit) | | (organizationalUnit) | ------------------------ ------------------------ ---------------------------- ------------------------ | | | | ----------------------- ------------------------ ------------------------- ------------------------- | cn=example_user | | cn=example_user | | uid=xmpp | | uid=wiki | | (account,person, | | (account,person, | | (account, | | (account, | | extensibleObject) | | extensibleObject) | | simpleSecurityObject) | | simpleSecurityObject) | ----------------------- ------------------------ ------------------------- -------------------------
Here is an LDIF representation of an example of what we use on ldap.kosmos.org:
Applications
# applications, kosmos.org dn: cn=applications,dc=kosmos,dc=org objectClass: top objectClass: organizationalRole cn: users # kosmos.org, applications, kosmos.org dn: ou=kosmos.org,cn=applications,dc=kosmos,dc=org objectClass: top objectClass: organizationalUnit ou: kosmos.org # account.pro, applications, kosmos.org dn: ou=account.pro,cn=applications,dc=kosmos,dc=org objectClass: top objectClass: organizationalUnit description: Pro Account ou: account.pro # wiki account, used by mediawiki to search for users and change passwords dn: uid=wiki,ou=kosmos.org,cn=applications,dc=kosmos,dc=org objectClass: simpleSecurityObject objectClass: account uid: wiki userPassword: secret # # xmpp account, used by ejabberd to search for users and change passwords dn: uid=xmpp,ou=kosmos.org,cn=applications,dc=kosmos,dc=org objectClass: simpleSecurityObject objectClass: account uid: xmpp userPassword: secret # xmpp account, used by ejabberd to search for users and change passwords dn: uid=xmpp,ou=account.pro,cn=applications,dc=kosmos,dc=org objectClass: simpleSecurityObject objectClass: account uid: xmpp userPassword: secret
Users
# container for the organizationUnits (domains) dn: cn=users,dc=kosmos,dc=org objectClass: top objectClass: organizationalRole cn: users # kosmos.org, users, kosmos.org dn: ou=kosmos.org,cn=users,dc=kosmos,dc=org objectClass: top objectClass: organizationalUnit description: Kosmos ou: kosmos.org aci: (target="ldap:///cn=*,ou=kosmos.org,cn=users,dc=kosmos,dc=org")(targetattr="cn || sn || uid || mail || userPassword || nsRole || objectClass") (version 3.0; acl "xmpp-kosmos-read-search"; allow (read,search) userdn="ldap:///uid=xmpp,ou=kosmos.org,cn=applications,dc=kosmos,dc=org";) aci: (target="ldap:///cn=*,ou=kosmos.org,cn=users,dc=kosmos,dc=org")(targetattr="cn || sn || uid || mail || userPassword || objectClass") (version 3.0; acl "xmpp-kosmos-read-search"; allow (read,search) userdn="ldap:///uid=wiki,ou=kosmos.org,cn=applications,dc=kosmos,dc=org";) aci: (target="ldap:///cn=*,ou=kosmos.org,cn=users,dc=kosmos,dc=org")(targetattr="userPassword") (version 3.0; acl "xmpp-kosmos-change-password"; allow (write) userdn="ldap:///uid=xmpp,ou=kosmos.org,cn=applications,dc=kosmos,dc=org";) # example user for kosmos.org dn: cn=example_user,ou=kosmos.org,cn=users,dc=kosmos,dc=org objectClass: top objectClass: account objectClass: person objectClass: extensibleObject cn: example_user sn: example_user uid: example_user mail: example_user@example.com xmpp: enabled userPassword: secret # example admin for kosmos.org dn: cn=example_admin,ou=kosmos.org,cn=users,dc=kosmos,dc=org objectClass: top objectClass: account objectClass: person objectClass: extensibleObject cn: example_admin sn: example_admin uid: example_admin mail: example_admin@example.com admin: true userPassword: secret # account.pro, users, kosmos.org dn: ou=account.pro,cn=users,dc=kosmos,dc=org objectClass: top objectClass: organizationalUnit description: account ou: account.pro aci: (target="ldap:///cn=*,ou=account.pro,cn=users,dc=kosmos,dc=org")(targetattr="cn || sn || uid || mail || userPassword || nsRole || objectClass") (version 3.0; acl "xmpp-account-read-search"; allow (read,search) userdn="ldap:///uid=xmpp,ou=account.pro,cn=applications,dc=kosmos,dc=org";) aci: (target="ldap:///cn=*,ou=account.pro,cn=users,dc=kosmos,dc=org")(targetattr="userPassword") (version 3.0; acl "xmpp-account-change-password"; allow (write) userdn="ldap:///uid=xmpp,ou=account.pro,cn=applications,dc=kosmos,dc=org";) # example user for account.pro dn: cn=example_pro,ou=account.pro,cn=users,dc=kosmos,dc=org objectClass: top objectClass: account objectClass: person objectClass: extensibleObject cn: example_pro sn: example_pro uid: example_pro mail: exampleaccount.pro xmpp: enabled userPassword: secret
Admin commands
The ldapsearch and ldapadd command-line tool are provided by different packages depending on your OS. For example ldap-utils on Ubuntu, openldap-clients on Fedora, openldap on Arch Linux. It is already provided in a default macOS installation.
This can also be done using the LDAP client library of your choice.
Listing accounts
$ ldapsearch -x -W -D 'cn=Directory Manager' -b "ou=kosmos.org,cn=users,dc=kosmos,dc=org" -H "ldaps://ldap.kosmos.org"
Adding an account
Generate a hashed password
This example is using Ruby, but anything that can generate a salted SHA512 hash will also work.
$ ruby -r base64 -r digest -r securerandom -e 'salt = SecureRandom.hex(32); password = "random_password"; puts "{SSHA512}" + Base64.strict_encode64(Digest::SHA512.digest(password+salt) + salt)' {SSHA512}WsELiZM9MlUM004LF3jpV5OuV+qTsGoRR1RzffdtUuPpzOl57I7WmKL+S46/KR8HUtYPRh1ttmsNvGUX/agxLjBkZGI0MTczNWNiZjkxMDI0NGEzZTE2ZDBlNGJkMDQ5N2ZhMjVjMjQ1NzFlZmJlNmZmODhmNjE5OGM1YWM3Zjc=
Add the account
ldapadd -x -W -D 'cn=Directory Manager' -H "ldaps://ldap.kosmos.org" << EOF dn: cn=alice,ou=kosmos.org,cn=users,dc=kosmos,dc=org objectClass: top objectClass: account objectClass: person objectClass: extensibleObject cn: alice sn: alice uid: alice mail: alice@example.com userPassword: {SSHA512}WsELiZM9MlUM004LF3jpV5OuV+qTsGoRR1RzffdtUuPpzOl57I7WmKL+S46/KR8HUtYPRh1ttmsNvGUX/agxLjBkZGI0MTczNWNiZjkxMDI0NGEzZTE2ZDBlNGJkMDQ5N2ZhMjVjMjQ1NzFlZmJlNmZmODhmNjE5OGM1YWM3Zjc= EOF adding new entry "cn=alice,ou=kosmos.org,cn=users,dc=kosmos,dc=org"