Authentication
389-ds
Using openldap I found overtime that docker images for it were not the most
actively maintained, and it took extremely long for find a reliable and up-to-
date image. During that time I got curious to know if there were any ldap
alternatives that were more maintained, and found 389-ds which is maintained
by Red Hat. It's also a component in FreeIPA so I'd imagine it stays
maintained for a long time. There was some issues that made it not the easiest
to migrate to, one being that it doesn't currently support argon2 password
encryption, and the docker image for it requires a bit of a fixup to create the
ldap tree and even the memberOf plugin to be used in my other applications.
Eventually I finally figured out how to use 389-ds cli tool and suprisingly it
was much more simple than openldap. I have now replaced openldap with this
and plan to keep using it moving forward.
Authelia
Deprecated (12/16/24)
Single Sign On service that unifies the authentication for all of the
applications I have deployed. Getting this configured took a long time but has
been one of the best applications I have. Using authelia I can define domain
restrictions for certain users, based on groups. With authelia you can
define a list of users through yaml files, but it's recommended to use an
ldap service that will be used as the backbone for user/group management. It
provides an simple login interface and password reset functionality with the
configuration of smtp (is necessary).
It was really easy to integrate authelia with traefik forward auth to
properly authenticate applications and ensure only certain users can access
them. Most of the complication in setting up authelia was translating lldap
identification of resources. I did at one point also used it's oauth
functionality but it was cumbersome to configure for every app with needing to
modify the configuration file and restart the deployment. Up until recently I
found authelia perfect for my user management, but I realized it's lacking in
functionality compared to authentik and keycloak (mostly lack of integration
from other IDP providers like Google).
Authentik
Deprecated (7/27/25)
Before using authelia I started researching and working with authentik which
provides all of the same functionality and actually even more. It comes with
user management as well as outposts that can be configured to run proxy
authentication and active directory (ldap). I could not easily get the ldap
outpost to run and it became a deal breaker as jellyfin was dependent on using
it. I did eventually figure it out the second time I used it, but configuring
flows for different actions felt so daunting. One really cool thing to see was
it's ability to synchronize users from an external source so I could continue to
use lldap as a backup.
Updated (12/16/24)
Took a second stab at setting up authentik but I think it's working perfectly
well for my service setup. It took some time to understand how flows/outposts
integrations worked, but after all of that was settled it has been super
straightforward. I needed to deploy authentik completely manual. Giving
authentik RBAC control to the cluster allows it to automatically deploy
outposts, but the LDAP outpost had weird behavior when making changes to it.
It would consistently ping the authentik server causing it to be overloaded.
It did require a two step deployment where I needed to grab the token from
authentik to use in the deployment for the outpost but it was totally fine as
a one time setup.
After figuring out how to synchronize over information from lldap I was able
to easily migrate all logins without losing any data. In addition I needed to
create new flows for invitations and reset password actions. But a really
cool new feature I was able to add was using google as a login solution.
Because most emails for accounts are using gmail I can automatically map
google logins to existing accounts so users could login without needing their
password. After recreating the service accounts for nextcloud and jellyfin I
was able to use ldap for them. But a really cool new addition was that I was
able to also add SSO for Jellyfin.
Updated (7/27/25)
I was hesistant integrating authentik due to many issues around it's stability
and the team's unwillingness to address core issues wrong with the software.
I've ran into two instances of frustration, the last one being a final straw.
The first was issues connecting with the LDAP provider that caused auth issues
with services that depended on it. The last was literally an error with sending
a reset password email, with an issue open that was not closed for inactivity.
Such a basic functionality breaking in a release was unacceptable to me and
decided to move one from it.
Migrating applications that supported OIDC logins to use keycloak was pretty
easy since keycloak supported all OIDC standards. One thing that I needed to
do for all created clients, was adding a group membership mapper to their
configurations. This allowed applications to read the groups a user belonged to.
Keycloak
Towards the end of 2024 I decided to mess around with Keycloak after discovering
a bug with authentik that broke the websocket connection between it's outposts
and the server, leaving me needing to restart the server every time I added a
new application or provider. It took quite some time to get this going, but only
after I found the necessary environment variables to configure it behind a
reverse proxy. Once that was completed most of remaining work was integrating it
with openldap to provide a two way sync of user data, as using lldap was not
possible due to it being written as a read only server.
In order to get keycloak to run in production mode behind a reverse proxy you
needed to define KC_PROXY_HEADER=xforwarded and KC_HTTP_ENABLED=true and
everything would work perfectly fine. I did learn a lot in how to map ldap
attributes to keycloak, and it was mostly on understanding what the different
attributes mean. I have come to have a lot of appreciation for authentik after
learning all the complexities of that.
The reason I could not adopt keycloak in my setup was due to oauth2-proxy
limitation in being able to support different configurations for different
applications. This would stop my ability from using different group entitlements
for certain applications. In addition, it was reverting to a user management
setting similar to authelia and lldap which did not feel like an
improvement.
Updated (7/27/25)
Took a second stab at keycloak with oauth2-proxy and it turns out that I had
approached it wrong at first. You can set up different oauth2-proxy services
for each group setting that honestly was only two with majority of the services
being behind an admin login. After figuring out the setup it made it super easy
to apply the middleware everywhere. The only caveat was needing to bring in
Authorization header pass-through values for the arr apps into traefik
settings, but it was still totally fine. A really import thing when integrating
openldap with keycloak was setting the password policy so that passwords are
not stored in plaintext.
ldap Account Manager
Deprecated (2/1/26)
After I found out that phpldapadmin image from osixia that I was using was
no longer active, I had to scramble and find an alternative ldap ui for
openldap. There was phpldapadminv2 which was the successor and
nfrastack/fusion-directory, but both felt lacking compared to this simple to
use UI. I was easily able to get the same tree view as phpldapadmin and
fusion-directory had way to many options that I didn't need. This did have
some finicky parts to it, where some simple object management things were hidden
behind a paywall. One thing I kind of hated was that I needed to remove the
unix module for users before I could make changes to them. I may go to
I
decided to drop this completely since most of it's tools I don't care to use and
phpldapadminv2 but going to keep messing with this and see how it goes.phpldapadminv2 covered everything that I need.
LLDAP
Deprecated (12/16/24)
A ldap service was necessary in order to work with authelia, but many of the
ldap services I came across were incredibly complicated to understand. lldap
was a recommendation to authelia that really helped me understand active
directory as it was much more minimal than other tools. Here's some things I
learned:
DNrefers to the domain that users are authenticated against, usually it follows the organization domain.UIDis the unique field used by users inldap. In mainstream ldap servers this would beentryUUID.CNrefers to a common name of an object.SNrefers to the last name of an object.OUrefers to the organizational unit such aspersonorgroup. A user belongs to a unit.
Oauth2 Proxy
Setting up oauth2-proxy was interesting to say the least and required really
thinking through how authorization works with my applications. The setup was
fairly easy with keycloak but applying it in traefik configurations required
a lot of trial and error until I understood what I really needed. With traefik
it makes it difficult to the use same oauth2-proxy instance to authorize for
different groups, but the solution was to just setup a different instance for
different group combinations. While it doesn't sound ideal theoretically, I only
needed two group configuration types for all my applications so there was
minimal overhead for that, and it's really easy to add.
I was thinking about setting it up per application, when I thought I can set
Authorization headers manually after logging in with Keycloak but it turns
out that it did not have that ability and just passed through the current login
credentials for that. To further explain the traefik configuration issues, it
does not use the /oauth2/auth endpoint that allows defining groups to limit
the scope of allowed users, but it instead uses /. Also a weird issue was that
oauth2-proxy scopes would be messed up when defining a group, so I needed to
manually set the scope for oauth2-proxy to use.
OpenLDAP
Deprecated (2/1/26)
This was a pain in the ass to setup, mostly on just finding an adequate image to
use since there was so many, but there were not maintained. In addition, getting
a UI application took so long as well. After finally understanding how to setup
openldap it was a breeze to recreate. Using the image: tiredofit/openldap,
it made it really easy to setup the admin user and handle other setup as well.
The most important thing this image provided that others didn't, was introducing
better password hashing, since by default openldap stores passwords in
plaintext.
Using two configuration files and applying them on startup using a custom
script, I was able to setup the ldap store exactly to my liking. In addition,
with integrating keycloak just enabling the v3 password settings, it ensured
that passwords were hashed when storing it. Using openldap is much more ideal
than lldap because since it is a writable store, I can easily use this to
migrate users to whatever OIDC service I want. Something not in common with
lldap is that the user class is inetOrgPerson instead of user, since it
contained all the attributes I needed in it's scheme versus the other person
classes.
Updating here, tiredofit/openldap image went EOL and was replaced by
nfrastack/openldap. While the migration was seamless thanks to ldap account
manager, it turns out it cause the password hashing issue to reemerge. This was
due to the old image pre-compiling argon2 into the ldap server, while in the
newer version I had to create a new ldif file to auto load the password policy
the trigger argon2 encryption on all passwords. Also I figured out I was using
cn incorrectly the entire time as the username, when I should have been using
uid. I found this when trying to update the actual names for users and noticed
the hashed passwords were being stored back into openldap as plaintext.
Another update, I eventually found a much more stable image symas/openldap,
which turned out to be the company that actively develops openldap. It was
surprisingly hard to find and I only came across it because of Gemini. Using
symas was a lot easier in getting it setup, except for memberOf plugin
configuration. For whatever reason it's not enabled and turning it on is not so
trivial but I do have a working example in the deprecated folder.