While working on a little side excursion to build an Intranet for our small community at https://hackerdrinks.sg, I figured that having a unified identity provider for our users would be a great start to having a single login for every service.
I wanted the simplest possible identity setup without having to configure too much complexity, and I ended up settling on LDAP as our main identity source.
I was contemplating between using:
- Authentik - too cumbersome, and too many levers
- Keycloak - Java, not my style and also too many levers (for now; nothing wrong with it)
- LLDAP - One config file is all you need
And I eventually chose LLDAP because it’s just a simple tool with a simple configuration, no extra levers, no need for complex IAM.
For the purpose of this article, I will not be talking about how to install LLDAP. You can follow their guide here
Connecting LDAP to Soju
Soju does not have LDAP support out of the box, but you can get there indirectly: soju supports PAM, and PAM can talk to LDAP via pam_ldap. The chain ends up looking like:
soju → PAM → pam_ldap → LLDAP
Technically it only requires a few things.
1. Compile soju with PAM support
$ CGO_ENABLED=1 GOFLAGS="-tags=pam" go build -o soju ./cmd/soju
If you’re using sqlite3, remember to add sqlite3 in the tags too, like -tags=pam,sqlite3.
2. Point soju at PAM
Add into soju’s config:
auth pam
enable-user-on-auth true
enable-user-on-auth makes soju create and enable a user the first time they authenticate successfully, so you don’t have to sojuctl create-user everyone by hand.
3. Install pam_ldap
$ sudo apt install libpam-ldap ldap-utils
ldap-utils isn’t strictly required, but you’ll want ldapsearch handy for debugging.
4. Create the PAM policy for soju
PAM looks up its policy by service name, and soju registers itself as soju, so the file has to be named accordingly:
$ sudo vim /etc/pam.d/soju
auth required pam_ldap.so
account required pam_ldap.so
5. Configure the LDAP connection
Edit /etc/ldap.conf and put in your necessary configuration:
base dc=example,dc=com
uri ldaps://ldap.example.com:6360
binddn uid=admin,ou=people,dc=example,dc=com
bindpw <password>
scope sub
ldap_version 3
tls_checkpeer no
A few things worth calling out:
- Port 6360 is not a typo. LLDAP’s defaults are
3890for LDAP/STARTTLS and6360for LDAPS, precisely so it doesn’t need root to bind the privileged389/636ports. - The
binddnis a search identity, not the login. LLDAP does not allow anonymous binds, sopam_ldapneeds an authenticated DN to look up the user’s entry before binding as that user with their password. Consider creating a dedicated read-only user in LLDAP for this instead ofadmin; your future self will thank you. And since/etc/ldap.confnow contains a password,chmod 600it. - TLS verification. LLDAP does not ship with a self-signed cert by default. If you’re using a self-signed certificate, make sure you set
tls_checkpeertoyesand also configure thetls_cacertfileandtls_cacertdir.
6. Restart soju
$ sudo systemctl restart soju
Sanity checking it
Before blaming soju, confirm LLDAP itself is happy:
$ ldapsearch -H ldaps://ldap.example.com:6360 \
-D 'uid=admin,ou=people,dc=example,dc=com' -W \
-b 'ou=people,dc=example,dc=com' '(uid=alice)'
If that returns your user, connect with an IRC client as an LLDAP user and watch the logs do their thing:
$ journalctl -u soju -f