Single sign-on with SSSD, LDAP, and Kerberos
Cached Tickets
When a user logs in to a Linux system, the Name Service Switch (NSS) is responsible for finding the user account in the user database. Traditionally, the user database is the /etc/passwd
file. If the account exists, the PAM subsystem authenticates the user, for example, by comparing the password entered by the user with its counterpart in the /etc/shadow
file. This approach quickly reaches its limits, especially if a large number of user accounts exist. In this example, the accounts would need to be created on every single machine because the user and password databases only exist on the local filesystem.
Objects All Around
The problem of user account management can easily be solved using a centralized directory service, such as NIS or LDAP, in which the user credentials are not stored locally but on a central server. Systems that can access the central server can thus query the server when a user tries to log in. Because NIS isn't exactly renowned for providing a secure environment, I'll be using the LDAP directory service in this article. In contrast to NIS, LDAP uses a hierarchical tree structure to store information in attributes.
If you combine these attributes, objects are the result: user objects, for example. A schema defines which information (attributes) can be used together.
An /etc/dirsrv/schema/10rfc2307.ldif
schema file on the 389 directory server defines the required attributes for RFC 2307- and 2307bis-compliant user objects. The object class from which user accounts are derived is called posixAccount
(Listing 1).
Listing 1: Definition of posixAccount
# grep -i posixaccount /etc/dirsrv/schema/10rfc2307.ldif objectClasses: ( 1.3.6.1.1.1.2.0 NAME 'posixAccount' DESC 'Standard LDAP objectclass' SUP top AUXILIARY MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory ) MAY ( userPassword $ loginShell $ gecos $ description ) X-ORIGIN 'RFC 2307' )
All required user attributes, like the UID, login name, and primary user group, are listed here after the MUST
tag, whereas optional attributes are listed after the MAY
tag. A similar object class exists for group objects and is named posixGroup
(Listing 2).
Listing 2: Definition of posixGroup
# grep -i posixgroup /etc/dirsrv/schema/10rfc2307.ldif objectClasses: ( 1.3.6.1.1.1.2.2 NAME 'posixGroup' DESC 'Standard LDAP objectclass' SUP top STRUCTURAL MUST ( cn $ gidNumber ) MAY ( userPassword $ memberUid $ description ) X-ORIGIN 'RFC 2307' )
A detailed description on how the directory server works is beyond the scope of this article, but you can read about in other places [1] if you are interested in more details.
For this article, I assume that an LDAP server with user accounts already exists. Which product you use here doesn't really matter, as long as it complies with the two RFCs I mentioned previously.
Of course, you could store the user password in the LDAP user object. After all, the posixAccount
does specifically define a userPassword
attribute for this purpose. However, this would obviously cause a security issue: When a user logs in, the password is transmitted in the clear to the LDAP server. If you don't use TLS encryption between the client and the server, anybody could sniff the cleartext password off the wire.
Instead of using TLS to secure communications between the systems involved, the user password should not be transmitted across the wire in the first place, which means saving the password at a location other than on the LDAP server. Incorrectly configured access rules (ACLs) for the password attribute would also mean considerable risk. Fortunately, the Kerberos authentication protocol provides a secure alternative.
Three-Headed Dog
Instead of user passwords, Kerberos transmits tickets across the wire. The tickets are initially exchanged between all the objects involved. This approach offers the advantage of never needing to transmit the password across the network, and tickets provide a single sign-on (SSO) option for users. In other words, once a user has authenticated against the Kerberos server, authentication against any other Kerberos-based service, say an LDAP server, is handled transparently, and users don't need to type their passwords again.
The protocol requires a Kerberos server, also known as a Key Distribution Center (KDC). The KDC contains a database with the passwords of every object involved in the exchange – that is, for users, network services, and machines. All of these are generically referred to as Kerberos principals. The passwords belonging to the principals are governed by a ruleset that specifies how complex they have to be and what their validity period is. Apart from these details, the server doesn't store any user or system information; all of that is stored at a different location, such as on a directory server.
The initial Kerberos login is very simple. The client opens a connection to a Kerberos server (KDC). This process either occurs transparently for the user via the login
program or relies on kinit
. The KDC comprises two components: an Authentication Server (AS) and a Ticket-Granting Server (TGS). The Authentication Server receives the client query and checks its namespace (realm) for the requested username (user principal). If the principal occurs in the Kerberos database, the AS generates a random session key and a Ticket-Granting Ticket (TGT). The TGT contains a variety of information, including the client name and associated IP address, a validity date, a timestamp, and the session key.
Kerberos encodes the TGT with a key that only the Authentication Server and the TGS know. In combination with the session key, the ticket is now sent to the client – not in the clear, of course, but encoded with a key generated from the client's password. After the client receives the AS response (the encoded TGT and session key), the user is prompted to enter her password. The password is converted into a key, which is then used to decode the TGTs that just arrived. The client saves the TGT in its credential cache and deletes the password the user entered from memory. Thanks to the TGT, the user can now prove her identity for the validity period of the ticket without needing to authenticate again by typing the password.
The user's identity has thus been verified by the TGT on the workstation side. If the user wants to access another network service, such as the LDAP server referred to earlier, the TGT needs to request another ticket from the KDC, but from the Ticket-Granting Server this time. This service ticket (ST) is issued for precisely the server for which the user requested it – in other words, the server needs to support access to the Generic Security Services API.
Requesting a service ticket is more complex. The client sends a request to the TGS. The request comprises the name of the server the client wants to access, an authenticator, and the stored TGT.
The authenticator comprises the client name, its IP address, and a timestamp (the current time on the client). The encrypted TGT is sent along with the authenticator to the Ticket-Granting Server. The authenticator is also encoded with the session key that the client received along with the TGT. The Ticket-Granting Server decodes the authenticator and the TGT and compares their content, the IP address by which the request was issued, and the current time. If everything matches, a new session key is generated for use by the client and the server that it wanted to access (e.g., the LDAP server) in future. This new session key is part of the service ticket that the Ticket-Granting Server issues, encrypts (with the TGT's own session key), and returns to the requesting client.
The game now starts again from scratch. The client receives the service ticket and passes it on to the required server (LDAP) to prove its identity. In addition to the service ticket, another authenticator is generated and sent to the server. If the ST and authenticator information match, the client is validated and thus authenticated without needing to authenticate against the server by providing a username and password.
The authenticator provides protection against an attacker sniffing a service ticket off the network and replaying it to a server to gain access later. An attack of this kind is known as a replay attack.
To ensure that the client is correctly authenticated, the time on all of the machines involved in the exchange must be set correctly. The easiest way to set the time is to use the Network Time Protocol (NTP).
Figure 1 again shows the procedure for a Kerberos session:
1. The client requests a TGT from the AS.
2. The AS issues this for the TGS and sends it back to the client.
3. The client requests a service ticket and sends the TGT to the TGS.
4. The TGS sends the ST back to the client.
5. The client sends the ST to the target server and authenticates it.
6. The target server authenticates the client.
The Kerberos Server
The Kerberos Server owns the database in which the principals are stored and thus needs to be very secure. You should never run any other services on the same machine. Principals exist for both users and Kerberos-based services and hosts. A principal has the following generic structure: primary/instance@REALM
, where instance
is optional and only used for grouping. For example, a user principal could look like this:
tscherf/admin@TUXGEEK.DE
An example of an LDAP server is:
ldap/tiffy.tuxgeek.de@TUXGEEK.DE
The realm holds all the principals in a certain zone and corresponds to the uppercase DNS domain name. Along with the principals, the user and service passwords are also stored in the database.
The server is fairly easy to configure. To do so, you enter the name of your Kerberos realm in /etc/krb5.conf
and issue the kdb5_util create
command to create the database in the /var/kerberos/krb5kdc
directory.
The database is managed either locally with the kadmin.local
tool or remotely with kadmin
. This setup assumes that the KAdmin service is enabled on the KDC, and you also need a valid admin principal in the /var/kerberos/krb5kdc/kadm5.acl
file.
When one of the management tools is called, it can issue an add_principal
to add a new principal to the database – for example, add_principal -pw password tscherf
.
The procedure is similar for a service or a workstation: add_principal -randkey ldap/ldap.tuxgeek.de
or add_principal -randkey host/grobi.tuxgeek.de
. The service principals must be known on the corresponding servers.
To allow this process to happen, you need to generate the service password from the Kerberos database by running the following command:
ktadd -k /etc/krb5.keytab host/tiffy.tuxgeek.de
Then you securely copy the /etc/krb5.keytab
file to the computer hosting the service, with scp
, for example. Then you need to specify this keytab file along with the service password in the configuration file for each Kerberos service. Kerberos services typically search for a file with the default name of /etc/krb5.keytab
. After you launch the KDC by typing service krb5kdc start
, the service is ready for operations.
To make sure the LDAP server accesses the Kerberos database to authenticate users, you will need to create a principal for it on the Kerberos server and copy it to the server. You can issue the following command:
kadmin.local: add_principal -randkey ldap/tiffy.tuxgeek.de kadmin.local: ktadd -k /tmp/ds.keytab ldap/tiffy.tuxgeek.de
After copying the /tmp/ds.key
to the /etc/dirsrv
directory, you also need to tell the server the path to the file:
echo "KRB5_KTNAME=/etc/dirsrv/ ds.keytab; export KRB5_KTNAME" >> /etc/sysconfig/dirsrv
After rebooting the server, users can now use Kerberos to log in to the server (Listing 3). They need a matching TGT to do so, of course. Without this current configuration, they would need to request this manually from the KDC with kinit
. When it accesses a Kerberos service, each client requests a service ticket for the requested service in the background. The service ticket is then used for authentication.
Listing 3: Kerberos Session Establishment
01 # kinit tscherf 02 Password for tscherf@TUXGEEK.DE: 03 04 # klist -5 05 Ticket cache: FILE:/tmp/krb5cc_500 06 Default principal: tscherf@TUXGEEK.DE 07 08 Valid starting Expires Service principal 09 08/06/10 15:41:50 08/07/10 15:41:50 krbtgt/TUXGEEK.DE@TUXGEEK.DE 10 renew until 08/06/10 15:41:50 11 12 # ldapsearch -LLL -h localhost -b "dc=tuxgeek,dc=de" uid=tscherf 13 SASL/GSSAPI authentication started 14 SASL username: tscherf@TUXGEEK.DE 15 SASL SSF: 56 16 SASL data security layer installed. 17 dn: uid=TScherf,ou=People,dc=tuxgeek, dc=de 18 givenName: Thorsten 19 sn: Scherf 20 loginShell: /bin/bash 21 uidNumber: 9999 22 gidNumber: 9999 23 objectClass: top 24 objectClass: person 25 objectClass: organizationalPerson 26 objectClass: inetorgperson 27 objectClass: posixAccount 28 uid: tscherf 29 cn: Thorsten Scherf 30 homeDirectory: /home/tscherf 31 32 # klist -5 33 Ticket cache: FILE:/tmp/krb5cc_500 34 Default principal: tscherf@TUXGEEK.DE 35 36 Valid starting Expires Service principal 37 08/06/10 15:41:50 08/07/10 15:41:50 krbtgt/TUXGEEK.DE@TUXGEEK.DE 38 renew until 08/06/10 15:41:50 39 08/06/10 15:43:20 08/07/10 15:41:50 ldap/tiffy.tuxgeek.de@TUXGEEK.DE 40 renew until 08/06/10 15:41:50
Depending on the directory server you use, you need to make sure that the mapping between the Kerberos principal for the user and the matching distinguished name (DN) on the LDAP server is correct. A plugin exists to handle this on the 389 Directory Server.
To allow a user to log in to a workstation using LDAP and Kerberos, you need to modify the configuration files for the PAM and NSS subsystems. PAM configuration files are typically in the /etc/pam.d
directory, and NSS uses the /etc/nsswitch.conf
file.
The system-config-authentication
tool is useful for configuring these files (Figure 2), and it also takes care of the required configuration changes.From now on, the PAM pam_krb5.so
library will validate the user's password. The Name Service Switch uses libnss_ldap.so
to query the user data on the LDAP server. When a user logs in to the system, a Kerberos TGT is issued, which can be used later for password-free access to other Kerberos services.
If the computer is always connected with the network, the procedure I have looked at thus far will work without any problems; any client can connect to any server (Figure 3). Problems begin when one of the two server systems – or, in the worst case, both systems – is unreachable, which can happen if the workstation happens to be a laptop.
While connected to the enterprise network, a user can log in with his company account, but if the user is on the road, he will not be able to use this account. For precisely this reason, many users create a second, local account for mobile use of their devices. However, the fairly recent System Security Services Daemon (SSSD) removes the need for this step.
FreeIPA
SSSD is part of the FreeIPA Project [2] but also exists as a separate tool. Fedora 11 had an early version of it on board, and the current versions are now used. Users on other distributions can install the service using the package manager or download the source code for the tool directly from the project homepage [3].
SSSD provides various functions, three of which are really interesting. First, the tool solves the offline authentication issue for users. SSSD keeps the credentials for a centralized server in a local cache to do so. When a user logs in to an enterprise network with a company account on his laptop, the credentials are automatically stored in the SSSD cache. You can configure the retention period in a centralized configuration file.
Second, SSSD supports queries to multiple LDAP or NIS servers. Thus, you can query a number of different user databases. From a performance point of view, using the new daemon offers some obvious advantages. Instead of needing to set up a connection to query each LDAP server, now only a single socket from SSSD to the LDAP server is required.
Third, the daemon has its own NSS and PAM interfaces for requesting client systems (Figure 4). On the back end, security providers handle access to the corresponding identity or authentication servers. If they are unreachable, the cache is checked for existing credentials.
Again, the easiest way to configure the service is to use the system-config-authentication
tool (Figure 5). FreeIPA is required as the user database, and a Kerberos server is required for authentication. The PAM configuration file (Listing 4) now uses pam_sss.so
instead of pam_krb5.so
. This tool also handles access to the PAM interface in SSSD. To make sure that the NSS docks with the interface intended for it, the configuration tool creates an sssd
entry for the user database in /etc/nsswitch.conf
.
Listing 4: PAM Configuration for SSSD
01 /etc/pam.d/system-auth 02 auth required pam_env.so 03 auth sufficient pam_unix.so nullok try_first_pass 04 auth requisite pam_succeed_if.so uid >= 500 quiet 05 auth sufficient pam_sss.so use_first_pass 06 auth required pam_deny.so 07 08 account required pam_unix.so broken_shadow 09 account sufficient pam_localuser.so 10 account sufficient pam_succeed_if.so uid < 500 quiet 11 account [default=bad success=ok user_unknown=ignore] pam_sss.so 12 account required pam_permit.so 13 14 password requisite pam_cracklib.so try_first_pass retry=3 15 password sufficient pam_unix.so md5 shadow nullok try_first_pass use_authtok 16 password sufficient pam_sss.so use_authtok 17 password required pam_deny.so 18 19 session optional pam_keyinit.so revoke 20 session required pam_limits.so 21 session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid 22 session required pam_unix.so 23 session optional pam_sss.so
In the /etc/sssd/sssd.conf
configuration database for SSSD, you will see the individual sections for NSS and PAM (Listing 5). The global sssd
contains the settings for the service itself. The domains
keyword controls access to multiple user databases, which are then defined by configuration options. The man page man 5 sssd.conf
provides a comprehensive overview of the available configuration options.
Listing 5: SSSD Configuration File
01 [sssd] 02 config_file_version = 2 03 reconnection_retries = 3 04 sbus_timeout = 30 05 services = nss, pam 06 domains = tuxgeek 07 08 [nss] 09 filter_groups = root 10 filter_users = root 11 reconnection_retries = 3 12 13 [pam] 14 reconnection_retries = 3 15 16 [domain/tuxgeek] 17 auth_provider = krb5 18 cache_credentials = True 19 ldap_id_use_start_tls = False 20 debug_level = 0 21 enumerate = True 22 krb5_kpasswd = tiffy.tuxgeek.de 23 ldap_schema = rfc2307bis 24 ldap_search_base = dc=tuxgeek,dc=de 25 krb5_realm = TUXGEEK.DE 26 chpass_provider = krb5 27 id_provider = ldap 28 ldap_uri = ldap://127.0.0.1/ 29 krb5_kdcip = tiffy.tuxgeek.de 30 ldap_tls_cacertdir = /etc/openldap/cacerts
Conclusions
The use of the System Security Services Daemon offers huge benefits, especially for mobile users. Instead of working with different accounts, users can work with a single account. In offline mode, user credentials are supplied from the cache. This function is also useful in data centers to help bridge the gap cause by a temporary failure of an LDAP or Kerberos server. Compared with the Name Service Cache Daemon, SSSD offers far more granular management of the cache entries, and (in the default configuration) the cache entries will not become invalid while a user is offline.