diff --git a/doc/designs/disable-stale-users.md b/doc/designs/disable-stale-users.md new file mode 100644 index 000000000..0918be02d --- /dev/null +++ b/doc/designs/disable-stale-users.md @@ -0,0 +1,303 @@ +# Disable Stale Users + +**DESIGN STAGE** + +## Overview + +Some regulations call for accounts that have not been used for a specific +amount of time be automatically disabled in the identity management system. + +FreeIPA does not keep track of successful authentication events. +The krbLastSuccessfulAuth attribute is not updated by default since +https://pagure.io/freeipa/issue/5313 for reliability reasons. +Therefore distinguishing between stale and active accounts cannot be done with +existing logon data. + + +## Possible approaches + +### Environments with mandatory, periodic password changes + +The problem is easy to solve in environments where user passwords must be +changed regularly. A user account whose associated password is past its due +change date can be considered stale and therefore be disabled by the system. +In this case, a grace period must be provided between password expiration and +the actual account locking event. +Total writes to LDAP using this approach due to the disable stale users tooling +are minimal (only nsAccountLock). However, password-change related writes are +needed every 90 days (by default). +Each password change updates the krbLastPwdChange, krbPasswordExpiration, +userPassword, krbPrincipalKey, and krbExtraData attributes. + +The Disable Stale Users (DSU) tooling proposes to make sure no non-locked +account has a password expired since N days, N being 7 by default. + +### Environments with no mandatory password changes + +For environments where user passwords never expire, a new mechanism must be +added to FreeIPA plugins ipa-kdb and ipa-lockout to account for each user +account authentication events. Since updating a LDAP user account attribute on +each authentication event must be avoided as the update frequency could easily +be high, (see https://pagure.io/freeipa/issue/5313 for background +information), a coarse update mechanism is proposed. + +An attribute containing a timestamp is added to each user: +coarseSuccessfulAuthTime. It will be added at account creation time (set to +current date). +The timestamp contained therein is not precise. It is updated by IPA plugins +whenever a successful authentication event happens, but only if the current +timestamp is sufficiently in the past. The update interval is configurable, but +to avoid replication storms, it is randomized. To achieve this, a randomized +value is added to a fixed time interval. + +Some attributes must be added to the cn=ipaConfig schema: +* the first one to activate the mechanism (default: not present): +``` +ipaConfigString: DSU:RecordSuccessfulAuth +``` +* the second attribute contains the fixed time period (default: 28 days): +``` +ipaDSURecordSucessfulAuthTimeInterval: 2419200 +``` +* the third one contains a time period to be added. The actual value used is + randomized between 0 and the attribute's value (default: 7 days) whenever + a user successfully authenticates. +``` +ipaDSURecordSucessfulAuthRandomizedTimeInterval: 604800 +``` + +Updating this (replicated) timestamp attribute every ~30 days requires less +LDAP writes than mandating password changes every 90 days as the latter would +imply updating five (replicated) attributes at least once in that 90-day +period. + +The Disable Stale Users (DSU) tooling, when the above mechanism is active, +proposes to make sure no non-locked account has a timestamp attribute 90 days +(by default) in the past. + +On a successful authentication event: + +``` +current_date = datetime.now(timezone.utc) +last_update = coarseSuccessfulAuthTime +update_internal = ipaDSURecordSucessfulAuthTimeInterval + + randint(0, ipaDSURecordSucessfulAuthRandomizedTimeInterval) +if current_date - last_update > update_internal: + coarseSuccessfulAuthTime = current_date +``` + +As a rule of thumb, if the requirement is to disable users not seen for 90 days +maximum, updating the attribute roughly every 35 days is good enough. +At worst, the user would have authenticated the first day and the last day of +the 35-day maximum update interval and the randomized time interval would have +been 7 days. +The attribute would therefore contain the date from the first day, as day #35 +would have been considered too close to the existing timestamp to warrant +updating it. DSU would then proceed to disable the user account 90 days after +the existing coarseSuccessfulAuthTime timestamp - this is only after 55 day +(90-35) of real inactivity, which fits the requirement (albeit loosely). + +Having a 90-day inactive user disabling requirement would by default disable +accounts really inactive for 55 (90-35) to 90 days. + +In short: + +``` +if (current_date - 90d) > coarseSuccessfulAuthTime: + disable_account() +``` + +The 90-day default value is configurable (see appropriate section below). + + +### Notes + +Installing the DSU tooling will not change the behavior of existing clusters. +DSU has to be explicitly configured and enabled to start disabling stale +accounts. + +Accounts are locked out by setting the nsAccountLock attribute +(OID: 2.16.840.1.113730.3.1.610) to True. + +FreeeIPA is not directly involved when a user logs in using an ssh key so it +cannot make decisions on whether an account is "active" or not. However, +setting nsaccountlock = TRUE will block all logins including those using ssh +key authentication. +For passwordless environments, S4U2Self is proposed to be added to SSSD to +account for successful user logon events through ipa-kdb: +https://pagure.io/SSSD/sssd/issue/4077 +The mechanism laid out in "Environments with no mandatory password changes" +would then be applied as-is. + +There is always the possibility that a long-inactive user account will be +disabled (nsAccountLock'd) by DSU even if that user managed to logon the +previous few seconds before or during the DSU window due to: +* the time it takes for the timestamp attribute to be replicated to the DSU +runner +* the time it takes for nsAccountLock to be replicated from the DSU runner +to the replica this user uses. + +There is no way to prevent that from happening with the present design, but +the probability of that happening is low. + + +## Example + +### Environments with mandatory, periodic password changes + +Given the following requirements and facts: +* stale accounts must be disabled after 90 days' inactivity +* grace period is decided to be 7 days +* the tool runs every day +* replication is expected to complete in 2h maximum + +setting the maximum password lifetime to 80 days or less would meet the +guidelines as 80+7+1 (give or take 2h) is less than 90. + +### Environments with no mandatory password changes + +If the requirement is to disable users not seen for 90 days maximum, updating +the attribute every 35 days is good enough. +At worst, the user would have authenticated the first day and at least once +during this 35 day period, say day #29. The attribute would therefore contain +the date from the first day, as day #29 would have been considered too close to +the existing timestamp to warrant updating it. The user account would then be +disabled after 61 days of inactivity which fits (loosely) the requirement. +In short, having a 90-day inactive user disabling requirement would by default +disable accounts inactive for 55 (90-35) to 90 days. + + +## Limitations + +### Environments with mandatory, periodic password changes + +If regulations call for stale/inactive accounts to be disabled within X +number of days, then the lifetime of the passwords for these accounts must be +set to a value lower than: +* X, +* minus the expected grace period mentioned above, +* minus the maximum amount of time between two invocations of the DSU tool, +* minus the expected maximum replication delay from the replica the DSU tool is +installed on to the farthest point of the cluster. + +### Environments with no mandatory password changes + +The longer the time period, the less update activity for the timestamp +attribute. But using a longer period runs the risk of disabling users who have +been gone for too short a time. For instance, setting the update period to 65 +days would disable accounts inactive for the last 25 to 90 days. Whether this +fits the site's requirements is up to the administrators to determine. + +Sites requiring a better accuracy must take care not to set the update period +too low for performance reasons. It is strongly recommended to make sure the +update period is longer than the expected maximum PTO or simply "away from IPA" +period for most user accounts to avoid replication storms when users come back. +This is especially true when these away periods are in sync, like in schools. + + +## Multiple runners + +DSU can be installed on more than a single replica. This provides redundancy. +Having multiple DSU instances active at the same time provides no advantage, +so the systemd timer helper script includes a randomized time wait period. + +On very busy clusters, using dedicated replicas (hidden replicas) for DSU is +recommended. + + +## How to Use DSU + +### Installation + +The Disable Stale Users tooling will be shipped with a new version of the +FreeIPA server package so it will be included in default installs. + +### Configuration + +#### Environments with mandatory, periodic password changes + + +The following attributes are added to the schema. +``` +ipaConfigString: DSU:EnablePasswordBasedDSU +PasswordBasedDSUGracePeriod: 604800 +DSUIgnoreGroups: admins +DSUVerbosity: 1 +``` +Adding DSU:EnablePasswordBasedDSU to the cn=ipaconfig schema will let +DSU disable accounts after a grace period defined in +PasswordBasedDSUGracePeriod (by default 604800 seconds). +This is the time delta after password expiry date on which to disable accounts. + +The DSU:IgnoreGroups is multi-valued. Users belonging directly or +indirectly to these groups will not be affected. + +DSUVerbosity's default value is "1". +A value of "1" makes the DSU tool log actions to systemd. +A value of "0" disables logging. +Other values are ignored and treated like "1". + + +#### Environments with no mandatory password changes + +The following attributes are added to the schema: +``` +ipaConfigString: DSU:EnableTimeStampBasedDSU +TimeStampBasedDSUInactivePeriod: 7776000 +DSUIgnoreGroups: admins +DSUVerbosity: 1 +``` + +Adding DSU:EnableTimeStampBasedDSU to the cn=ipaconfig schema will let +DSU disable accounts when the account's coarseSuccessfulAuthTime is more +than TimeStampBasedDSUInactivePeriod in the past. + +The other variables behave the same way as the password-based-dsu case. + + +### Usage + +The DSU tool is not a UNIX daemon. It is a CLI tool that can be triggered +by a systemd timer. + +Running ``ipa-dsu`` is enough to start the tool. If DSU is enabled (see above), +it will disable stale accounts appopriately. + +The dry-run mode ``ipa-dsu --dry-run`` does not disable/lock accounts in any +way. Rather, it shows what would be done, essentially acting as a verbose mode +with "disable_accounts" set to False. The output is JSON. + +There is no verbose mode combined with disable_accounts set to True. The tool +logs what it does and the reason for each account disabling action into +journald if DSUVerbosity is set to 1. + + +### Activation + +Activate the DSU tool to run every night using the provided systemd timer: +``systemctl enable ipa-dsu.timer`` + + +### Logs + +The DSU tool logs each action in journald. Sample output: +``` +ipa-dsu: disabled account uid=alice,cn=users,cn=accounts,dc=example,dc=com - password expired on 20191001. +``` +``` +ipa-dsu: disabled account uid=bob,cn=users,cn=accounts,dc=example,dc=com - user not seen since 20190701. +``` + +## Proposed Enhancements to DSU + +DSU could reset passwords of locked-out accounts to a random, long string +after a second grace period. + +DSU could offer a way for admins to display which account(s) would be +disabled past a certain date using a CLI knob that would modify --dry-run. + +The DSU-mandated modifications to ipa-kdb and ipa-lockout described in +"Environments with no mandatory password changes" could also update the +timestamp attribute on password changes. That way, the DSU logic could be +switched to being logon timestamp-based only. +