Docs/SysAdmin/Server/Disconnected Auth
From Mandriva Community Wiki
Collected hints and tips for setting a client up for disconnected authentication and perhaps even authorization. I hope this document can be used by administrators and authors of wizard-like tools to configure system authentication.
Contents |
[edit] Introduction
It's very nice and cool to have a central server authenticating and authorizing users in a network. But sometimes this server is offline, or unreachable or the user is just a notebook user at home where he can not reach the corporate network.
The situation we will address here is the one of a roaming user, i.e., a notebook user who goes home with the machine at the end of the day or is travelling with it. How does he/she login away from the corporate network? It surely doesn't make sense preventing the login of this user just because the machine is disconnected.
We will discuss here some of the options available to solve this problem and also present some configuration alternatives.
If you want to go directly to the sample configuration files, scroll to the bottom.
[edit] Options
We usually have two options:
- login with a local account
- login with cached credentials
Local accounts are quick and easy to setup, but not so much anymore if you consider a big corporation with many users. Before giving the notebook to the user, a local account would need to be setup for him/her, with the right password (preferably the same as the network one). This doesn't scale very well.
Cached credentials more or less solve the previous problem, because it's almost like local accounts created on the fly. Everytime a user logs in, the password that was used is stored in a local database. If the network login fails, another attempt is made against this local database.
In this document, we will proceed with the cached credentials solution, because we think this scales better and because creating local accounts is a simple task that deserves no further explanations.
[edit] How caching works
We need to cache at least two "items": the user's password and information about this user (possibly including authorization information). These will be referred to as credentials and NSS information.
The user's credentials will be cached on disk on every successfull login and will be used whenever the network login fails. The user will see a message like this, depending on the service that was used (some may have no capability to display messages back to the user):
[mary@pandora ~]$ ssh cd4 Password: You have been logged on using cached credentials. Last login: Mon Nov 27 10:33:01 2006 from pandora.conectiva [mary@dhcp208-cd4-chroot ~]$
This takes care of the authentication part. A bit trickier is the NSS piece. This information is usually stored in /etc/passwd, /etc/shadow and /etc/group local files, and controlled from /etc/nsswitch.conf. What we will do is add another source of NSS information, one that will be our cache. We do this with a new module, called nss_db, which will be used in addition to the ones already present.
The problem now is how to populate the NSS cache. A first attempt was made with a package called nss_updatedb. This package would install a cron job that would periodically fetch all NSS information from all users and store it in the local db cache. This has several drawbacks:
- no need to have information about all users in the local cache: just from the ones who have ever logged in
- potential security risk: more info stored than needed
- heavy network usage, depending on how many users are available
- in the case of LDAP it would most likely require a specific sizelimit configuration on the server
A better solution is to store only NSS information that is needed. A patch was added to the original nss_update package that makes it possible to only fetch NSS data from users that have logged in on the machine. This reduces all the drawbacks pointed out above.
There is, however, one more issue: when to fetch the NSS information and store it in the DB. Ideally, it should be done at login time, just as pam_ccreds does it. But this is not implemented yet. So, the nss_updatedb package still relies on a cron job to periodically do it. This means that if the laptop is removed from the network before this cron job had a chance to run, there may be missing entries from the cache. Perhaps in the future pam_ccreds will be able to update NSS information on demand.
[edit] Packages
These are the packages we are going to use:
- nss_db: NSS library and makedb utility, used to create a DB from /etc/passwd, /etc/shadow and /etc/group
- nss_updatedb: cron job to update the DB
- pam_ccreds: pam module to cache credentials
All have to be installed on the client.
[edit] Configuration files
The configuration files we will have to touch are these:
- /etc/nsswitch.conf: to add the new db module
- /etc/pam.d/system-auth: to incorporate pam_ccreds
- /etc/sysconfig/nss_updatedb: to configure nss_updatedb behaviour
Of all these files, the trickiets one is without doubt /etc/pam.d/system-auth, mainly because it's a bit different for every network authentication module that could be used. And details matter.
[edit] /etc/nsswitch.conf
This is how we are going to configure this file: /etc/nsswitch.conf:
passwd: files ldap [NOTFOUND=return] db group: files ldap [NOTFOUND=return] db
The [NOTFOUND=return] part means that if whatever was searched was not found in the previous module (ldap, in this case), the search stops. So our db module will not be queried in normal operation, when the ldap server is up. If, however, the ldap module fails (like in a server down or unreachable), then the search proceeds to the next module, which is our cache.
We used ldap as the network module in this example. It could be any other module, like winbind, but we will see later that winbind has its own offline mode so we don't need nss_db when using winbind.
[edit] /etc/sysconfig/nss_updatedb
This file controls how and when nss_updatedb is called. It looks like this by default:
/etc/sysconfig/nss_updatedb:
# Configuration for the nss_updatedb cron service # Set this to the name service nss_updatedb should update from # if it can't detect it correctly from /etc/nsswitch.conf #NSS_SERVICE=ldap # Set the cron interval you want to run nss_updatedb with if # the default of "hourly" is not suitable #CRONTIME=hourly # Set this variable if you want the crontab to look only for # people that has been logged on this system. #ONLY_LOGGED_USERS=yes
- *NSS_SERVICE*: set this to the network NSS module that is being used. If not set, the script will attempt to guess it by searching which module comes before the *db* one in /etc/nsswitch.conf
- *CRONTIME*: should be set to *hourly*, *daily*, *weekly*, *monthly* or *yearly*
- *ONLY_LOGGED_USERS*: new feature of the patched nss_updatedb: if set to *yes*, will only fetch NSS information from logged in users
For ldap, this is what the configuration should be (comments removed for clarity):
NSS_SERVICE=ldap CRONTIME=hourly ONLY_LOGGED_USERS=yes
[edit] /etc/pam.d/system-auth
- highlight important parts, and "pluggable" ones (i.e., where you can just plug in another network auth module)
- discuss auth and account: what is done and checked for in each phase
- pam_unix in account is tricky: it's fooled by nss_db into thinking the user is local, but nss_updatedb doesn't fetch shadow information. So we need to make pam_unix ignore it
This configuration is more complex unfortunately. PAM performs two basic tasks which are of interest to us here: authentication and authorization. The authentication part is easier and can be summarized as follows:
#%PAM-1.0 auth required pam_env.so auth sufficient pam_unix.so likeauth nullok auth [authinfo_unavail=ignore user_unknown=ignore success=1 default=2] pam_network_module use_first_pass auth [default=done] pam_ccreds.so action=validate use_first_pass auth [default=done] pam_ccreds.so action=store auth [default=bad] pam_ccreds.so action=update auth required pam_deny.so
In bold characters are the configuration changes/additions to a standard *auth* section. It shows the addition of the pam_ccreds module as well as the modification of the network module that is used. It could be pam_ldap, pam_krb5 or some other.
This stack has the following characteristics towards authentication:
- if a user is local and the password is correct, the pam_unix module succeeds and the stack terminates there
- if the pam_unix module fails, then we get to the network module which has the following flags:
- *authinfo_unavail=ignore*: to cope for when the network auth server is offline. If this happens, the module is skipped (ignored) and we get to the first pam_ccreds line
- *user_unknown=ignore*: to cope for when the network auth server is up, but its backend is not (heimdal kerberos with ldap backend, for example), which would yeld the fatal "user unknown" error. If we know we are going to use just pam_ldap, then we can drop this flag.
- *success=1*: skip the next module in the case of success, which in this case means jump to *action=store*. This means the password is correct and can be stored for future use.
- *default=2*: on all other errors, skip two modules, which ends up in *action=update* with default result "bad". The action name is a bit unfortunate, because it really means to remove the user/pass from the cache database.
Now comes the *account* section. Here is a basic template:
account [user_unknown=ignore authinfo_unavail=ignore default=done] pam_unix.so account [user_unknown=ignore authinfo_unavail=ignore default=done] pam_network_module account required pam_permit.so
The main change is that we replaced the usual last module *pam_deny.so* with *pam_permit.so*. This had to be done because there is no way around the fact that authorization information cannot be obtained when the network server is offline or unreachable, and it's too complex to cache. So, if all listed modules fail due to unavailability of information, this section gives an OK. We also make it ignore unknown users, i.e., accept them. This assumes that the previous authentication section already failed for unknown users, so there is no problem in accepting them here.
Additionally, we had to make pam_unix ignore authinfo_unavailable errors because it can be fooled by nss_db into thinking a user exists, but it will not find authorization information for this user if he/she is remote, because nss_updatedb doesn't cache shadow information. So pam_unix would search for this user in the shadow map and find nothing. Identical behaviour can be obtained by adding the *broken_shadow* parameter.
The password and session sections are simplier and there is no need for further explanation. So, here is a basic skeleton for a complete /etc/pam.d/system-auth for disconnected auth. Later we will present examples for existing services, such as OpenLDAP and Kerberos.
#%PAM-1.0 auth required pam_env.so auth sufficient pam_unix.so likeauth nullok auth [authinfo_unavail=ignore user_unknown=ignore success=1 default=2] pam_network_module use_first_pass auth [default=done] pam_ccreds.so action=validate use_first_pass auth [default=done] pam_ccreds.so action=store auth [default=bad] pam_ccreds.so action=update auth required pam_deny.so account [user_unknown=ignore authinfo_unavail=ignore default=done] pam_unix.so account [user_unknown=ignore authinfo_unavail=ignore default=done] pam_network_module account required pam_permit.so password required pam_cracklib.so retry=3 password sufficient pam_unix.so nullok use_authtok md5 shadow password sufficient pam_network_module use_authtok password required pam_deny.so session optional pam_mkhomedir.so skel=/etc/skel/ umask=0022 session optional pam_keyinit.so revoke session required pam_limits.so session required pam_unix.so session optional pam_network_module
This is what the configuration should look like in general. It has the following characteristics:
- local users can login normally, with or without the network authentication online
- if network authentication is online, network users can login normally. They will also have their credentials cached on disk by pam_ccreds for later use
- if network authentication is offline, network users will be able to login normally via pam_ccreds and the previously cached credential
- authorization data is NOT CACHED. It means that if some user was prevented from logging in due to some authorization rule, this rule would not prevent the login in offline mode because it would not be known.
[edit] Configuration for pam_krb5
The behaviour of pam_krb5 towards the availability of the servers, assuming the KDC is Heimdal with database in OpenLDAP, is:
KDC | LDAP | Result |
---|---|---|
online | online | business as usual |
online | offline | user unknown |
offline | offline | authinfo unavailable |
offline | online | authinfo unavailable |
Considering the above table, this is the suggested /etc/pam.d/system-auth file for a configuration with user authentication on a KDC and user information coming from nss_db and/or nss_ldap and/or local users:
#%PAM-1.0 auth required pam_env.so auth sufficient pam_unix.so likeauth nullok auth [authinfo_unavail=ignore user_unknown=ignore success=1 default=2] pam_krb5.so use_first_pass auth [default=done] pam_ccreds.so action=validate use_first_pass auth [default=done] pam_ccreds.so action=store auth [default=bad] pam_ccreds.so action=update auth required pam_deny.so account [user_unknown=ignore authinfo_unavail=ignore default=done] pam_unix.so account [user_unknown=ignore authinfo_unavail=ignore default=done] pam_krb5.so account required pam_permit.so password required pam_cracklib.so retry=3 password sufficient pam_unix.so nullok use_authtok md5 shadow password sufficient pam_krb5.so use_authtok password required pam_deny.so session optional pam_mkhomedir.so skel=/etc/skel/ umask=0022 session optional pam_keyinit.so revoke session required pam_limits.so session required pam_unix.so session optional pam_krb5.so
[edit] Configuration for pam_ldap
This is the suggested /etc/pam.d/system-auth file for a pam_ldap configuration. It uses the same ideas as the configuration for pam_krb5 we just presented:
#%PAM-1.0 auth required pam_env.so auth sufficient pam_unix.so likeauth nullok auth [authinfo_unavail=ignore user_unknown=ignore success=1 default=2] pam_ldap.so use_first_pass auth [default=done] pam_ccreds.so action=validate use_first_pass auth [default=done] pam_ccreds.so action=store auth [default=bad] pam_ccreds.so action=update auth required pam_deny.so account [authinfo_unavail=ignore default=done] pam_unix.so account [authinfo_unavail=ignore default=done] pam_ldap.so account required pam_permit.so password required pam_cracklib.so retry=3 password sufficient pam_unix.so nullok use_authtok md5 shadow password sufficient pam_ldap.so use_authtok password required pam_deny.so session optional pam_mkhomedir.so skel=/etc/skel/ umask=0022 session optional pam_keyinit.so revoke session required pam_limits.so session required pam_unix.so session optional pam_ldap.so
[edit] Configuration for pam_winbind
Winbind has its own offline mode which we are going to use because of some issues (see below). This makes our life actually easier.
This is the /etc/pam.d/system-auth configuration file for such a setup:
#%PAM-1.0 auth required pam_env.so auth sufficient pam_unix.so likeauth nullok auth sufficient pam_winbind.so use_first_pass cached_login auth required pam_deny.so account sufficient pam_unix.so account sufficient pam_winbind.so use_first_pass cached_login account required pam_deny.so password required pam_cracklib.so retry=3 password sufficient pam_unix.so nullok use_authtok md5 shadow password sufficient pam_winbind.so use_authtok password required pam_deny.so session optional pam_mkhomedir.so skel=/etc/skel/ umask=0022 session optional pam_keyinit.so revoke session required pam_limits.so session required pam_unix.so
We also need to change /etc/samba/smb.conf to enable offline logins:
[global] (...) winbind offline logon = yes
After this, the services need to be restarted and winbind will start to cache the passwords used in successfull logins.
Do note that nss_updatedb has to be *disabled* and the nss_db configuration removed from /etc/nsswitch.conf.
[edit] Misc issues
[edit] nss_updatedb issues
- the cron job should not spam the mailbox when the server is unavailable: it should work like vacation, only one message per day
- would be nice if the cron job could run as a non-root user
[edit] Winbind issues
- pam_winbind.so is segfaulting on CD4/MDV2007 when changing passwords (standard passwd command). The segfault is at the end, though: the password change does occur.
- pam_winbind doesn't like it when the PDC or BDC vanishes: can't yet make it ignore the error. When that happens, it returns permission denied instead of returning the real reason of the error. In that way, we can't distinguish this permission denied error from the real one. Makes it useless with pam_ccreds.
- default nss_updatedb won't work, because it tries to enumerate all users and groups which is now disabled by default in winbind. Either enabled it in /etc/samba/smb.conf (winbind enum groups and winbind enum users or set /etc/sysconfig/nss_updatedb to only fetch the logged in users).
- pam_winbind in CD4/MDV2007 is complaining that the password has expired. Seems to be this bug: https://bugzilla.samba.org/show_bug.cgi?id=3969
- pam_winbind doesn't show that the login is being done with cached credentials
- winbind's offline authentication mode is not perfect:
- need to wait for some timeout before it switched modes (offline to online, and online to offline). Don't know the exact time yet: could be 60s or 300s, or something in between.