Seize fine-grained control of when and from where your users can access your system.
Traditional Unix authentication doesn’t provide much granularity in limiting a user’s ability to log in. For example, how would you limit the hosts that users can come from when logging into your servers? Your first thought might be to set up TCP wrappers or possibly firewall rules [Hack #44].
But what if you want to allow some users to log in from a specific host, but disallow others from logging in from it? Or what if you want to prevent some users from logging in at certain times of the day because of daily maintenance, but allow others (e.g., administrators) to log in at any time they wish? To get this working with every service that might be running on your system, you would traditionally have to patch each of them to support this new functionality. This is where pluggable authentication modules (PAM) enters the picture.
PAM allows for just this sort of functionality (and more) without the need to patch all of your services. PAM has been available for quite some time under Linux, FreeBSD, and Solaris and is now a standard component of the traditional authentication facilities on these platforms. Many services that need to use some sort of authentication now support PAM.
Modules are configured for services in a stack, with the authentication process proceeding from top to bottom as the access checks complete successfully. You can build a custom stack for any service by creating a file in /etc/pam.d with the same name as the service. If you need even more granularity, you can include an entire stack of modules by using the pam_stack
module. This allows you to specify another external file containing a stack. If a service does not have its own configuration file in /etc/pam.d, it will default to using the stack specified in /etc/pam.d/other.
There are several types of entries available when configuring a service for use with PAM. These types allow you to specify whether a module provides authentication, access control, password change control, or session setup and teardown. Right now, we are interested in only one of the types: the account type. This entry type allows you to specify modules that will control access to accounts that have been authenticated.
In addition to the service-specific configuration files, some modules have extended configuration information that can be specified in files within the /etc/security directory. For this hack, we’ll mainly use two of the most useful modules of this type: pam_access
and pam_time
.
The
pam_access
module allows you to limit where a user or group of users may log in from. To make use of it, you’ll first need to configure the service with which you want to use the module. You can do this by editing the service’s PAM config file in /etc/pam.d.
Here’s an example of what /etc/pam.d/login might look like:
#%PAM-1.0 auth required pam_securetty.so auth required pam_stack.so service=system-auth auth required pam_nologin.so account required pam_stack.so service=system-auth password required pam_stack.so service=system-auth session required pam_stack.so service=system-auth session optional pam_console.so
Notice the use of the pam_stack
module; it includes the stack contained within the
system-auth file. Let’s see what’s inside /etc/pam.d/system-auth:
#%PAM-1.0 # This file is auto-generated. # User changes will be destroyed the next time authconfig is run. auth required /lib/security/$ISA/pam_env.so auth sufficient /lib/security/$ISA/pam_unix.so likeauth nullok auth required /lib/security/$ISA/pam_deny.so account required /lib/security/$ISA/pam_unix.so password required /lib/security/$ISA/pam_cracklib.so retry=3 type= password sufficient /lib/security/$ISA/pam_unix.so nullok use_authtok md5 shadow password required /lib/security/$ISA/pam_deny.so session required /lib/security/$ISA/pam_limits.so session required /lib/security/$ISA/pam_unix.so
To add the pam_access
module to the login service, you could add another account entry to the login configuration file, which would, of course, just enable the module for the login service. Alternatively, you could add the module to the system-auth file, which would enable it for most of the PAM-aware services on the system.
To add pam_access
to the login service (or any other service, for that matter), simply add a line like this to the service’s configuration file after any preexisting account entries:
account required pam_access.so
Now that you’ve enabled the pam_access
module for our services, you can edit /etc/security/access.conf to control how the module behaves. Each entry in the file can specify multiple users, groups, and hostnames to which the entry applies, and specify whether it’s allowing or disallowing remote or local access. When pam_access
is invoked by an entry in a service configuration file, it looks through the lines of access.conf and stops at the first match it finds. Thus, if you want to create default entries to fall back on, you’ll want to put the more specific entries first, with the general entries following them.
The general form of an entry in access.conf is:
permission
:users
:origins
where permission
can be either +
or -
. This denotes whether the rule grants or denies access, respectively.
The
users
portion allows you to specify a list of users or groups, separated by whitespace. In addition to simply listing users in this portion of the entry, you can use the form user
@
host
, where host
is the local hostname of the machine being logged into. This allows you to use a single configuration file across multiple machines, but still specify rules pertaining to specific machines.
The
origins
portion is compared against the origin of the access attempt. Hostnames can be used for remote origins, and the special LOCAL
keyword can be used for local access. Instead of explicitly specifying users, groups, or origins, you can also use the ALL
and EXCEPT
keywords to perform set operations on any of the lists.
Here’s a simple example of locking out the user andrew (Eep! That’s me!) from a host named colossus
:
- : andrew : colossus
Note that if a group that shares its name with a user is specified, the module will interpret the rule as applying to both the user and the group.
Now that we’ve covered how to limit where a user may log in from and how to set up a PAM module, let’s take a look at how to limit what time a user may log in by using the pam_time
module. To configure this module, you need to edit /etc/security/time.conf. The format for the entries in this file is a little more flexible than that of access.conf, thanks to the availability of the NOT (!
), AND (&
), and OR (|
) operators.
The general form for an entry in time.conf is:
services
;devices
;users
;times
The services
portion of the entry specifies what PAM-enabled service will be regulated. You can usually get a full list of the available services by looking at the contents of your /etc/pam.d directory.
For instance, here are the contents of /etc/pam.d on a Red Hat Linux system:
$ ls -1 /etc/pam.d
authconfig
chfn
chsh
halt
internet-druid
kbdrate
login
neat
other
passwd
poweroff
ppp
reboot
redhat-config-mouse
redhat-config-network
redhat-config-network-cmd
redhat-config-network-druid
rhn_register
setup
smtp
sshd
su
sudo
system-auth
up2date
up2date-config
up2date-nox
vlock
To set up pam_time
for use with any of these services, you’ll need to add a line like this to the file in /etc/pam.d that corresponds to the service you want to regulate:
account required /lib/security/$ISA/pam_time.so
The devices
portion specifies the terminal device from which the service is being accessed. For console logins, you can use !ttyp*
, which specifies all TTY devices except for pseudo-TTYs. If you want the entry to affect only remote logins, use ttyp*
. You can restrict it to all users (console, remote, and X11) by using tty*
.
For the users
portion of the entry, you can specify a single user or a list of users, separated with
|
characters.
Finally, the times
portion is used to specify the times when the rule will apply. Again, you can stipulate a single time range or multiple ranges, separated with |
characters. Each time range is specified by a combination of one or more two-character abbreviations denoting the day or days that the rule will apply, followed by a range of hours for those days.
The abbreviations for the days of the week are Mo
, Tu
, We
, Th
, Fr
, Sa
, and Su
. For convenience, you can use Wk
to specify weekdays, Wd
to specify the weekend, or Al
to specify every day of the week. If using the latter three abbreviations, bear in mind that repeated days will be subtracted from the set of days to which the rule applies (e.g., WkSu
would effectively be just Sa
). The range of hours is simply specified as two 24-hour times, minus the colons, separated by a dash (e.g., 0630-1345
is 6:30 A.M. to 1:45 P.M.).
If you wanted to disallow access to the user andrew from the local console on weekends and during the week after hours, you could use an entry like this:
system-auth;!ttyp*;andrew;Wk1700-0800|Wd0000-2400
Or perhaps you want to limit remote logins through SSH during a system maintenance window lasting from 7 P.M. Friday to 7 A.M. Saturday, but you want to allow a sysadmin to log in:
sshd;ttyp*;!andrew;Fr1900-0700
As you can see, there’s a lot of flexibility for creating entries, thanks to the logical Boolean operators that are available. Just make sure that you remember to configure the service file in /etc/pam.d for use with pam_time
when you create entries in /etc/security/time.conf.