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.
