Linux Password Fail Delay (Login/Sudo/SU/SSH)
- Authentication Methods
- Default Configuration
- Weirdness Begins
- FAIL_DELAY in login.defs
- pam_unix.so nodelay
- pam_delay.so Revisited
- pam_unix.so Delay Configuration
- SU Delay
- Lowering The Delay
When authentication on a GNU/Linux system fails, normally there is a delay added before the failure is reported to the user. I find the length of the delay excessive, and today we'll take a look at how to configure it to be more sensible.
But first I'd like to note that FreeBSD (and, likely, other BSDs) have a much better implementation of the login delay: the first couple of authentication failures are instant, and subsequent failures have the delay added (which is also longer than Linux's delay). This makes the delay normally non-intrusive at all, i.e., in normal system usage I virtually never encounter it. Linux's implementation is to apply the delay on every failure, which I guess was easier to implement but provides a poor user experience.
Authentication Methods
I am concerned with 4 authentication methods:
-
login- this is the prompt that is displayed after a computer boots. It can also be manually invoked by runningloginfrom a root shell. -
su- this is normally how I gain root from a regular user account. -
sudo- I generally do not use sudo myself but I will include it here for completeness. -
ssh- it can be useful to have different settings for local vs remote logins. To testsshlogin delay, it is sufficient to executessh localhostfrom any user account and enter empty login and password.
These login methods are hard to time because they consume input from terminal rather than from redirectable standard input. The timings below are my approximations.
Default Configuration
On a newly installed Debian system, we find the following note and setting in
/etc/pam.d/login:
# Enforce a minimal delay in case of failure (in microseconds).
# (Replaces the `FAIL_DELAY' setting from login.defs)
# Note that other modules may require another minimal delay. (for example,
# to disable any delay, you should add the nodelay option to pam_unix)
auth optional pam_faildelay.so delay=3000000
... and, the related entry in /etc/login.defs:
################# OBSOLETED BY PAM ##############
# #
# These options are now handled by PAM. Please #
# edit the appropriate file in /etc/pam.d/ to #
# enable the equivelants of them.
#
###############
# ... other lines snipped ...
#FAIL_DELAY
Testing our 4 authentication methods, we get:
| Method | Delay |
|---|---|
login |
3 seconds |
su |
3 seconds |
sudo |
2 seconds |
ssh |
2 to 4 seconds depending on user account |
While login and su produce the expected 3 second delay, sudo and
ssh appear to have a mind of their own and have a delay that is clearly
less than 3 seconds (I'd say it is just over 2 seconds long).
For ssh, the delay is all over the place: running ssh localhost from
a non-root user account produces a 2 second delay, ssh localhost from the
root account produces a 3 second delay, ssh bogus@localhost from the root
account produces sometimes a 4 second delay and sometimes a 2 second delay.
Interesting.
Weirdness Begins
OK, let's comment out the auth optional pam_faildelay.so delay=3000000
line and see what happens. No restart of any services or reboot of the
system is required for the changes to take effect.
On my system commenting that line out appeared to have had no effect
whatsoever - both login
and su retained their 3 second delay, sudo retained its 2 second delay
and ssh kept delaying between 2 and 4 seconds. In actuality, the delay
for login decreased from 3 seconds to 2 seconds, but I didn't realize this
until later. The behavior with the pam_faildelay.so line commented out
is as follows:
| Method | Delay |
|---|---|
login |
2 seconds |
su |
3 seconds |
sudo |
2 seconds |
ssh |
2 to 4 seconds depending on user account |
Since I didn't realize that login behavior actually changed, I thought
that I didn't make the change correctly. Increasing the delay to 10 seconds
will clarify the situation, as it did for me. Testing the following
configuration:
auth optional pam_faildelay.so delay=10000000, we get:
| Method | Delay |
|---|---|
login |
10 seconds |
su |
3 seconds |
sudo |
2 seconds |
ssh |
2 to 4 seconds depending on user account |
The change to the pam_faildelay.so line only affects login method,
and the delay there is always at least 2 seconds regardless of the setting.
With this information the following comment at the very top of /etc/pam.d/login
now makes more sense:
#
# The PAM configuration file for the Shadow `login' service
#
By "'login' service" they must mean the login program. I don't know why
it's being referenced as a "service". The manual page for it calls it a
program.
Re-reading the comment above the pam_faildelay.so configuration line
we also see that the delay specified is minimal. Clearly something else is
specifying a minimum delay of 2 seconds, but there isn't any other mention of
"delay" under /etc/ on my system.
Question to the authors of default PAM configuration: why is the login
delay set to 3 seconds and sudo/ssh delay is 2 seconds? Most systems
will be attacked over the network, so why is one local login mechanism
configured to have a minimally longer delay than network logins?
FAIL_DELAY in login.defs
Now that we know there are minimal delays specified by unknown software,
let's play with the "obsoleted" FAIL_DELAY setting in login.defs.
Unlike the pam_faildelay.so, the value here is in seconds, thus I added
to my login.defs:
FAIL_DELAY=10
I commented out the pam_faildelay.so in /etc/pam.d/login for this test.
Result:
| Method | Delay |
|---|---|
login |
2 seconds |
su |
10 seconds |
sudo |
2 seconds |
ssh |
2 to 4 seconds depending on user account |
Now, let's set FAIL_DELAY=0 and see what happens:
| Method | Delay |
|---|---|
login |
2 seconds |
su |
2 seconds |
sudo |
2 seconds |
ssh |
2 to 4 seconds depending on user account |
It appears that FAIL_DELAY has a non-zero default value (3 seconds),
which is not documented but is used by su. su is also subject to the
2 second delay established by as of yet unknown software.
Note also that although FAIL_DELAY is claimed to be "overridden by PAM",
the overrides by default only exist for one program (login, the only program
that /etc/pam.d/login apparently applies to) thus changing the value in
/etc/pam.d/login where it can be found by grep doesn't affect su in
the slightest, as per our testing so far.
pam_unix.so nodelay
Let's add nodelay to pam_unix.so configuration, as advised in /etc/pam.d/login.
Unfortunately there are many references to pam_unix.so on my system:
/etc/login.defs:# overriden by PAM, since the default pam_unix module has it's own built
/etc/pam.d/common-account:account [success=1 new_authtok_reqd=done default=ignore] pam_unix.so
/etc/pam.d/common-auth:auth [success=1 default=ignore] pam_unix.so nullok
/etc/pam.d/common-password:# Explanation of pam_unix options:
/etc/pam.d/common-password:# used to change user passwords. The default is pam_unix.
/etc/pam.d/common-password:#`OBSCURE_CHECKS_ENAB' option in login.defs. See the pam_unix manpage
/etc/pam.d/common-password:password [success=1 default=ignore] pam_unix.so obscure yescrypt
/etc/pam.d/common-session-noninteractive:session required pam_unix.so
/etc/pam.d/common-session:session required pam_unix.so
/etc/pam.d/login:# to disable any delay, you should add the nodelay option to pam_unix)
/etc/pam.d/runuser:session required pam_unix.so
Editing these files by hand is tedious. The following shell command adds
nodelay to all lines:
for f in /etc/pam.d/*; do sed -i -e 's/pam_unix.so/pam_unix.so nodelay/' $f; done
And the following commands removes nodelay:
for f in /etc/pam.d/*; do sed -i -e 's/pam_unix.so nodelay/pam_unix.so/' $f; done
Result after disabling the delay in all files (with /etc/pam.d/login and
/etc/login.defs also still setting the delay to zero):
| Method | Delay |
|---|---|
login |
None |
su |
None |
sudo |
None |
ssh |
None |
The delay is now gone, and individual settings can be verified to do what we figured out them to do earlier:
- Having no
FAIL_DELAYinlogin.defsat all adds about 2 seconds of delay tosuand does not change behavior oflogin,sudoorssh. Having no delay insurequires settingFAIL_DELAY=0and addingnodelaytopam_unix.so. - The
pam_faildelay.sosetting in/etc/pam.d/loginaffects only theloginprogram, nothing else.
pam_delay.so Revisited
The man page for
pam_faildelay.so states:
pam_faildelay is a PAM module that can be used to set the delay on failure per-application.
If no delay is given, pamfaildelay will use the value of FAILDELAY from
/etc/login.defs.
Our testing reveals that the first paragraph of this description is misleading:
pam_faildelay.so is one of three sources of the delay and the delay is set
to the largest one requested by any of the sources (pam_faildelay.so has
no privileged standing in this regard).
The second part, however, appears to be accurate. After disabling the
delay added by pam_unix.so, specifying the following in /etc/pam.d/login:
auth optional pam_faildelay.so
... causes the delay experienced by login to match the delay specified by
FAIL_DELAY in login.defs, and if there is no FAIL_DELAY setting in
login.defs, there isn't any delay in login operation.
pam_unix.so Delay Configuration
The man page for pam_unix.so says
the following about the nodelay argument:
This argument can be used to discourage the authentication component from requesting a delay should the authentication as a whole fail. The default action is for the module to request a delay-on-failure of the order of two second.
Whoever wrote this deserves an award for language contortionism,
at the collective expense of the rest of the world who has to make sense of
what this description is struggling to convey. "authentication component"
refers to the part of pam_unix.so which handles authentication; for
whatever reason, pam_unix.so performs several tasks rather than those
tasks being split across modules. Because of this pam_unix.so figures
many times in /etc/pam.d. You'd need to understand the various components
of pam_unix.so and map the lines under /etc/pam.d to the component to
figure out where nodelay would be applicable and where it wouldn't be.
I imagine in practice there's no functional problem with appending it to all
pam_unix.so references.
Separately, we learn that the delay is not fixed but "on the order of two
second", which I suppose can explain the variable delay seen by ssh
but doesn't explain why sudo's delay is always shorter and why
login's delay doesn't ever seem to be more than 3 seconds with the
default configuration. Maybe both sudo and login do delay longer on
occasion and I'm just missing it.
SU Delay
su really is a special case. Remember how we figured out that
FAIL_DELAY defaults to zero if not specified? Yeah, this doesn't apply to
su. If there is no FAIL_DELAY given in login.defs, su imposes a
delay of about 1.5 seconds and every other program has no delay.
This delay remains in effect even if another delay is configured via
PAM elsewhere, as will be done in the next section. To have su start
out with no delay, login.defs must have an explicit FAIL_DELAY=0 in it.
Lowering The Delay
In my case, I want the delay to be lower than the default of 2-5 seconds.
This means I have to disable the default delay of pam_unix.so by
adding nodelay to every mention of pam_unix.so in /etc/pam.d,
then replacing the delay specified in pam_faildelay.so with a smaller value
(or remove this setting entirely), and add some delays for programs other
than login (su, sudo and ssh).
The order of directives in PAM configuration matters; pam_faildelay.so
must be specified before pam_unix.so or the delay specification will be
ignored (with no warnings). On my system /etc/pam.d/login includes
/etc/pam.d/common-auth, and the delay for all programs can be specified
by adding the following line to the top of /etc/pam.d/common-auth, before
thepam_unix.so` line:
auth optional pam_faildelay.so delay=500000
This requests a very sensible 0.5 second delay. Let's see the results:
| Method | Delay |
|---|---|
login |
0.5 seconds |
su |
0.5 seconds (~1.5 seconds if you don't have FAIL_DELAY=0 in login.defs) |
sudo |
0.5 seconds |
ssh |
0.5 to 1 1 seconds |
Clearly we are still too optimistic about understanding how this delay
business is working. I verified that setting the delay to 0 instead of
500000 in pam_faildelay.so removes the delay completely in all
programs, including ssh, but with any delay configured via
pam_faildelay.so, the actual delay produced by ssh is longer. Depending
on the configured delay I see something like this:
| Configured Delay | Actual Delay |
|---|---|
| 0.5 seconds | 0.7 to 1 seconds |
| 5 seconds | 6-9 seconds |
It looks like ssh is somehow increasing the delay and doing it in a way
that depends on the configured delay length. How and why it does that is
something I elected to omit researching, because I wanted a slightly longer
delay for ssh anyway, and here it is doing that all on its own, thus
requiring zero additional configuration, even if it's pure chance that its
logic is indeed desirable. I settled on reducing the delay configured with
pam_faildelay.so to 0.3 seconds; this produces a roughly half a second
delay for SSH logins and a shorter delay for local logins on the machine,
which I am quite happy with.