This is an attempt of building lightweight mock container that can be used in development stack instead of active directory.
It does not and will not support most (99%) of the features that the real system has, and as such doesn't work even as simple ldap server.
Feature list:
- Lightweight (fast startup + small memory footprint)
- Support for simple ldap authentication: userPrincipalName (email) + password
- Support for listing users and groups on ldap search.
- Supports simple AND/OR search filters for attributes: objectclass + userprincipalname
- Domain validation in baseDN
- SSL support
Todo:
- Add more attributes to search filter (memberOf at least)
Application requires 3 configuration files (in configs folder) to work. Location and name of config.json is hardcoded, but the user and group configuration files can be renamed/relocated. Project contains example configuration files.
- config.json
- The actual application spesific configuration
- users.json
- List of users known to app and groups they belong to
- 'upn' and 'password' attributes are used as bind username/passwords
- groups.json
- List of groups in system.
User object consists of 7 attributes:
- upn
- "userPrincipalName" .. email address that user can authenticate with, also shown in user attributes
- password
- Plaintext password for user (so don't store any actual secrets here)
- passwordNeverExpire
- Boolean value telling if accounts password should never expire
- Currently doesn't really do anything, but does show on 'userAccountControl' attribute
- accountDisabled
- Boolean value telling if account is completely disabled
- If set to true, then prevents user from logging in
- cn
- "Common name" identifier for user object (also appears as name in attributes field)
- groups
- List of groups the user belongs to (case sensitive, must be found in groups.json)
- attributes
- Extra attributes to add to search result for users, like: countryCode, givenName .. Do not add upn/name attributes manually here
- cn
- "Common name" identifier for group object (also appears as name in attributes field)
If "crtFile" and "keyFile" attributes are set in the configuration, then the server will use SSL encryption (ldaps).
Note: port for ldaps is usually 636, so remember to take this into account. App itself doesn't care on which port it listens on, SSL mode is enabled if both key and crt file are present.
To generate key and cert, use the following commands:
openssl genrsa -out server.key 2048
openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
If you get the following error to logs: 'tls: bad record MAC', remember to allow self signed certificates in the ldap tool for example in ldapsearch:
LDAPTLS_REQCERT=ALLOW ldapsearch -H ldaps://localhost:636 ...
Make sure you have recent go (min: 1.26) installed. Copy config.example.json to config.json and modify it to suit your needs, then:
go run *.go
By default the provided dockerfile will copy config.example.json file to container as config.json file. So remember to mount your own config.json file instead of the example file.
Built docker image:
docker build -t smad:latest -f docker/Dockerfile .
Run image. Note that using port number below 1024 requires root privileges, and since this container doesn't use root user, the port is by default set to 1389:
docker run --rm -p 389:1389 smad:latest
Note: You must set the domain correctly (=match base dc) in config.json, otherwise the search returns nothing.
System currently supports the following search case(s):
-
listing of data (groups and users):
ldapsearch -H ldap://localhost:1389 -x -W -o ldif-wrap=no -D "test.user@gmail.invalid" -b "dc=example,dc=com" -
filtering by one objectClass value
ldapsearch -H ldap://localhost:1389 -x -W -o ldif-wrap=no -D "test.user@gmail.invalid" -b "dc=example,dc=com" "objectClass=top" -
filtering by multiple objectClass values
ldapsearch -H ldap://localhost:1389 -x -W -o ldif-wrap=no -D "test.user@gmail.invalid" -b "dc=example,dc=com" "(&(objectClass=top)(objectClass=group))" -
filtering by either objectclass or userprincipalname
ldapsearch -H ldap://localhost:1389 -x -W -o ldif-wrap=no -D "test.user@gmail.invalid" -b "dc=example,dc=com" "(|(objectClass=group)(userprincipalname=test@email.invalid))"