At the recently held
Zeronights conference
, I talked about two-factor authentication, and what problems might be in its implementation. Unfortunately, the time of speech for a complete immersion in the topic was not enough, so I will try to reveal some details in the framework of individual posts.
And I will start with the most popular topic, namely two-factor authentication on linux - what are the configuration options and why even a very good solution is required to be modified by a file.
Customization options
As I said, the 2fa theme for linux is very popular, there are many solutions that allow you to do this.
There are two fundamental ways to make the second factor in ssh authentication. The first method is a pseudo-second factor, launching an arbitrary binary after the authorization stage using the
ForceCommand option in the sshd settings:
www.openssh.com/txt/release-4.4')
What is good about this method? There is support from openssh4.4, so you can hardly find a server where the corresponding settings cannot be enabled. This is where the advantages essentially end. The cons are much more:
- if you have tcpportforward enabled, then port forwarding can be performed to bypass existing checks in the binary;
- everything that is written in the rc file will be executed before the ForceCommand, so there you can register the execution of exec sh and not confirm the input every time by the second factor;
- when the user connects to the server via sftp or scp, he will not see any invitation that you can type in the case of standard login using the ssh client. Therefore, the convenience for the user in this case is practically reduced to nothing.
That is why we consider the second method, which is given in most instructions. Of course, this is setup using a pam module.
The second factor is through pam
The classic script is a modification of the
/etc/pam.d/sshd config
file , when the necessary module is placed at the very beginning approximately in the following format:
auth required pam_google_authenticator.so
But this does not solve the problem if the authentication is not performed at the pam level, but is performed by means of the sshd server itself, for example, key authentication. How to be in this case?
Authentication Methods
Fortunately, in openssh since version 6.2, the native support of the second factor has been made. Now, using the
AuthenticationMethods option, you can list the authentication methods that must be successfully passed to log in to the server:
lwn.net/Articles/544640An example of this configuration:
AuthenticationMethods publickey,password hostbased,publickey
What is written here? For successful authentication, you need to go through one of the following combinations:
- publickey + password
- or hostbased + publickey
But where to connect the module for two-factor authentication? It connects in the keyboard-interactive method. That is, if you want after authentication via publickey, confirmation via the same google auth module is required, then set the following in the sshd config:
AuthenticationMethods publickey,keyboard-interactive
and in the
pam.d / sshd file
you specify:
auth required pam_google_authenticator.so
It's pretty simple. But exactly as long as you do not want to enable several authentication methods, if one method is executed at the level of sshd itself (publickey or kerberos), and the other method is at the level of pam (same password). The problem is that both password and keyboard-interactive are processed in the same pam-config. It is necessary to somehow learn how to separate the first stage of authentication from the second in the case of such a sshd config:
AuthenticationMethods password,keyboard-interactive publickey,keyboard-interactive
I have been looking for a solution to this problem for a long time, and came across a description of how facebook did the second factor:
www.slideshare.net/yandex/004-tim-tickelchadgreene2facwww.youtube.com/watch?v=pY4FBGI7bHMWhen the security guys from facebook told about the introduction of 2fa in their place, they referred to authentication methods -
Authentication Submethods . This allows you to limit authentication to a given device. As a result, my colleagues managed to specify authentication for keyboard-interactive via duo:
lwn.net/Articles/544640 (comment from dugsong)
But attempts to find these commits or the necessary version of openssh - 6.2p1 were unsuccessful. Therefore, it was decided to study the problems further.
Experiments with setting up pam
Here we remember again what the pam stack is and with what options you can connect the module. Everyone remembers the standard connection options in the auth - required, requisite, optional, optional section.
But experiments on combining modules with these options alone did not lead to anything. Either the user will be asked for the password even in the case of key authentication, or it will be possible to bypass the second factor by re-entering the password.
And then we start customizing pam more subtly. For each resulting status, you can indicate your influence on the stack, incl. “Skip” one or more plugins.
For example, in this way.
auth [success=1 default=ignore] pam_radius_auth.so
When setting up two-factor through duosecurity, we made exactly this pam configuration, as it was noted that if the user cannot be interactive, the module returns just
PAM_ABORT . That is, authentication begins to look like this:
AuthenticationMethods gssapi-with-mic,keyboard-interactive password,keyboard-interactive
And the pam.d / sshd config becomes like this:
auth [success=2 abort=ignore default=1] /lib64/security/pam_duo.so auth [success=1 default=ignore] pam_unix.so nullok_secure auth requisite pam_deny.so auth required pam_permit.so
Consider what happens in the first possible authentication option - gssapi-with-mic, keyboard-interactive
The user logs in on the kerberos-ticket, then you need to go through keyboard-interactive authentication. The pam_duo module is successfully connected, if successful, the transition to
PAM_PERMIT follows , in all other variations - to
PAM_DENY . It's pretty simple.
What will happen if the input is a password. It performs the same stack of pam modules, but pam_duo cannot be initialized and
PAM_ABORT is returned. Since we have
abort = ignore , it doesn’t affect anything, control is passed to the next pam_unix module. If everything is good, then there is a transition to the second stage, keyboard-interactive, and the above described mechanics are repeated.
Yes, everything seems to be ok. But there is a nuance.
pam_duo allows you to specify additional settings - for whom authentication is required, and for whom - not.
duo.com/docs/duounix#duo-configuration-optionsHere is an example of this configuration:
groups = users,!wheel,!*admin
And what did we find out as a result of testing? That if the user, according to an example, is in the wheel group, the module will return
PAM_SUCCESS , which is quite logical. But this will return before attempting to initialize the module, that is, even in the password stage. Thus, knowing the username of such a user, you can not just bypass the second factor, but log in to the system without even knowing the user's password. In general, a complete failure.
We also find the second feature. If you change the settings not pam.d / sshd, but global ones - password-auth, which is usually connected in other modules, it turns out that login through the local console occurs with the ability to connect modules with interactive interaction. That is, the first stage of verification is skipped and the second factor is immediately checked, where, as we remember, you can simply know the name of the account with groups of exceptions. This is also a failure.
File completion
But there is no going back, so we start reading the source code.
The main file in which all the checks go:
github.com/duosecurity/duo_unix/blob/master/pam_duo/pam_duo.cWe are interested in the
pam_sm_authenticate function. As we study the sources, we find that we can call a function for interaction between the module and the application that calls the module (in our case it will be sshd):
man7.org/linux/man-pages/man3/pam_get_item.3.htmlman7.org/linux/man-pages/man3/pam_conv.3.htmlAfter about 5 segfault (during testing and connecting the corrected module) we find out which of the parameters will differ in the case of password and keyboard-interactive. Testing was carried out through an attempt to display a space using the specified functions. The pointer to
resp in the
pam_conv structure will be 0 if it
succeeds .
As a result, we obtain a modified source code that performs two functions — returns
PAM_AUTHINFO_UNAVAIL when the module is connected in the password stage. And returns
PAM_AUTHINFO_UNAVAIL , if the service name is different from sshd (to avoid the above situation with the input via the local console):
gist.github.com/videns/5348e3cc04fbce3a8c26fe3c99a61b50/revisionsWell, for ease of installation on all servers, we assemble a package, for example, using the same fpm.
After installing the modified module, you need to make the latest changes to pam.d / sshd:
auth [success=2 authinfo_unavail=ignore default=1] /lib64/security/pam_duo.so auth [success=1 default=ignore] pam_unix.so nullok_secure auth requisite pam_deny.so auth required pam_permit.so
As a result, if the module is connected not at the keyboard-interactive stage, then the response
PAM_AUTHINFO_UNAVAIL will be returned, which is processed in the stack. In all other cases there will be a transition to pam_deny or pam_permit if successful.
Also, for those accounts that are used in services and must therefore enter one factor at a time, you can make separate settings using the
Match parameter in the sshd config.
Summing up, I would say that even a very good solution often requires a little refinement of the file, so that for users it does not become like this:

It is also ready to share more information about two-factor authentication, for example, which rakes were at the time of integration or describe interesting features of the duosecurity provider.