Docs/SysAdmin/Server/Disconnected Auth

From Mandriva Community Wiki

Jump to: navigation, search
Disconnected Authentication and Authorization

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.
Personal tools