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 runninglogin
from 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 testssh
login delay, it is sufficient to executessh localhost
from 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_DELAY
inlogin.defs
at all adds about 2 seconds of delay tosu
and does not change behavior oflogin
,sudo
orssh
. Having no delay insu
requires settingFAIL_DELAY=0
and addingnodelay
topam_unix.so
. - The
pam_faildelay.so
setting in/etc/pam.d/login
affects only thelogin
program, 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
the
pam_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.