Single Sign-On and the Corporate Directory, Part IV
Welcome back for the last article on using your single sign-on and corporate directory infrastructure. What we've covered so far is how to set up the infrastructure and how to plug various types of clients running different operating systems in to that infrastructure. The majority of that work benefits your users in enabling them to sign on only once, after which they can use a variety of resources, such as storage shares, printers, e-mail and more.
This month, we cover methods to use Kerberos and LDAP to make your job easier. As always, the sample programs and other files are available from the on-line Resources.
No matter what size your shop is, printing is always a necessity. Unfortunately, printing also can be one of the most error-prone processes as well, especially in a heterogeneous environment. Luckily, the CUPS (Common UNIX Printing System) Project has been established. The goals of CUPS are to provide a standards-based printing solution and to provide unified printing for UNIX-based systems. Today, it's the default printing system for most Linux distributions as well as Apple OS X.
Setting up a basic print server using CUPS is simple. To get a basic understanding of CUPS and a working install, you should read Colin Topliss' article “Centralized Printing Using CUPS” (see Resources). Make sure to configure the CUPS server on one of your Samba servers if you want to enable Windows clients to print. We cover how to do this shortly.
The first thing to do after setting up your print server is enabling encryption using SSL. You should create a certificate signing request, or CSR, and sign it with your CA. Save the certificate and key in /etc/ssl/cups as cups-cert.pem and cups-key.pem, respectively. Also, make sure that they're owned by the user that cupsd uses, usually lp, and that the permissions are set properly on the private key, 0400. Next, make some changes to /etc/cups/cupsd.conf:
ServerCertficate /etc/ssl/cups/cups-cert.pem ServerKey /etc/ssl/cups/cups-key.pem
Save your changes and restart cupsd.
One of the most difficult tasks of print management is keeping track of all the printers to manage. Gone are the days when all the printers existed in a central location. Now printers, more than likely, are scattered around the office, attached to people's workstations, and whatnot. Worse, the fact that these printers are different makes and models increases the challenge of organizing them.
The CUPS Web site (see Resources) states that LDAP support is scheduled for inclusion in version 1.3. Version 1.1 is currently stable, and 1.2 has been in testing for some time now. However, this needn't stop us from using LDAP as a way to inventory all the printers in the office and even provide a way to automate printer addition to a CUPS server.
The IETF has been thinking about this and has developed RFC 3712, “Lightweight Directory Access Protocol (LDAP): Schema for Printer Services”. I've taken the liberty of converting this RFC into an actual schema for use with OpenLDAP and included it in the on-line Resources. Include this schema in your slapd.conf file and restart slapd. Now we can add information about our printers:
dn: ou=printers,o=ci,dc=example,dc=com objectClass: organizationalUnit ou=printers dn: printer-name=pr-laser,ou=printers,o=ci,dc=example,dc=com objectClass: top objectClass: printerAbstract objectClass: printerService objectClass: printerIPP printer-name: pr-laser printer-location: A101 printer-info: laserjet.ppd printer-more-info: http://www.hp.com printer-make-and-model: HP LaserJet printer-uri: socket://pr-laser.example.com
Most of these are self-explanatory, but printer-info and printer-uri might need a little explaining. We use the printer-info attribute to specify the PostScript printer definition, PPD, to use for this printer, in this case laserjet.ppd. The printer-uri attribute is used to define the URI to contact the printer. The socket:// device is usually used for HP JetDirect connections to a printer. To find all the devices your print server supports, use the lpinfo command, which is usually kept in /usr/sbin:
# /usr/sbin/lpinfo -v network socket direct hal network http network ipp network lpd direct scsi serial serial:/dev/ttyS0?baud=115200 direct usb:/dev/usb/lp0 network smb
Your output may vary depending on what options were enabled when CUPS was compiled.
You now have a printer listed in LDAP, but what can you do with it as CUPS doesn't support LDAP? At the very least, you have a central place for keeping track of all your printers and their capabilities. I've written a small Perl script that queries LDAP for all the printers in the directory, and then creates a script that can be used to add all the printers to a CUPS server. It doesn't do much, but it gives you a start on how you can use LDAP to supplement CUPS and make management a bit more tractable. If you decide to use some of the attributes, such as printer-sides-supported, printer-finishings-supported and printer-media-supported, you could easily extend the script to call lpoptions to set printer-specific settings automatically as well.
One of the great things about CUPS is that the default settings for it allow it to discover other CUPS servers and the printers served by them. That means for Linux and OS X clients to use your new server, it's as easy as starting cupsd, waiting about 30 seconds, and you're off and running. Luckily, it's not much harder to get Windows clients up and running either, using Samba. Following are the required changes to the smb.conf file:
[global] ... load printers = Yes printing = cups printcap = cups printer admin = root [printers] comment = All Printers path = /var/spool/samba browseable = no public = yes guest ok = yes writable = no printable = yes [print$] comment = Printer Drivers path = /etc/samba/drivers browseable = yes guest ok = no read only = yes write list = root
The parameters in the global section enable CUPS printing support. The printers section makes all the printers listed in the printcap file automatically available to Windows clients. The print$ section turns on automatic driver download, or Point 'n' Print, for Windows clients. What this means is that Windows clients won't be required to install print drivers for each printer they install. When they initially connect to the printer, clients will download and install a set of generic CUPS print drivers, removing the need for the user or the administrator to worry about Windows print drivers. Save your changes and restart Samba.
Before Point 'n' Print is a reality, there are still a few more things to do. First, you should download the most recent stable CUPS drivers, version 1.1.16 as of this writing, from the Easy Software Products FTP server to your CUPS/Samba server. Untar the bundle and run the install script, cups-samba.install. If the installer puts the cups.hlp file in /usr/share/drivers, move it into /usr/share/cups/drivers with the rest of the drivers. Next, make sure the print driver share directory, /etc/samba/drivers, exists. Finally, you need to add the drivers to the Samba share. If you've removed the root Samba user out of LDAP, you'll need to re-add it for these next two steps, as you need to be a uid 0 user. Refer to Part III of this series [February 2006] if you're not sure how to do this:
# smbclient //localhost/print\$ -Uroot -c 'mkdir ↪W32X86; put /etc/cups/ppd/pr-laser.ppd ↪W32X86/pr-laser.ppd; put ↪/usr/share/cups/drivers/cupsdrvr.dll ↪W32X86/cupsdrvr.dll; put ↪/usr/share/cups/drivers/cupsui.dll ↪W32X86/cupsui.dll; put ↪/usr/share/cups/drivers/cups.hlp ↪W32X86/cups.hlp' # rpcclient localhost -Uroot -c 'adddriver ↪"Windows NT x86" "pr-laser:cupsdrvr.dll ↪:pr-laser.ppd:cupsui.dll:cups.hlp:NULL:RAW:NULL"'
These two commands refer specifically to the printer we added to LDAP above, pr-laser. You need to run these two commands for each printer served by your CUPS server that you want Windows clients to access. Adding these commands to the printer creation script might be a good idea if you have many printers.
Now, if you browse to your Samba/CUPS server from a Windows client, you'll see a Printers and Faxes share. If you choose that share, you'll see all the printers served via CUPS. If you right-click on a printer and choose Connect..., it automatically downloads and installs the drivers and connects to the printer, making it available to print from that client. That's it!
Up until now, LDAP administration has been done by hand-editing LDIF files and using the command-line OpenLDAP tools. Craig Swanson and Matt Lung give some excellent pointers in their “OpenLDAP Everywhere Revisited” article (see Resources) to some GUI utilities for managing LDAP, but they overlooked one that I think needs mentioning, GQ. Although GQ is not in active development, the 1.0 beta1 version has proved to be stable and extremely useful. If GQ keeps segfaulting, though, you probably need to apply a patch to util.c (see Resources). One of the great things about GQ is its support of SASL authentication. This allows us to make modifications to LDAP using the GUI. In addition, I've found that browsing the schema has shown me object classes and attributes I probably would never have found otherwise.
If you've been a sysadmin for more than five minutes, you know the power of scripting common tasks. LDAP administration can be rather wordy, so being able to script those common tasks is invaluable. Both Perl and Python have very powerful LDAP modules. You've already been introduced to the Perl interface from last month's article's smb-create-password.pl and smb-new-machine.pl, but Python's LDAP modules are just as useful. Perl also has interfaces for Kerberos and SASL. Instead of going into an API description of each of these modules, I'm going to show you how to use them while also showing you new and different ways to use LDAP and Kerberos.
Included in the Resources is an OpenSSH schema. One of the first uses of this schema you might think of is keeping public keys of hosts in one location, a kind of known_hosts directory. In fact, that is why this schema was created. Future versions of OpenSSH will be able to use name service switch, NSS, to look up host keys instead of always requiring a local file containing them all. This is great because you'll no longer need to push and pull known_hosts files when hosts are added or removed, but unless you've patched your versions of OpenSSH, it's not that useful yet.
At the Computation Institute, we have a large cluster that many outside collaborators use. The cluster has its own network home directories, so it doesn't mount the central NFS ones. We also didn't want password-based logins where passwords are transmitted over the wire. Normally, this would be a fine time to enforce GSSAPI-based authentication, except we don't have control over the collaborator's desktop. So, I asked a colleague of mine to write a script to automate creating the user's home directory and adding a user's SSH key to her .authorized_keys file if she provided one. Because Python is his language of choice, the mkhomedirs.py script was born.
Here's how it works. When users are granted access to the cluster, they are put into the cluster-users netgroup, which is also served from LDAP. The mkhomedirs.py script, run every hour from cron, checks the list of current users in the cluster-users netgroup to see which ones don't have home directories. When it finds a user without a home directory, it creates one and copies over necessary files, such as those from /etc/skel. Once the user provides an SSH key, the key is added to the user's sshPublicKey attribute in LDAP. The mkhomedirs.py script also checks to see which users don't have a ~/.ssh/authorized_keys file. If a user doesn't have that file and has a key in LDAP, it creates the file and adds the key to it, allowing the user to log in. This script doesn't impose the restriction that a user's authorized_keys file must contain only those keys that are in LDAP, but it would be trivial to add that functionality. A common trick in a cracker's toolbox is to add his or her SSH key to another user's authorized_keys file. If you require all keys in a user's authorized_keys file to be in the directory, you can send off warnings when an unknown key has been added to a user's authorized_keys file.
Another way you might use LDAP is to create iptables rules for your hosts automatically. We achieve this by enumerating all the services for a host and all the networks that are allowed to access that service in LDAP:
dn: cn=login,ou=hosts,o=ci,dc=example,dc=com objectClass: top objectClass: ipHost cn: login ipHostNumber: 192.168.1.2 dn: cn=sshd,cn=login,ou=hosts,o=ci,dc=example,dc=com objectClass: top objectClass: ipService cn: sshd ipServicePort: 22 ipServiceProtocol: tcp ipServiceProtocol: udp description: SSH Daemon dn: cn=all-local,cn=sshd,cn=login,ou=hosts,o=ci, ↪dc=example,dc=com objectClass: top objectClass: ipNetwork cn: all-local ipNetworkNumber: 192.168.1.0 ipNetmaskNumber: 255.255.255.0 description: Local Network
Next, we need something that will traverse all the hosts and give us iptables rules for each one. In the on-line Resources, I've provided a script I've written, create-iptables.sh, which does exactly that. It depends on several Perl modules to which I've provided links in the Resources. What it does, briefly, is copy a prefix file for each host that has some rules that apply for all hosts and sets up the chains we use in the script. Next, it makes sure that all the IPs the host uses are allowed to connect back to the host. It then traverses the services, opening holes for those networks listed for each service. Finally, it appends the default rule set, which is to drop all packets. All the scripts are written to the directory iptables-scripts, and all previous scripts are saved to iptables-backups. You should create these directories before running the script. These scripts can then be pushed out to the proper hosts and run to keep host rules up to date.
You could easily modify this script to generate other pieces, such as /etc/hosts.allow and network device ACLs for added security. Another use for this type of directory structure is to generate custom scans for nmap or nessus to eliminate false positives.
The last example I've included is generating a dhcpd.conf file for your DHCP server. This script requires that the hosts in LDAP be members of both the ipHost and ieee802Device object classes and have their macAddress and ipHostNumber attributes assigned. It's not a very sophisticated script, in that it won't make sure that a host's IP is valid. It also won't handle a host that has multiple IPs or multiple subnets served by the same DHCP server.
There is a patch for ISC's DHCP server to add support for getting information directly out of LDAP, but I prefer to wait for patches to be vetted and included in the main distribution before use on production servers. For those who are curious, I've included a link in the Resources.
Many more applications are including LDAP support directly, or there are patches available. There is an LDAP sdb back end for BIND 9 for storing zone info in LDAP, and sudo has the ability to get sudoers information from LDAP. However, remember, if there's something you want to do with LDAP for your organization that requires new attributes or object classes, you can contact IANA to be assigned your own OID for use.
Up until now, we've been dealing with extending the use of LDAP, but there are more ways we can make use of Kerberos as well. One important piece in your organization for which you might want single sign-on enabled is authenticating for Web resources. Many modern browsers, such as IE 6.0, Mozilla, Firefox and Safari, already (or can be made to) support GSSAPI negotiation. To make use of this, you can install and enable the Apache mod_auth_kerb module. It can negotiate ticket-based authentication for single sign-on or present the user with a traditional user name/password box and authenticate the user to the KDC.
One other extension of Kerberos has come in the form of NFSv4. Version 4 of NFS has included stronger security as part of the protocol. It has ACL support and can use a user's Kerberos credentials for access and rights. The CITI group at the University of Michigan is spearheading the Linux implementation of NFSv4 and has links to all the patches you'll need for the user-space tools. Recent 2.6 kernels come with support for NFSv4 and rpcsec_gss, but some distributions don't enable the support by default. The necessary packages can be emerged on Gentoo systems, and the support is fully there in Red Hat Enterprise Linux 4.
We've come a long way in this series of articles. You should have a scalable directory and single sign-on environment. We've gone over how to integrate heterogeneous clients into the infrastructure. Lastly, we've covered how you, the sysadmin, can more easily manage and leverage your LDAP and Kerberos environments.
This work was supported by the Mathematical, Information, and Computational Sciences Division subprogram of the Office of Advanced Scientific Computing Research, Office of Science, U.S. Department of Energy, under Contract W-31-109-ENG-38.4:08. Thanks to Justin Binns for the mkhomedirs.py script.
Resources for this article: /article/8749.
Ti Leggett (email@example.com) is a systems administrator for the Futures Laboratory of the Mathematics and Computer Science Division at Argonne National Laboratory. He also has a joint appointment with the Computation Institute at the University of Chicago.