goauthentik / authentik

The authentication glue you need.
https://goauthentik.io
Other
13.62k stars 910 forks source link

LDAP source: internal password is written after password change (without "update internal password on login") #9518

Open sumpfralle opened 6 months ago

sumpfralle commented 6 months ago

Describe the bug With the new option "update internal password on login" (password_login_update_internal_password) disabled, I expected, that passwords are not written to the database anymore. But the password is still written to the internal database, if a user changes his password via authentik.

To Reproduce Steps to reproduce the behavior:

  1. configure an LDAP source
  2. disable "update internal password on login" for this source (the default for new sources)
  3. login to authentik with an LDAP account username
  4. verify, that the content of the password field in the authentik_core_user table for this user is still empty
  5. change the password of the user in authentik's password change dialog (new password: pw1)
  6. the password field of the authentik_core_user table is non-empty (probably containing the hash of pw1)
  7. change the password directly in the LDAP directory (not via authentik) to pw2
  8. try to login into authentik with pw1: works (probably based on the password field stored in authentik's database)
  9. try to login into authentik with pw2: works (probably based on the password stored in LDAP)

Expected behavior I expected, that the new setting "update internal password on login" is supposed to fix issue #6122 (it was closed by #8377). In this case, the password field should never be written.

Thus, the following details seem to be undesirable from my point of view:

Version and Deployment (please complete the following information):

rissson commented 5 months ago

Well, that's really up to the admin to configure. If you don't want your user to be able to change their password (because they should use the LDAP one), then they should be denied to run a password change flow (by default default-password-change).

sumpfralle commented 5 months ago

Just for the clarification of the use case:

This works beautifully and is in line with the intended use-case of the LDAP feature in authentik.

The only problem is, that authentik is storing a password in its own internal database, even though the configuration setting "update internal password on login" is disabled. This was probably just an oversight and should (IMO) obviously be fixed.

kukoarmas commented 4 months ago

I've been unable to replicate the problem.

So it seems the option is working as expected.

Maybe the problem is that the previously cached password are still used, so a workaround would be to empty them

sumpfralle commented 4 months ago

I've been unable to replicate the problem.

Thank you for trying to reproduce the problem!

Please take a look at the steps (5) and (6) of my procedure above: the problem is the password change via authentik. The changed password should just be synced to the LDAP server (this always worked), but it should not be stored locally in authentik's database.

kukoarmas commented 4 months ago

Ok, @sumpfralle is right If the goal of the "update internal password on login" is not having the user's password hash in the local database, it's not enough.

I've replicated two situations that stores the user's password hash in the local database, even with the option "update internal password on login" NOT selected:

  1. When a user is initially synchronized from the LDAP source because it didn't already exist in the local database. When this happens, the password field contains a password hash. If the user already existed with an empty password, the password field is not updated
  2. When a user changes it's password in authentik (has @sumpfralle mentioned). When this happens, a different kind of hash is stored (pbkdf2_sha256) in the database

So, if the goal was to not have a password hash in the authetik database, these two scenarios should be considered to not store the hash locally

authentik-automation[bot] commented 2 months ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

BeryJu commented 2 months ago

With the User password writeback option enabled, a password will only be written to LDAP when it is written to a user in authentik. I'm not sure what the best option would be to make these two steps separate without having to make a lot of assumptions in the code.

sumpfralle commented 2 months ago

The two involved settings of the LDAP source seem to be:

  • Update internal password on login: When the user logs in to authentik using the LDAP password backend, the password is stored as a hashed value in authentik. Toggle off (default setting) if you do not want to store the hashed passwords in authentik.
  • User password writeback: Enable this option if you want to write password changes that are made in authentik back to LDAP.

I have the feeling, that the name of the first setting ("Update internal password on login") is a source of confusion:

The seccond setting ("User password writeback") seems to be quite clear:

I'm not sure what the best option would be to make these two steps separate without having to make a lot of assumptions in the code.

I guess (without having looked at the details), that an improved wording of the settings would remove the need for these unsuitable assumptions.

BeryJu commented 2 months ago

image some updated wording that I think makes it quite a bit clearer what the options mean/do

sumpfralle commented 2 months ago

IMO this combination of options is not suitable for the goals of authentik.

As @kukoarmas wrote above (similar to my previous words):

So, if the goal was to not have a password hash in the authetik database, these two scenarios should be considered to not store the hash locally

Thus, I would recommend the following two options:

1) Cache the users' passwords in authentik

Whenever a user logs in via authentik or changes the password via authentik, the password hash is cached in authentik's database.

2) Update LDAP on password changes

Enable this option to write password changes submitted via authentik back to LDAP.

I think, the above would be the description of a reasonable implementation.

At the moment, I still have to delete all password hashs in authentik's database periodically (a cron job), since these cached values turn stale after external password changes. At the moment there is no configurable way to solve this problem of cache invalidation in authentik.

sumpfralle commented 1 month ago

In order to avoid any misunderstandings, I want to summarize the current state of LDAP support in authentik:

Currently it is not possible to run authentik in combination with an LDAP service, while allowing password changes via authentik and LDAP. Trivial cause: it is currently not possible to disable authentik's password caching.

Thus, currently it is necessary to manually discard the password hashs from the authentik database (e.g. via a cron job), if password changes via LDAP (without authentik) are possible.

IMO authentik's currently available configuration settings are not consistent and should be changed. See my proposal above.

rissson commented 1 month ago

What do you refer to when saying "authentik password caching"?

sumpfralle commented 1 week ago

What do you refer to when saying "authentik password caching"?

I refer to the fact, that authentik stores a password hash in its own database. This password hash is used for subsequent authentication requests (instead of asking the LDAP backend).

This password hash can be stored in two situations:

The unconditional storage of password hashes during a password change currently forces administrators to manually discard the password hashes from the authentik database (e.g. via cron jobs). Otherwise the authentik database may contain outdated password hashs (i.e. "user can log in with an old password").

rissson commented 1 week ago

Alright let's clarify what all of this is.

First, authentik doesn't "cache" any password. Either it writes it to its database, or not.

Now, when you have an LDAP source configured, you effectively have to deal with two password databases: the one from authentik and the one from LDAP. Interaction with those happens one two separate occasions: when the user logs in and when the user changes their password.

Log in

When a user logs in to authentik, they go through a password stage. In that password stage, you can configure which backends are to be used. This is where you can choose what combination of "standard password" (the one stored in the authentik password database), "app password" (from app tokens a user can create for out of band access to authentik), "LDAP password" (the one stored in the LDAP password database), or more recently "Kerberos password" (let's ignore that for now as this is for Kerberos sources, and works in the same way that LDAP does).

If you want users to never use the internal authentik database, then "standard password" should be deselected.

Now when the user logs in with their LDAP password, i.e. when authentik checks the user's password against LDAP (this happens here if you want to read the code), authentik allows the password to be written back to authentik if the "Update internal password on login" toggle is on (https://github.com/goauthentik/authentik/blob/main/authentik/sources/ldap/auth.py#L43). The use case here is that if LDAP goes down, users will still be able to log in using the password stored in authentik (the password is "cached" but not really).

Whether that's a good idea or not is really not up to us. It depends on the specific setup where authentik is used, and in many cases is a valid setup. In any case, as it's configurable behaviour, it's up to the admin to configure to their needs.

Password change

From LDAP

Nothing much to say here, as that happens outside authentik. When the user next logs in to authentik, provided the password stage is configured to use the LDAP backend, the new password will be used as described above.

From authentik

Password changes happen through a "Stage configuration" flow, configured on the password stage itself as "Configuration flow" (if you have many password stages, the first one is picked and shown in the user settings).

If no configuration flow is configured, the user will not be able to change their password from authentik.

If a configuration flow is configured, the user will go through it, and all validation policies on the prompt stages there will be run. If the "User password writeback" option is turned on in the LDAP source, the password will also be validated against the LDAP directory, if that directory is an Active Directory (https://github.com/goauthentik/authentik/blob/main/authentik/sources/ldap/signals.py#L42, https://github.com/goauthentik/authentik/blob/main/authentik/sources/ldap/password.py#L71, https://github.com/goauthentik/authentik/blob/main/authentik/sources/ldap/password.py#L138).

Then, the password will always be written to authentik's database.

Then, if the "User password writeback" option is turned on in the LDAP source, the password will be written back to LDAP (which happens here).


Now to answer the initial issue, if you want users to be able to change their password from authentik, and that password should be written back to LDAP, but only the password from LDAP should be used for log in, you should configure the following:

In the LDAP source:

In the Password stage: