Chapter 6. User Management

This one can log in,
this other can get email;
never give out root.

While computer intrusions over the Internet make headlines, a system administrator’s greatest security threats often come from a system’s own users. Maybe they won’t ship your data to a crime syndicate, but disgruntled and incompetent users will crash your servers given the chance—sometimes out of malice, but more often out of ignorance. Think about security as the combination of confidentiality, integrity, and availability, and it will immediately become clear how users with unrestricted system access can damage security.

Despite what you might have learned from the Bastard Operator from Hell, the system exists for the users, and proper management of those users’ accounts is absolutely necessary. In this chapter, we’ll cover one of the systems administrator’s most common tasks: managing users by adding, removing, configuring, and modifying user accounts.

In recent years, there has been a trend toward using the privileged root account for everyday tasks on systems that have only a single user.[13] Using a privileged account to read your email and browse the Web increases your risks from both user errors and malicious attacks. While a careless keystroke by a regular user will generate only a permission denied error, that same keystroke by root might render your system unusable and destroy all your data. Even if you’re the only person using your OpenBSD system, you must use an unprivileged user account for day-to-day tasks.

If an intruder compromises an unprivileged account, the potential damage is limited only by that user’s permissions. If the compromised account handles your email and web bookmarks, you might suffer only personal embarrassment. But if that account handles system administration tasks, your intruder can inflict unlimited system damage and send you scurrying for your backup. Using a regular account for day-to-day tasks means that you can take extra steps to restrict the root account.

Perform all tasks with the minimum level of privilege necessary. If you don’t need root access to perform a task, don’t use it! For example, OpenBSD’s web server runs as the specific user www, rather than as root. If an intruder breaks into your web server and gains access your system as the www user, he can damage only the files the www user has permission to write to. Likewise, if the web server software goes into an error state and starts deleting files at random, this same principle limits the files it can delete. The least privilege approach protects the system from both intruders and its own software.

Operating systems that give every user privileged access have more problems as a result. Virus effectiveness, unexpected misconfiguration, and even crashes can be traced back to unnecessary privileged access. OpenBSD might be the most secure operating system in the world, but all those fancy security features can’t protect you from poor system administration practices.

Using the root account for routine tasks also creates bad habits. People under pressure perform what they practice. If you use root on your desktop for routine work, you’ll need to fight your habits to perform routine tasks when you work on a production server. This sort of sloppiness inevitably breeds security problems. Even on my OpenBSD desktop, where I’m the only user, I do everything as a regular user specifically to develop and maintain good sysadmin habits.

Adding Users

OpenBSD uses many of the standard UNIX user- and password-management programs, such as passwd(1) and vipw(8). It also includes a friendly interactive user-creation program, adduser(8). We’ll cover adduser first, and then look at some of the more advanced tools.

Adding Users Interactively

Only the root user can run adduser. If you start adduser at the command line without specifying any options, it drops you into a friendly interactive dialog where you can create new users.

The first time adduser runs, it asks a series of questions to determine its default settings. It saves these default settings, but you can change the defaults later.

# adduser
Couldn't find /etc/adduser.conf: creating a new adduser configuration file
Reading /etc/shells
Enter your default shell: csh ksh nologin sh tcsh [ksh]: 1
Your default shell is: ksh -> /bin/ksh
Default login class: authpf bgpd daemon default staff
[default]: 2
Enter your default HOME partition: [/home]: 3
Copy dotfiles from: /etc/skel no [/etc/skel]: 4
Send welcome message?: /path/file default no [no]: 5
Do not send message(s)
Prompt for passwords by default (y/n) [y]: 6
Default encryption method for passwords: auto blowfish des md5 old
[auto]: 7

adduser first asks for your preferred default shell. It reads /etc/shells to see all the shells installed on your system. Though I’ve long used tcsh, I usually start new users with the OpenBSD standard of ksh 1. That way, they have a shell that more closely resembles what is used by the rest of the world, and they quickly learn that I cannot answer questions about their shell.

Next, adduser asks for your default login class. I’ll cover login classes later in this chapter. For now, assign new users to the default login class at 2.

If you have a default OpenBSD installation, your user home directories are on the /home partition. If not, specify the default home directory at 3.

User accounts need configuration dotfiles (.shrc, .login, .profile, and so on). If you have a directory containing customized dotfiles, tell adduser about it at 4. Otherwise, just accept the default.

Though OpenBSD doesn’t include a welcome message by default, you can put one on the system so new users will have an email waiting for them on their first login. Give adduser the full path to the file containing your welcome message at 5.

Depending on how you create user accounts, you might want to provide a password when you create the user account. Accounts created without passwords are disabled until a password is assigned. If you won’t be assigning passwords when creating accounts, you can tell adduser not to prompt you for them at 6.

Finally, you can choose the encryption algorithm used to hash user passwords, which are stored in /etc/master.passwd. Unless you have specific interoperability needs or otherwise know what you’re doing, accept the default at 7.

From now on, adduser will use these chosen defaults. If you want to modify the defaults later on, change them in /etc/adduser.conf. Read the adduser(8) man page for a complete list of configuration file options.

Now that you’ve set your default options, run adduser again to create user accounts.

Start by assigning a username. Many people are irrationally attached to particular usernames, and it’s polite to ask them if they have a preference.

Ok, let's go.
Don't worry about mistakes. There will be a chance later to correct any input.
Enter username []: pkdick

Once you have a username, you’ll get a chance to enter the user’s real name or the account’s intended purpose.

Enter full name []: Phil Dick

The shell you specify is a matter of user preference. The list of shells is taken from /etc/shells, with the addition of the nologin option. Users can change their shell unless you specifically prevent that, so don’t worry too much about which shell you assign.

Enter shell csh ksh nologin sh tcsh [ksh]:

Next, choose a user ID (UID) number. By default, UID numbering starts at 1000, and adduser uses the lowest available number. You can change this if needed to match some local standard.

Uid [1001]:

By default, new users are assigned to a group with the same name as their username. Each user can be assigned to only a single login group (or primary group), but you can assign user accounts to multiple secondary groups if needed. If you want this user to be able to use the root account, invite the user to the wheel group. Other common groups include staff, users, and operator.

Login group pkdick [pkdick]:
Login group is ``pkdick''. Invite pkdick into other groups: guest no
[no]: wheel

Choose a login class for the user. If you don’t understand login classes yet, accept the default. I recommend assigning administrative users—for example, those in the wheel group—to the staff class. If you’re a desktop user, you want to be in the staff login class.

Login class authpf bgpd daemon default staff [default]: staff

If you set adduser to ask for passwords, it will ask you for a password, and then ask again to confirm.

Enter password []:
Enter password again []:

Now adduser displays everything you selected.

Name:        pkdick
Password:    ****
Fullname:    Phil Dick
Uid:         1001
Gid:         1001 (pkdick)
Groups:      pkdick wheel
Login Class: staff
HOME:        /home/pkdick
Shell:       /bin/ksh
OK? (y/n) [y]: y

Either accept or reject the user at this point. If you accept, adduser will create the new user and ask if you want to create another user.

If you need to create many users, you probably don’t want to spend your day looping through adduser dialogs. If you have scripts, cron jobs, or web interfaces that add user accounts, you’ll want to create users noninteractively. adduser’s -batch flag enables this. When you use batch mode, adduser takes four additional arguments: the username, the groups the username belongs to, the full name, and the password in encrypted format.

# adduser -batch username group 'Real Name' encryptedpassword

To create our user pkdick in batch mode, we would run this:

# adduser -batch pkdick wheel 'Phil Dick' NotThePassword

One thing to note here is that pkdick’s password is not NotThePassword. adduser expects us to provide a random salt that hashes to the string NotThePassword, not the password itself. For instructions on how to generate encrypted passwords, see Passwords and Batch Mode.

If you actually follow any of the previous examples, you’ll create an account with no known password. Modern Unix-like operating systems don’t store passwords in readable format; instead, passwords are stored as a hash of the password and a random salt. When you assign a password to a user, the system takes the password, adds the salt, and performs some horrible computations to generate a hash of the password. The system then stores that hash and salt in the /etc/master.passwd file. When you attempt to log in, the login process takes your password, adds the salt, and computes the hash of that combination. If the computed hash matches what’s stored in /etc/master.passwd, the login is permitted.

The examples create an account with a password hash of NotThePassword. Because this isn’t a legitimate hash, no entered password will match it. We need to provide a pregenerated encrypted password, enter an unencrypted password, and let adduser calculate the hash for us, or create an account without a password.

Creating a new account without a password is the simplest option. OpenBSD will disable the account until you assign a password to it, but this is acceptable for accounts used to run daemons, or if you have a help desk staff to assist new users in setting passwords. To create an account without a password, simply omit the password from the account-creation process.

# adduser -batch pkdick wheel 'Phil Dick'

If you want to enter an unencrypted password on the command line, use the -unencrypted option. Put this option before the -batch option. For example, to give Phil’s account the password IsThePassword, enter the following:

# adduser -unencrypted -batch pkdick wheel 'Phil Dick' IsThePassword

This account now has a password of IsThePassword. You might use this inside a script or when no one is around to look over your shoulder. The password will appear in the system’s process list, however, so any users on the system can see the password if they’re quick enough to notice.

Another option is to generate a prehashed password using encrypt(1). By default, encrypt gives you a blank line. When you enter a word, it returns the hash of that word. It defaults to using the encryption algorithm defined in the default login class. (For the past several years, this has been Blowfish.) You can enter any number of words, and each will be hashed separately. Press CTRL-C to exit encrypt.

# encrypt
gerbil
$2a$06$V/VO91VVAKSNslesQNH6pezXsGhoKUMcnvWxyDOJUmWRk3fflX5cy
weasel
$2a$06$652ngShUnOBuFEL7X2yrf.E0U2GUw/FseVq/BkVgaiyqvp4wt.Nsy
^C
#

If you’re encrypting only one password or creating passwords interactively, give the -p option to encrypt. This gives you a non-echoing password prompt.

# encrypt -p
Enter string:
$2a$06$nyA.mygoei/6VGS2tq4wA.VOzB6inwlK9pWOIAsiUWBkWf0CqOJ7.
#

I frequently create administrator accounts with one set of standards and unprivileged accounts with another. I create sysadmin accounts by hand using adduser in interactive mode (I don’t create sysadmin accounts very often). Someone else creates unprivileged user accounts using an adduser batch mode script I wrote. adduser.conf contains the default settings for sysadmins, which I then override in the script. This approach requires less of my organic memory and ensures that unprivileged accounts are consistent.

All of these options must appear on the command line before the -batch argument. adduser treats everything after -batch as account information.

The -noconfig option tells adduser to not read defaults from adduser.conf. Using this option in a script guarantees that sysadmin-friendly defaults in adduser.conf don’t leak into unprivileged accounts.

The -dotdir option specifies a directory for user dotfiles. All files in this directory are copied to the new user’s home directory. I often have special dotfiles for unprivileged users.

The -home option tells adduser where to create the new user’s home directory. This is not the actual home directory, but the base directory where the home directory will be created. For example, if all of your web server customers have home directories on the /www partition, you might use -home /www.

To assign a nondefault login class, use the -class option.

The -message option gives a path to the new user message. To turn off a default of sending a message, use -message no.

To assign a shell, use -shell and the shell name as it appears in /etc/shells, or nologin to disable logins.

Perhaps you want to assign your batch-created users UIDs in a specific range. Maybe all of your customers have a UID above 10000, while sysadmins have a UID in the thousands. Specify a minimum UID with -uid_start and a maximum with -uid_end. If available, the login group created will be given a GID equal to the UID.

Removing unneeded user accounts is just as important as adding new ones. Use rmuser(8) to delete accounts.

# rmuser pkdick
Matching password entry:
pkdick:*:1001:1001::0:0:phil dick:/home/pkdick:/bin/ksh
Is this the entry you wish to remove? y
Remove user's home directory (/home/pkdick)? y
Updating password file, updating databases, done.
Updating group file: Removing group pkdick -- personal group is empty
 done.
Removing user's home directory (/home/pkdick): done.

The rmuser command displays the account entry from /etc/passwd, giving you a chance to verify that you really want to delete this particular user. Read the account’s real name, and verify that you’re deleting the correct account. Next, rmuser asks if you want to delete the user’s home directory. If you suspect that you might need some files from that user account, you could choose to keep the directory around for a while. It automatically deletes the user’s cron jobs and incoming mail file.

You create users with privileges based on the knowledge you have at the time. The information you have is probably wrong, so get comfortable with editing users. In most cases, chpass(1) does everything you need in a user-friendly way.

Users can edit their own accounts by running chpass without any arguments.

$ chpass
$ Changing user database information for mwlucas.
Shell: /usr/local/bin/tcsh
Full Name: mwlucas
Office Location:
Office Phone:
Home Phone:

Here, users can update their shell or change their directory information. Many applications ignore the directory information (phone numbers and office location) stored in /etc/passwd, but in some places, it’s important. Make changes, save, and exit.

If you run chpass as root, giving a username as an argument, you get a very different picture.

# chpass mwlucas
# Changing user database information for mwlucas.
Login: mwlucas
Encrypted password: $2a$08$s2EVX.cAhYHskOaHk/4C5eLn76atAmGPU7z5DqRKAYe/V.OGgWXVi
Uid [#]: 1000
Gid [# or name]: 1000
Change [month day year]:
Expire [month day year]:
Class: staff
Home directory: /home/mwlucas
Shell: /usr/local/bin/tcsh
Full Name: mwlucas
Office Location:
Office Phone:
Home Phone:

Here, you can forcibly change the user’s password (although there are better ways to do this), shell, UID, password expiration, and so on, in addition to all of the user’s directory information.

Changes made through chpass affect only /etc/passwd, /etc/master.passwd, and /etc/group. If you change a user’s UID, GID, or home directory, you must also make the corresponding changes to the files the user owns and his home directory; otherwise, the user’s account won’t work correctly. If /etc/passwd lists your home directory as /newhome/mwlucas in /etc/passwd, but your files are in /home/mwlucas, you’ll have trouble on your hands.

Note that you can’t edit /etc/master.passwd or /etc/passwd with just any text editor; you need to use tools that manage the corresponding password databases. If you insist on editing the password file by hand, you can use vipw(8) to directly edit /etc/passwd. If you’re not familiar with vipw, stick with chpass. The most common use for vipw is when the password file is damaged, and the most common way someone damages the password file is by using vipw.

A user’s shell can be used to limit what a user can do, but OpenBSD provides very specific access controls with login classes. Login classes, set in /etc/login.conf, define the resources and information accessible to users. Login classes also let you control password length and expiration times, as well as external authentication mechanisms.

Each user is assigned to a class, and each class places limits on available resources. When you change the limits on a class, the new limits are applied to each user the next time the user logs in. Define a user’s class when creating the account, or change it with chpass.

By default, login.conf offers two classes for users, one class for daemons, and a few special-case classes. The default user class gives the user wide-ranging access to system resources and is suitable for machines with a limited number of shell users. The staff user class gives the user no restrictions on memory use, sets very high limits on the number of processes a user can run concurrently, and allows the user to log in even when logins are forbidden.

If these two classes meet your needs, and if you won’t be using an alternative authentication protocol like Remote Authentication Dial In User Service (RADIUS) or Kerberos, you can skip this section. If not, read on.

Each class definition consists of a series of variable assignments describing the class’s resource limits, authentication, and environment. Each variable assignment in the class definition begins and ends with a colon. The backslash character indicates that the class continues on the next line, which makes the file more readable.

Here’s the definition of the default class:

  default:\
1      :path=/usr/bin /bin /usr/sbin /sbin /usr/X11R6/bin /usr/local/bin /usr/local/sbin:\
2      :umask=022:\
3      :datasize-max=512M:\
       :datasize-cur=512M:\
       :maxproc-max=256:\
       :maxproc-cur=128:\
       :openfiles-cur=512:\
       :stacksize-cur=4M:\
       :localcipher=blowfish,6:\
       :ypcipher=old:\
4      :tc=auth-defaults:\
       :tc=auth-ftp-defaults:

The default class has several variables. Some of these have fairly obvious interpretations. For example, the path variable at 1 assigns a default command search path to the user’s shell, usually visible to the user as $PATH. The umask setting at 2 assigns a default umask to the user’s shell. The user can override both of these.

Other settings, such as datasize-max and maxproc-max at 3, are harder to define by guesswork. We’ll go through some of the more commonly used values in the next section.

Similar in behavior to the termcap tc variables at 4 used to configure serial console clients in Chapter 5, the default class copies settings from the entries auth-defaults and auth-ftp-defaults elsewhere in login.conf.

Some variables don’t require a value to trigger behavior; these values trigger a specified behavior simply by adding them to login.conf. For example, the presence of requirehome means that the user must have a valid home directory to log in.

Changing login.conf

On many BSD systems, you must transform the login.conf file to a program-friendly database file, login.conf.db, with cap_mkdb(8). OpenBSD doesn’t require this. Programs that check login classes first look for the login class database, and if they don’t find it, they directly parse login.conf. You can use cap_mkdb to create such a database, which will very slightly improve the performance of software that checks login.conf.

# cap_mkdb /etc/login.conf

Note that once you create this database, you must rebuild it every time you edit login.conf. Database values in login.conf.db will always override your login.conf settings. Alternatively, you can remove login.conf.db and force programs to always parse login.conf.

I recommend skipping cap_mkdb on modern hardware.

Resource limits allow you to control the amount of system resources any one user can monopolize at any one time. If several hundred users are logged in to one machine, and one user decides to compile LibreOffice, that person will consume far more than his fair share of processor time, memory, and I/O. By limiting the resources any one user can use, you can make the system more responsive for all users.

Resource limits were more commonly used back when computing facilities were very expensive and departments received bills for the amount of computing time they used. These days, utilization accounting isn’t so important. It’s generally cheaper to buy more computing power than it is to configure accounting or resource limits. That said, if you have a buggy daemon that sometimes leaks and starts to soak up CPU time or memory, giving it a login class can prevent it from devouring the system.

Table 6-1 lists some resource-limiting login.conf variables.

Resource limits are generally set per process. If you permit each process 200MB of RAM and allow 40 processes per user, you’ve just allocated each user 8GB of memory. Perhaps your system has a lot of memory, but does it really have that much?

All resource-limiting variables except vmemoryuse support maximum and current (advisory) limits. Users are warned by the system when they exceed current limits and cannot exceed the maximum limits. This works well on a cooperative system, where multiple users share resources but need to be notified when they are approaching their limit.

To specify a current limit, add -cur to the variable name. To make a maximum limit, add -max. For example, to set a current and maximum limit on the number of processes a user can have, use this definition in the class:

…
:maxproc-cur: 50:\
:maxproc-max: 60:\
…

A user in this class will receive a warning when he uses more than 50 processes and will not be able to use more than 60 processes. If you do not specify a limit as current or maximum, it acts as both.

Unlike the user environment, which can be configured in several different places, many password controls can be configured only via the user class. The password controls affect only the local password database, not Lightweight Directory Access Protocol (LDAP), Kerberos, RADIUS, or other remote password databases.

Let’s have a look at some commonly used password controls.

OpenBSD supports many different authentication mechanisms, such as the local password file, Kerberos, S/Key, RADIUS, and so on. Specify the authentication method desired in the user class definition, and OpenBSD will use it. This system behind this is called BSD Authentication.

Setting an authentication mechanism does not configure the authentication mechanism. For example, configuring a login class to authenticate via Kerberos doesn’t magically establish a Kerberos domain. If the specified authentication method is unavailable, classes configured to use that method will be unable to log in.

Not all authentication methods interoperate with all protocols. For example, while SSH works with physical tokens, it doesn’t work with the lchpass authentication protocol, which allows users to change their password but disallows logins. Review the man page for each authentication method for details.

Some authentication methods require additional configuration. For example, if you want to use RADIUS authentication, you must tell your system where to find your RADIUS server. The special login.conf variables and their use are documented in the authentication method’s man page.

Table 6-3 lists the authentication methods supported by OpenBSD’s built-in BSD Authentication.

The ports collection (discussed in Chapter 13) contains a few additional login methods, such as fingerprint scanners (sysutils/login_fingerprint), OATH one-time passwords (sysutils/login_oath), and LDAP integration (sysutils/login_ldap). You can also create your own custom authentication methods; see login.conf(5) for details.

Set the authentication method using the auth variable in login.conf:

:auth=token,passwd:\

Users in a class with this set try to authenticate via an X9.9 token. If that’s not possible, the system falls back on the local password database.

BSD Authentication supports different authentication methods for different daemons. You can specify a service name after the auth keyword, indicating that this set of authentication methods applies to only that particular service. You’ll frequently see login classes like auth-ssh and auth-su.

Here are a couple of sample entries from the default login.conf file:

# Default allowed authentication styles
auth-defaults:auth=passwd,skey:
# Default allowed authentication styles for authentication type ftp
auth-ftp-defaults:auth-ftp=passwd:

This defines the class auth-defaults, with only one entry. By default, users in this class first use password authentication, and then S/Key authentication. The auth-ftp-defaults class defines auth-ftp as using the password database, and only the password database.

Earlier in this chapter, I mentioned that the default class included two other classes. These are the auth-defaults and auth-ftp-defaults classes. Every other login class in the default login.conf file includes them by reference. If you change the authentication methods used by the auth-defaults class, that change will apply to every other login class.

I have a long-running love/hate relationship with RADIUS. It’s the lowest common denominator of authentication protocols. Just about every operating system and hardware device supports it, but it’s a finicky protocol with innumerable edge cases. Luckily, configuring OpenBSD as a RADIUS client is simple. Any RADIUS server can provide authentication services for OpenBSD.

I encourage you to use another login service, such as LDAP or Kerberos, rather than RADIUS. But in certain cases, for certain users, RADIUS is adequate. RADIUS combined with Microsoft’s Internet Authentication Service gives you easy password synchronization with the local Windows domain and reduces your support load.

First, read login_radius(8), and then configure your RADIUS server to permit access from your OpenBSD host. To configure RADIUS authentication, you need the RADIUS server’s IP address, the port RADIUS runs on, and a shared secret. (For historical reasons, it’s best to specify the RADIUS port explicitly rather than relying on /etc/services.) In our example, the RADIUS server is 192.0.2.2, the port is 1812, and the secret is the string Insubordination88.

First, create a directory to hold the server configuration file and set its permissions appropriately, as per login_radius(8).

# mkdir /etc/raddb
# chgrp _radius /etc/raddb/
# chmod 755 /etc/raddb/

Now create the file /etc/raddb/servers. This file should contain a server and its secret, each on one line. Our servers file has only one line:

192.0.2.2	Insubordination88

Now change login.conf to use RADIUS by default.

auth-defaults:\
        :auth=radius:\
        :radius-port=1812:\
        :radius-server=192.0.2.2:

The auth-defaults class is OpenBSD’s default authentication class. If we change it, we change how every other class authenticates. We set the auth type to radius, and set the port and the server.

Immediately upon saving the file, OpenBSD will try to authenticate all user accounts against the RADIUS server. You might want to change the auth-ftp class to match.[14]

Until you confirm everything is working, keep an SSH session logged in as root so you can change login.conf. Otherwise, you might lock yourself out of the system, or at least out of the root account. If you can’t get into the system, you’ll need to reboot into single-user mode and edit login.conf.

Changing the authentication scheme for all users might not be desirable, either. You might want authpf(8) users to authenticate against RADIUS, but have users in the staff class authenticate against the local password database. Perhaps you don’t want your root account to authenticate via RADIUS, so you need an auth-su login class that points at the local password database. Using login classes, you can configure user authentication to fit your specific needs.

Unprivileged User Accounts

An unprivileged user account is a user account with no privileges to any programs or files. Many programs run as unprivileged users or use unprivileged users to perform specific duties. These unprivileged users get only the rights needed to perform a limited task.

“Only the rights needed to perform a limited task” sounds like every user account, doesn’t it? That’s true, but the account used by the least privileged human being still has more rights than many programs need. Any user with shell access usually has a home directory. Users can create files in their home directory, run text editors, process email, run scripts, and compile (if not install) software. An average shell user needs these minimal privileges, but programs do not. By having a program run as a very restricted user, you control the amount of damage the software or intruders can do to the system.

OpenBSD includes several unprivileged users out of the box. Take a look at /etc/passwd, and you’ll see accounts like sshd, named, _ntp, and so on. These are all unprivileged accounts used by specific server daemons. Examine them, and you’ll find several common characteristics.

Unprivileged users do not have normal home directories. Most share the home directory of /var/empty, which is owned by root and contains nothing except a logging socket. Having a home directory the user cannot write to makes the account less flexible, but is good enough for most server daemons. If these users do own files on the system, file permissions are usually set so that the user cannot write to them.

Similarly, no one should ever log in to the system with these accounts. If the named user account is reserved for the DNS subsystem, why would anyone actually need to log in as that account? Unprivileged users are assigned a shell that specifically forbids logging in: /sbin/nologin.

How does all this enhance system security? Let’s pick on the web server, a common intrusion vector, as an example. OpenBSD runs its web server as the user www. Suppose an intruder discovers a security flaw in your website and can use this to make the web server execute arbitrary code. This is a security nightmare; our intruder can now make the server program do absolutely anything within its power. But what, exactly, is within the web server’s power?

A command prompt permits much more mischief and mayhem than a website, so the intruder will probably try to access a command prompt on the system. The www user has a shell that specifically disallows a command prompt. While this doesn’t categorically prevent the intruder from getting a command prompt, it does make it much more difficult.

But our intruder is clever. Through really excellent intrusion skills, he makes the web server open a high-numbered port that dumps clients into a root shell. He now has access to a command prompt and can wreak untold damage … or can he?

He has no home directory, and no permissions to create one. Any files he wants to store must go into a globally accessible directory such as /tmp or /var/tmp, increasing his visibility. The web server configuration file is not owned by the www user. Even if the intruder has a path into the web server, he cannot reconfigure it. He can’t change the website files, as the www user doesn’t own them. The www user doesn’t have access to anything on the system, actually. Additionally, OpenBSD’s built-in web server chroots itself. Having broken into the web server program, the intruder now must escape the chroot and penetrate a privileged program.

Can he penetrate your system? Possibly, but it will be much more difficult. If he is specifically targeting you or your company, he might go to the trouble. If he is just looking for easy meat, however, he will probably give up and go bother someone running a Linux or Windows system.

Using unprivileged users doesn’t solve all security problems, mind you. The compromised www user can view web application source files. If your application is badly written or has database passwords hardcoded into hidden files, you’re still in trouble. But if you don’t use poorly written applications and you’ve kept your system updated and patched, the intruder will have a very hard time penetrating the rest of your server.

The first unprivileged account was nobody. It was created for use by the Network File System (NFS, discussed in Chapter 9) to map files owned by root on foreign systems. Decades ago, people started using nobody as a generic unprivileged user, running web servers, proxy servers, and other daemons as nobody. While this was better than running those programs as root, it’s still poor practice. If an intruder penetrated one of those programs, he would gain access to all processes owned by nobody. Our hypothetical web server intruder would suddenly have access not only to the web server, but also to the database, NFS, or anything else running as nobody!

Every daemon that needs to run as a user needs its own unprivileged accounts—the whole point of using unprivileged users is to minimize the damage one piece of software can inflict. Use them liberally. OpenBSD provides discrete unprivileged users for services as small as finger(1) and the audio system. Follow this example.

_username

If you take a look at /etc/passwd, you’ll see that many unprivileged users have an underscore before their name, such as _syslogd, _ldapd, and _dhcp. This is an OpenBSD convention for identifying unprivileged users. Most add-on software also uses unprivileged usernames beginning with an underscore, such as _mysql and _postgresql.

Not all unprivileged usernames start with an underscore, however. Some of these are legacy users that OpenBSD retains for compatibility reasons, such as nobody. Others have a long history or support inflexible software, and changing them would be more annoyance than it’s worth.

The presence of an underscore means that a user is unprivileged. The absence of an underscore means nothing; the user might be a normal account or it might be unprivileged. If you create your own unprivileged users, you don’t need to include a leading underscore, but doing so will help other system administrators understand what the user does.



[13] This probably leaked through from the Microsoft culture, where for many years, every user had administrative access.

[14] Or you might not want to make this change. FTP transmits passwords in clear text, so you might want to use a separate password source for FTP connections. Why transmit passwords securely over one protocol, while transmitting them insecurely on a neighboring port?