9
SECURING YOUR SYSTEM

image

Securing your system means ensuring that your computer’s resources are used only by authorized people for authorized purposes. Even if you have no important data on your system, you still have valuable CPU time, memory, and bandwidth. Many folks who thought that their systems were too unimportant for anyone to bother breaking into found themselves an unwitting relay for an attack that disabled a major corporation. You don’t want to wake up one morning to the delightful sound of law enforcement agents kicking in your door because your insecure computer broke into a bank.

Sure, there are things worse than having some punk kid take over your servers—say, having the neighborhood loan shark break both your legs. Discovering that your organization’s web page now says, “Ha ha, you’ve been r00ted!” is a decent competitor for second place. Even more comprehensible intrusions cause huge headaches. Most of the actual intrusions I’ve been involved with (not as an attacker, but as a consultant to the victim) have originated from countries with government censorship, and traffic analysis showed that the intruders were actually just looking for unrestricted access to news sites. While I fully sympathize with these people, when I’m depending upon the stable operation of my servers to run my business, their intrusion is unacceptable.

Over the last few years, taking over remote computers has become much easier. Point-and-click programs for subverting computers can be found through search engines. When one bright attacker writes an exploit, several thousand bored teenagers with nothing better to do can download it and make life difficult for the rest of us. Even if the data on your system is worthless, you must secure the system’s resources.

Generally speaking, operating systems are not broken into; the programs running on operating systems are. Even the most paranoiac, secure-by-default operating system in the world can’t protect badly written programs from themselves. Occasionally, one of those programs can interact with the operating system in such a way as to actually compromise the operating system. The most well-known of these are buffer overflows, where an intruder’s program is dumped straight into the CPU’s execution space and the operating system runs it. FreeBSD has undergone extensive auditing to eliminate buffer overflows as well as myriad other well-understood security issues, but that’s no guarantee that they’ve been eradicated. New functions and programs appear continuously, and they can interact with older functions and each other in unexpected ways.

FreeBSD provides many tools to help you secure your system against attackers, both internal and external. While no one of these tools is sufficient, all are desirable. Treat everything you learn about system security as a tool in a kit, not as the answer to all your problems. For example, while simply raising a system’s securelevel will not make your system secure, it can help when combined with reasonable permissions, file flags, regular patching, password control, and all the other things that make up a good security policy. We’ll cover more advanced security tools in Chapter 19, but without the basic protections discussed here, those tools won’t help secure your system.

Who Is the Enemy?

We’ll arbitrarily lump potential attackers into four groups: script kiddies, disaffected users, botnets, and skilled attackers. You’ll find a more detailed classification in books dedicated to security, but that’s not what you’re here for. These categories are easily explained, easily understood, and include 99 percent of all the attackers you’re likely to encounter.

Script Kiddies

The most numerous human attackers, script kiddies, are not sysadmins. They’re not skilled. They download attack programs that work on a point-and-click basis and go looking for vulnerable systems. They’re the equivalent of purse snatchers, preying upon old ladies holding their bags just a little bit too loosely. Fortunately, script kiddies are easy to defend against: just keep your software up-to-date and follow good computing practices. Like locusts, script kiddies are easy to squash, but there are just so darned many of them!

Disaffected Users

The second group, your own users, causes the majority of security problems. Your organization’s employees are the people most likely to know where the security gaps are, to feel that the rules don’t apply to them, and to have the time to spend breaking your security. If you tell an employee that company policy forbids him access to a computer resource, and if the employee feels that he should have access to it, he’s likely to search for a way around the restriction. Anyone who feels that he’s so fabulously special that the rules don’t apply to him is a security risk. Worse, when an employee who knows all the dirty laundry gets angry, bad things can happen. You might have all your servers patched and a downright misanthropic firewall installed, but if anyone who knows the password is Current93 can dial the back room modem, you’re in trouble.

The best way to stop people like these is simply not to be sloppy. Don’t leave projects insecurely half-finished or half-documented. When someone leaves the company, disable his account, change all administrative passwords, inform all employees of that person’s departure, and remind them not to share confidential knowledge with that person. Have a computer security policy with real violation penalties and have HR enforce it. And get rid of the unsecured modem, the undocumented telnet server running on an odd port, or whatever hurried hack you put into place thinking that nobody would ever find it.

Botnets

Botnets are more numerous than either of the above, but they’re not human. They’re machines compromised by malware and controlled from a central point. Botnets can include millions of machines. The malware authors control the botnets and use them for anything from searching for more vulnerable hosts to sending spam or breaking into secure sites. Most botnets are composed of Windows and Linux machines, but there’s no reason why FreeBSD operating systems can’t be assimilated into botnets.

Fortunately, botnet defense is much like script kiddie defense; keeping your software patched and following good computing practices goes a long way.

Motivated Skilled Attackers

The most dangerous group—skilled attackers—are competent system administrators, security researchers, penetration specialists, and criminals who want access to your specific resources. Computer penetration is a lucrative criminal field these days, especially if the victim has resources that can be used for distributed denial-of-service (DDos) attacks or mass spam transmission. Compromising a web farm and turning it to evil is profitable. If you have valuable company secrets, you might be targeted by one of these intruders. If one of these people really wants to break into your network, he’ll probably get there.

Still, security measures that stop the first three groups of people change the tactics of the skilled attacker. Instead of breaking into your computers over the network, he’ll have to show up at your door dressed as a telco repairman lugging a packet sniffer, or dumpster-dive searching for old sticky notes with scribbled passwords. This dramatically increases his risk, possibly making an intrusion more trouble than it’s worth. If you can make the intruder’s break-in plan resemble a Hollywood script no matter how much he knows about your network, your security is probably pretty good.

FreeBSD Security Announcements

The best defense against any attackers is to keep your system up to date. This means you must know when to patch your system, what to patch, and how. An outdated system is a script kiddie’s best friend.

The FreeBSD Project includes volunteers who specialize in auditing source code and watching for security issues with both the base operating system and add-on software. These developers maintain a very low-volume mailing list, FreeBSD-security-notifications@FreeBSD.org, and subscribing is a good idea. While you can monitor other mailing lists for general announcements, the security notifications list is a single source for FreeBSD-specific information. To subscribe to the security notifications mailing list, see the instructions on http://lists.freebsd.org/. The FreeBSD security team releases advisories on that mailing list as soon as they’re available.

Read advisories carefully and quickly act on those that affect you, as you can be certain that script kiddies are searching for vulnerable machines. FreeBSD makes applying security patches pretty easy, as Chapter 18 discusses.

User Security

Remember when I said that your own users are your greatest security risk? Here’s where you learn to keep the little buggers in line. FreeBSD has a variety of ways to allow users to do their work without giving them free rein on the system. We’ll look at the most important tools here, starting with adding users in the first place.

Creating User Accounts

FreeBSD uses the standard Unix user management programs such as passwd(1), pw(8), and vipw(8). FreeBSD also includes a friendly interactive user-adding program, adduser(8). Only root may add users, of course. Just type adduser on the command line to enter an interactive shell.

The first time you run adduser(8), it prompts you to set appropriate defaults for all new user settings. Use the following example session to help you determine appropriate defaults for your system.

   # adduser
Username: xistence
Full name: Bert Reger
Uid (Leave empty for default):

The username is the name of the account. Users on my systems get a username of their first initial, middle initial, and last name. You can assign usernames by whatever scheme you dream up. Here, I let the user pick their own username, an indulgence I always later regret. The full name is the user’s real name. FreeBSD then lets you choose a numerical user ID (UID) . FreeBSD starts numbering UIDs at 1,000; while you can change this, all UIDs below 1,000 are reserved for system use. I recommend just pressing ENTER to take the next available UID.

Login group [xistence]:
Login group is xistence. Invite xistence into other groups? []: www
Login class [default]:
Shell (sh csh tcsh nologin) [sh]: tcsh
Home directory [/home/xistence]:
Home directory permissions (Leave empty for default):

The user’s default group is important—remember, Unix permissions are set by owner and group. The FreeBSD default of having each user in their own group is usually the most sensible way for most setups. Any of the big thick books on system administration offers several grouping schemes—feel free to use whatever matches your needs. You can add this user to other groups in addition to the primary group at this time, if appropriate.

A login class specifies what level of resources the user has access to. We’ll talk about login classes later in this section.

The shell is the command line environment. While the system default is /bin/sh, I prefer tcsh.1 If you’re deeply attached to another shell, feel free to use it instead. Knowledgeable users can change their own shells.

The home directory is where the user’s files reside on disk. The user and that user’s primary group own this directory. You can set custom permissions on the directory if you want, probably so that other users can’t view this user’s directory.

Use password-based authentication? [yes]:
Use an empty password? (yes/no) [no]:
Use a random password? (yes/no) [no]: y
Lock out the account after creation? [no]: n

The password options give you a certain degree of flexibility. If all of your users are comfortable with key-based SSH authentication, perhaps you can get away without using passwords. In the meantime, the rest of us are stuck with passwords .

Use an empty password if you want the user to set his or her own password via the console. Whoever connects to that account first gets to set the password. This makes an empty password a good idea right up there with smoking inside a hydrogen dirigible.

A random password , on the other hand, is a good idea for a new account. The random password generator FreeBSD provides is good enough for day-to-day use. Random passwords are usually hard to remember, which encourages the user to change his password as soon as possible.

When an account is locked , nobody can use it to log in. This is generally counterproductive.

After entering all this information, adduser spits everything back at you for review and confirmation or rejection. Once you confirm, adduser verifies the account setup and provides you with the randomly generated password. It then creates the user’s home directory, copies the shell configuration files from /etc/skel, and asks whether you want to set up another user.

Configuring Adduser: /etc/adduser.conf

Creating new users on some Unix systems requires you to manually edit /etc/passwd, rebuild the password database, edit /etc/group, create a home directory, set permissions on that home directory, install dotfiles, and so on. This makes handling your local customizations routine—if you set everything by hand, you can manage your local account setup easily. The adduser(8) program provides a set of sensible defaults. For sites with different requirements, /etc/adduser.conf lets you set those requirements as defaults while retaining the high degree of automation. To create adduser.conf file, run adduser -C and answer the questions.

Uid (Leave empty for default):
Login group []:
Enter additional groups []: staff
Login class [default]: staff
Shell (sh csh tcsh nologin) [sh]: tcsh
Home directory [/home/]: /nfs/u1/home
Use password-based authentication? [yes]:
Use an empty password? (yes/no) [no]:
Use a random password? (yes/no) [no]: yes
Lock out the account after creation? [no]: no

You might want to start numbering UIDs somewhere other than 1,000. If you want higher initial UIDs, enter it in the Uid space . Don’t start below 1,000.

The login group is the default user group. An empty login group means the user account defaults to having its own unique user group (the FreeBSD default).

You can specify any additional user groups that new accounts belong to by default, as well as the login class . I set both of these to staff so that all new users get added to that group and assigned that class.

Choose a default shell for your users.

Your home directory location might vary from the standalone FreeBSD standard. In this example, I’ve specified a typical style of NFS-mounted home directories used when many users have accounts on many machines.

Choose the default password behavior for new users. You can specify whether users should use passwords at all and whether the initial password should be empty or random .

Finally, dictate whether new accounts should default to being locked or not .

You’ll find more configuration settings in adduser.conf(5). While you can set account characteristics here, the format of this file is considered internal to adduser(8). The setting names can change with any FreeBSD release. To change adduser.conf, re-run adduser -C.

Editing Users

Managing users isn’t just about creating and deleting accounts. You’ll need to change those accounts from time to time. While FreeBSD includes several tools for editing accounts, the simplest are passwd(1), chpass(1), vipw(8), and pw(8). These work on the tightly interrelated files /etc/master.passwd, /etc/passwd, /etc/spwd.db, and /etc/pwd.db. We’ll start with the files and then review the common tools for editing those files.

The files /etc/master.passwd, /etc/passwd, /etc/spwd.db, and /etc/pwd.db hold user account information. Each file has a slightly different format and purpose. The file /etc/master.passwd is the authoritative source of user account information and includes user passwords in encrypted form. Normal users don’t have permission to view the contents of /etc/master.passwd. Regular users need access to basic account information, however; how else can unprivileged system programs identify users? The file /etc/passwd lists user accounts with all privileged information (such as the encrypted password) removed. Anyone can view the contents of /etc/passwd to get basic account information.

Many programs need account information, and parsing a text file is notoriously slow. In this day of laptop supercomputers, the word slow isn’t very meaningful, but this was a very real problem back when disco freely roamed the earth. For that reason, BSD-derived systems build a database file out of /etc/master.passwd and /etc/passwd. (Other Unix-like systems have similar functionality in different files.) The file /etc/spwd.db is taken directly from /etc/master.passwd and contains sensitive user information, but it can be read only by root. The file /etc/pwd.db can be read by anyone, but it contains the limited subset of information contained in /etc/passwd.

Any time any standard user management program changes the account information in /etc/master.passwd, FreeBSD runs pwd_mkdb(8) to update the other three files. For example, the three programs passwd(1), chpass(1), and vipw(8) all allow you to make changes to the master password file, and all three programs trigger pwd_mkdb to update the related files.

Changing a Password

Use passwd(1) to change passwords. A user can change his own password, and root can change anyone’s password. To change your own password, just enter passwd at the command prompt.

# passwd
Changing local password for mwlucas
Old Password:
New Password:
Retype New Password:

When changing your own password, passwd(1) first asks for your current password. This is to ensure that nobody else can change your password without your knowledge. It’s always good to log out when you walk away from your terminal, but when you don’t, this simple check in passwd(1) prevents practical jokers from really annoying you. Then enter your new password twice, and it’s done. When you’re the superuser and want to change another user’s password, just give the username as an argument to passwd.

# passwd mwlucas
Changing local password for mwlucas
New Password:
Retype New Password:

Note that root doesn’t need to know the user’s old password; the root user can change any user account on the system in any manner desired.

Changing Accounts with chpass(1)

The account has more information associated with it than just the password. The chpass(1) utility lets users edit everything they can reach in their account. For example, if I run chpass(1) as a regular user, I get an editor with the following text:

#Changing user information for mwlucas.
Shell: /bin/tcsh
Full Name: Michael W Lucas
Office Location:
Office Phone:
Home Phone:
Other information:

I’m allowed to edit six informational fields in my account. The first, my shell, can be set to any shell listed in /etc/shells (see “Shells and /etc/shells” on page 178). I can change my full name; perhaps I want my full middle name listed, or maybe I wish to be known to other system users as Mr. Scabies. I can update my office location and office phone so my coworkers can find me easily. This is another feature that was very useful on the university campuses where BSD grew up and where system users rarely had an idea of anyone’s physical location. Now that we have extensive online directories and many more computers, it’s less useful. I generally set my home phone number to 911 (999 in the UK), and I put a little bit of personal information in the Other space.

Also note what I can’t change as a regular user. The sysadmin sets my home directory, and I may not change it even if the system has a new hard drive with lots of empty space for my MP3 collection. My UID and GID numbers, similarly, are assigned by the system or the sysadmin.

On the other hand, if I run chpass xistence, its heightened privileges give me a very different view.

   #Changing user information for xistence.
   Login: xistence
Password: $6$D9b4FFD0kHK2sPSP$bXUFTQqV/QposXw2KTlswzpvoz4HBo8...
   Uid [#]: 1001
   Gid [# or name]: 1001
   Change [month day year]:
   Expire [month day year]:
   Class:
   Home directory: /home/xistence
   Shell: /bin/tcsh
   Full Name: Bert Regeer
   Office Location:
   Office Phone:
   Home Phone:
   Other information:

As root, you can do anything you like to the poor user. Changing his login to megaloser is only the start of the havoc you can wreak. You even get access to the user’s hashed password . Don’t alter this field, unless you’re comfortable computing password hashes. Use passwd(1) to more safely and reliably change the user’s password. You can also change the user’s home directory, although chpass(1) doesn’t move the user’s files; you must copy them by hand.

You can also set a date for password changes and account expiration. Password expiration is useful if you’ve just changed a user’s password and you want him to change it upon his first login. Account expiration is useful when someone asks for an account but insists it’s needed only for a limited time. You can forget to go back to delete that account, but FreeBSD never forgets. Both of these fields take a date in the form month day year, but you need only the first three letters of the month. For example, to make a user’s password expire on June 8, 2028, I would enter Jun 8 2028 in the Change space. Once the user changes his password, the password expiration field is blanked out again, but only the system administrator can extend an account expiration date.

The Big Hammer: vipw(8)

While chpass(1) is fine for editing individual accounts, what happens when you must edit many accounts? Suppose your system has hundreds of users and a brand new hard disk for the home partition. Do you really want to run chpass(1) hundreds of times? That’s where vipw(8) comes in.

Directly edit /etc/master.passwd with vipw(8). When you finish your edits, vipw(8) checks the password file’s syntax to be sure you haven’t ruined anything. Then, it saves the new password file and runs pwd_mkdb(8). Although vipw(8) can protect your password file from many basic mistakes, if you’re clever, you can still muck things up. You must understand the format of the password file to use vipw(8) properly.

If the information in /etc/master.passwd conflicts with information in other files, /etc/master.passwd wins. For example, the primary group that appears in /etc/master.passwd is correct, even if /etc/group doesn’t show the user as a member. This “master.passwd is always correct” logic is deeply ingrained throughout user management.

Each line in /etc/master.passwd is a single account record, containing 10 colon-separated fields. These fields are the following:

Username

This field is either an account name created by the sysadmin or a username created at install time to provide some system service. FreeBSD includes users for system administration, such as root, daemon, games, and so on. Each of these users owns a part of the base system. FreeBSD also provides accounts for common services, such as the www user reserved for use by web servers. Add-on software might add its own system accounts as well.

Encrypted Password

The second field is the encrypted password. System users don’t have a password, so you can’t log in as one of them. User accounts have a string of random-looking characters here.

User ID

The third field is the user ID number, or UID. Every user has a unique UID.

Group ID

Similarly, the fourth field is the group ID number, or GID. This is the user’s primary group. Usually this is identical to the UID, and the group has the same name as the username.

User’s Class

The next field is the user’s class as defined in /etc/login.conf (see “Restricting System Usage” on page 188).

Password Expiration

This field is the same as the password expiration date set via chpass(1), but here the time gets stored as seconds from the epoch. Use date -j and the +%s output format to generate epochal seconds from a real date. To convert midnight, June 1, 2018, to epochal seconds, run date -j 201806010000 '+%s'.

Account Expiration

This field enables you to make the account shut itself off on a certain day. Just set the account expiration date as you would for password expiration.

Personal Data

This field is also known as the gecos field for obscure historical reasons. It contains the user’s real name, office number, work phone number, and home phone number, all separated by commas. Do not use colons in this field; /etc/master.passwd reserves colons as a field delimiter.

User’s Home Directory

The ninth field is the user’s home directory. While this defaults to /home/<username>, you can move this anywhere appropriate. You’ll also need to move the actual home directory and its files when you change this field. Users with a nonexistent home directory can’t log in by default, although the requirehome setting in login.conf can change this.

User’s Shell

The final field is the user’s shell. If this field is empty, the system assigns the user the boring old /bin/sh.

While chpass(1) lets you muck up individual user accounts, vipw(8) unleashes you on the entire userbase. Be careful with it!

Removing a User

The rmuser(8) program deletes user accounts. You’ll be prompted for the username you want to delete and asked whether you want to remove that user’s home directory. That’s really all you have to do; destruction is much easier than creation, after all.

Scripting with pw(8)

The pw(8) command provides a powerful command line interface to user accounts. While useradd(8) walks you through setting up an account in a friendly manner, pw(8) lets you specify everything on a single command line. I find pw(8) cumbersome for day-to-day use, but if you manage many user accounts, it’s invaluable.

One thing I do use pw(8) for is locking accounts. While a locked account is active, nobody can log in to it. I’ve used this to great effect when a client was behind on a bill; users call quite quickly when they can’t log in, and yet their websites continue to come up and their email continues to accumulate.

# pw lock xistence

When Bert apologizes, I’ll unlock his account.

# pw unlock xistence

If you need scripts to manage your users, definitely read the pw(8) man page.

Shells and /etc/shells

The shell is the program that provides the user’s command prompt. Different shells behave differently and offer different shortcuts and features. Many people are very attached to particular shells and complain bitterly if their shell isn’t available on a system. The packages collection contains many shells.

The file /etc/shells contains a list of all legitimate user shells. When you install a shell from a port or a package, it adds an appropriate entry in /etc/shells. If you compile your own shell from source, without using a FreeBSD port, you must list the shell by its complete path in /etc/shells.

The FTP daemon won’t allow a user to log in via FTP if his shell isn’t listed in /etc/shells. If you use /sbin/nologin as an FTP-only user shell, you must add it to this file, although a better way to handle such users is with login classes, as discussed later in this chapter.

root, Groups, and Management

Unix security has been considered somewhat coarse because one superuser, root, can do anything. Other users are lowly peons who endure the shackles root places upon them. The problem is, root doesn’t have a wide variety of shackles on hand and can’t individualize them very well. While there’s some truth to this, a decent administrator can combine groups and permissions to handle almost any problem securely.

The root Password

Certain actions require absolute control of the system, including manipulating core system files such as the kernel, device drivers, and authentication systems. Such activities are designed to be performed by root.

To use the root password, you can either log in as root at a console login prompt or, if you’re a member of the group wheel, log in as yourself and use the switch user command su(1). (We’ll discuss groups later in this section.) I recommend su; it logs who uses it and can be used on a remote system. The command is very simple to use:

# su
Password:
#

Next, check your current user ID with the id(1) command:

# id
uid=0(root) gid=0(wheel) groups=0(wheel), 5(operator)
#

You now own the system—and I do mean own it. Consider every keystroke; carelessness can return your hard drive to the primordial state of unformatted empty wasteland. And share the root password sparingly, if at all, because anyone who has the root password can inflict unlimited damage on the system.

Remember, only the users in the group wheel can use the root password to become root through su(1). Anyone can use the root password at the system console, which is why physical protection of your system is vital. If you give the root password to a regular user who doesn’t have physical access to the console, they can type su and enter the root password as many times as they want, and it still won’t work.

This naturally leads to the question, “Who needs root access?” Much of the configuration discussed in this book requires use of the root password. Once you have the system running properly, you can greatly decrease or discontinue use of the root password. For those remaining tasks that absolutely require root privileges, I recommend the sudo package, and probably my book Sudo Mastery (Tilted Windmill Press, 2013). One of the simplest ways to reduce the need for root access is through the proper use of groups.

Groups of Users

Unix-like operating systems classify users into groups, each group consisting of people who perform similar administrative functions. A sysadmin can define a group called webmasters, add the accounts of the people editing web pages to that group, and set the privileges on the web-related files so that the members of that group can edit those files. She can also create a group called email, add the email administrators to that group, and set the permissions of mail-related files accordingly. Using groups in this manner is a powerful and oft-neglected tool for system management.

Any user can identify the groups she belongs to with id(1). The preceding example showed that the user root is in the groups wheel and operator. Root is a special user, however, and can do anything she pleases. Here’s my account, which is a little more realistic for an average user:

# id
uid=1001(mwlucas) gid=1001(mwlucas) groups=1001(mwlucas),0(wheel),68(dialer),1
0001(webmaster)

My UID is 1001, and my username is mwlucas. My GID, primary group ID, is 1001, and my primary group is named mwlucas as well. This is all pretty standard for the first user on a system, and even in later users, the only thing that changes is the numbers assigned to the account and primary group. More interesting is what other groups I’m assigned to: in addition to my primary group, I’m in the groups wheel, dialer, and webmaster. Wheel members may use the root password to become root, dialer members may use tip(1) without becoming root, and webmaster members can edit the web files on the local system. Each of these groups has special privileges on my system, and as a member of those groups, I inherit those privileges.

Group information is defined in /etc/group.

/etc/group

The file /etc/group contains all group information except for the user’s primary group (which is defined with the user account in /etc/master.passwd). Each line in /etc/group contains four colon-delimited fields: the group name, the group password, the group ID number, and a list of members.

Here’s a sample entry:

wheel:*:0:root,mwlucas,xistence

The group name is a human-friendly name for the group. This group is named wheel. Group names are arbitrary; you can call a group of users lackeys if you wish. Choose group names that give you an idea of what the groups are for; while you might remember that your lackeys may edit the company web page, will your coworkers understand that?

The second field, the group password, was a great idea that turned out to be a security nightmare. Modern Unix-like systems don’t do anything with the group password, but the field remains because old programs expect to find something in this space. The asterisk is a placeholder to placate such software.

The third field gives the group’s unique numeric group ID (GID). Many programs use the GID rather than name to identify a group. The wheel group has a GID of 0, and the maximum GID is 65535.

Last is a comma-delimited list of all users in the group. The users root, mwlucas, and xistence are members of the group wheel.

Changing Group Memberships

If you want to add a user to a group, add his username to the end of the line for that group. For example, the wheel group is the list of users that may use the root password. Here, I add rwatson to the wheel group:

wheel:*:0:root,mwlucas,xistence,rwatson

Mind you, the odds of me convincing rwatson (leading security researcher and ex–FreeBSD Foundation President) to assume sysadmin duties on any of my systems range from negligible to nonexistent, but it’s worth a try.

Creating Groups

To create a new group, you need only a name for the group and a group ID number. Technically, you don’t even need a member for the group; some programs run as members of a group, and FreeBSD uses the group permissions to control those programs just as the users are controlled.

Traditionally, GIDs are assigned the next number up the list. GID is an arbitrary number between 0 and 65535. Generally speaking, GIDs below 1000 are reserved for operating system usage. Programs that need a dedicated group ID usually use one in this range. User accounts start numbering their GIDs at 1001 and go up. Some special groups might start numbering at 65535 and go down.

Using Groups to Avoid Root

In addition to being a security concern, the root password distribution policy can cause dissension in any organization. Many sysadmins refuse to share the root password with people who’re responsible for maintaining part of the system but don’t offer an alternative and thereby prevent people from doing their job. Other sysadmins hand out root to dang near anyone who wants it and then complain when the system becomes unstable. Both attitudes are untenable in the long run. Personally, I don’t want root on your system. While having root privileges can be convenient, a lack of responsibility when the system breaks is more convenient.

One common situation is where a junior sysadmin is responsible for a particular portion of the system. I’ve had many DNS administrators work under me;2 these people don’t ever install software, recompile the kernel, or perform other sysadmin tasks. They only answer emails, update zone files, and reload the named daemon. New sysadmins often believe they need root access to do this sort of work. Nope. You can use groups.

Establishing your own groups, consisting of people who perform similar administrative functions, lets you avoid distributing the root password and still allow people to do their work. In this section, we’ll implement group-level access control over nameserver files. The same principles apply to any files you choose to protect. Mail and web configuration files are other popular choices for group-based management.

System Accounts

FreeBSD reserves some user account names for integrated programs. We discuss these unprivileged accounts in Chapter 19. For example, the nameserver runs under the user account bind and the group bind. If an intruder compromises the nameserver, she can access the system only with the privileges of the user bind.

Don’t have users log in as these users. They’re not set up as interactive accounts by design. What’s more, do not allow the group of the system account user to own the files created for that function. Create a separate user and group to own program files. That way, our hypothetical intruder can’t even edit the files used by the DNS server, further minimizing potential damage. If the program regularly updates the files (e.g., a database’s backend storage), you must give the program access rights, but chances are that a human being doesn’t ever need to edit that file. Similarly, there’s no reason a database should be able to edit its own configuration file.

Administrative Group Creation

The simplest way to create a group that owns files is to employ adduser(8) to make a user that owns them and then to utilize that user’s primary group as the group for the files. Because we already have a user called bind, we’ll create an administrative user dns. The username isn’t important, but you should choose a name that everyone will recognize.

Give your administrative user a shell of nologin, which sets a shell of /sbin/nologin. This prevents anyone from actually logging in as the administrative user.

If you want, you could specify a particular UID and GID for these sorts of users. I’ve been known to choose UID and GID numbers that resemble those used by their related service accounts. For example, the user bind has a UID and GID of 53. I could give the user dns a UID of 10053 to make it easily recognizable. At other times, I start numbering my administrative groups at 65535 and work my way down. It doesn’t matter as long as I’m completely consistent within an organization.

Do not add this administrative user to any other groups. Under no circumstances add this user to a privileged group, such as wheel!

Every user needs a home directory. For an administrative user, a home directory of /nonexistent works well. This user’s files are elsewhere in the system, after all.

Lastly, let adduser(8) disable the account. While the shell prevents logins, an extra layer of defense won’t hurt.

Now that you have an administrative user and a group, you can assign ownership of files to that user. A user and a group own every file. You can see existing file ownership and permissions with ls -l. (If you’ve forgotten how Unix permissions work, read ls(1) and chmod(1).) Many sysadmins pay close attention to file owners, somewhat less attention to worldwide permissions, and only glance at the group permissions.

# ls -l
total 3166
-rw-r-----  1 mwlucas  mwlucas    79552 Nov 11 17:58 rndc.key
-rw-rw-r--  1 mwlucas  mwlucas  3131606 Nov 11 17:58 mwl.io.db

Here, I’ve created two files. The first file, rndc.key, can be read and written by the user mwlucas. It can be read by anyone in the group mwlucas, but no one else can do anything with it. The file mwl.io.db can be read or written by the user mwlucas or anyone in the group mwlucas, but others can only read the file. If you’re in the group mwlucas, you can edit the file mwl.io.db without becoming root.

Change a file’s owner and group with chown(1). You must know the name of the user and group whose ownership you want to change. In this case, we want to change both files to be owned by the user dns and the group dns.

# chown dns:dns rndc.key
# chown dns:dns mwl.io.db
# ls -l
total 3166
-rw-r-----  1 dns  dns    79552 Nov 11 17:58 rndc.key
-rw-rw-r--  1 dns  dns  3131606 Nov 11 17:58 mwl.io.db

These files are now owned by the user dns and the group dns. Anyone who is in the group dns can edit mwl.io.db without using the root password. Finally, this file can be read by the user bind, who runs the nameserver. Add your DNS administrators to the dns group in /etc/group, and abruptly they can do their jobs.

The DNS administrators might think they need the root password for restarting the nameserver program itself. However, this is easily managed with rndc(8). Other tasks can be managed with cron jobs or with the add-on program sudo(8).

If you don’t want an administrative user but only a group, use vigr(8) to edit /etc/group.

Interesting Default Groups

FreeBSD ships with several default groups. Most are used by the system and aren’t of huge concern to a sysadmin—you should know that they’re there, but that’s different than working with them on a day-to-day basis. In Table 9-1, I present for your amusement and edification the most useful, interesting, and curious of the default groups. Adding your own groups simplifies system administration, but the groups listed here are available on every FreeBSD system.

Table 9-1: FreeBSD System Groups

Group name

Purpose

audit

Users who can access audit(8) information

authpf

Users who can authenticate to the PF packet filter

bin

Group owner of general system programs

bind

Group for the BIND DNS server software

daemon

Used by various system services, such as the printing system

_dhcp

DHCP client operations

dialer

Users who can access serial ports; useful for modems and tip(1)

games

Owner of game files

guest

System guests (almost never used)

hast

Files used by hastd(8)

kmem

Programs that can access kernel memory, such as fstat(1), netstat(1), and so on

mail

Owner of the mail system

mailnull

Default group for sendmail(8) or other mail server

man

Owner of uncompressed man pages

network

Owner of network programs like ppp(8)

news

Owner of the Usenet News software (probably not installed)

nobody

Primary group for unprivileged user nobody, intended for use by NFS

nogroup

Group with no privileges, intended for use by NFS

operator

Users that can access drives, generally for backup purposes

_pflogd

Group for PF logging

proxy

Group for FTP proxy in PF packet filter

smmsp

Group for Sendmail submissions

sshd

Owner of the SSH server (see Chapter 20)

staff

System administrators (from BSD’s college roots, when users were staff, faculty, or students)

sys

Another system group

tty

Programs that can write to terminals, like wall(1)

unbound

Files and programs related to the unbound(8) DNS server

uucp

Group for programs related to the Unix-to-Unix Copy Protocol

video

Group that can access DRM and DRI video devices

wheel

Users who may use the root password

www

Web server programs (not files)

_ypldap

Files needed by the LDAP-backed YP server ypldap(8)

I know very few people using either internet news or UUCP, and you might think you could reuse those groups for other purposes. You’re really better off creating a new group than risking confusion later, however. Group ID numbers are not in short supply.

Tweaking User Security

You prevent any single user from utilizing too much memory, processor time, or other system resources by setting limits on the account. Now that even small computers have very fast processors and lots of memory, these limits aren’t as important, but it’s still very useful in systems with dozens or hundreds of users. You can also control where users may log in from.

Restricting Login Ability

FreeBSD checks /etc/login.access every time a user tries to log in. If login.access contains rules that forbid logins from that user, the login attempt fails immediately. This file has no rules by default, meaning that anyone who provides a valid username and password has no restrictions.

The /etc/login.access file has three colon-delimited fields. The first either grants (+) or denies (-) the right to log in; the second is a list of users or groups; and the third is a list of connection sources. You can use an ALL or ALL EXCEPT syntax, which allows you to make simple but expressive rules. Rules are checked on a first-fit basis. When login(1) finds a rule where the user and the connection source match, the connection is immediately accepted or rejected, making rule order vital. The default is to allow logins.

For example, to allow only members of the wheel group to log in from the system console, you might try this rule:

+:wheel:console

The problem with this rule, however, is that it doesn’t actually deny users login privileges. Since the default is to accept logins, and since all this rule does is explicitly grant login privileges to the users in the wheel group, nothing changes. Bert certainly isn’t in the wheel group, but if he tries to log in, no rule denies him access.

You could try two rules like this:

+:wheel: console
-:ALL:console

This set of rules would achieve the desired effect but is longer than you need. Use ALL EXCEPT instead.

-:ALL EXCEPT wheel: console

This rule rejects unwanted logins most quickly and runs less risk of administrator error. As a rule, it’s best to build login.access lists by rejecting logins, rather than permitting them. FreeBSD immediately rejects nonwheel users at the console upon hitting this rule.

Change the default from “allow access” to “deny access” by adding a final rule.

-:ALL:ALL

Any login request that doesn’t match an earlier permit rule gets denied.

The last field in login.access, the connection source, can use hostnames, host addresses, network numbers, domain names, or the special values LOCAL and ALL. Let’s see how they work.

Hostnames

Hostnames rely upon DNS or the hosts file. If you suspect that your nameserver might suffer an intrusion or attack, avoid hostnames; intruders can give a hostname any IP address that they like and fool your system into accepting the connection, and a nameserver failure could lock you out completely. Still, it’s possible to use a rule like this:

-:ALL EXCEPT wheel:fileserver.mycompany.com

Users in the wheel group can log in from the fileserver, but nobody else can.

Host Addresses and Networks

Host addresses work like hostnames, but they’re immune to DNS failures or spoofing.

-:ALL EXCEPT wheel:203.0.113.5

A network number is a truncated IP address, like this:

-:ALL EXCEPT wheel:203.0.113.

This network number allows anyone in the wheel group to log in from a machine whose IP address begins with 203.0.113 and denies everyone else access from those IP addresses.

LOCAL

The most complicated location is LOCAL, which matches any hostname without a dot in it (generally, only hosts in the local domain). For example, www.mwl.io thinks that any machine in the domain mwl.io matches LOCAL. DNS spoofing can easily evade this filter. Although my desktop claims that it has a hostname of storm.mwl.io, its IP address has reverse DNS that claims it’s somewhere in my cable modem provider’s network. The host www.mwl.io thinks that my desktop isn’t in the same domain and hence isn’t local. As such, I can’t use the LOCAL verification method.

Similarly, anyone who owns a block of IP addresses can give their addresses any desired reverse DNS. The LOCAL restriction is best avoided.

ALL and ALL EXCEPT

ALL matches everything, and ALL EXCEPT matches everything but what you specify. These are the most useful connection sources, in my opinion. For example, if you had a highly secure machine only accessible from a couple of management workstations, you could have a rule like this:

-:ALL EXCEPT wheel:ALL EXCEPT 203.0.113.128 203.0.113.44

Tie It All Together

The point of these rules is to build a login policy that matches your real-world policies. If you provide generic services but only allow your system administrators to log on remotely, a one-line login.access prevents any other users from logging in:

-:ALL EXCEPT wheel:ALL

This is great if you can live with a restriction this tight. On the other hand, I’ve worked at several internet service providers that used FreeBSD to provide client services. Lowly customers weren’t allowed to log onto the servers unless they had a shell account. System administrators could log in remotely, as could the DNS and web teams (members of the dns and webmasters groups). Only sysadmins could log onto the console, however.

-:ALL EXCEPT wheel:console
-:ALL EXCEPT wheel dns webmasters:ALL

Set this up in login.access once, and let group membership control all of your remote logins forever after.

Restricting System Usage

You can provide more specific controls with login classes. Login classes, managed through /etc/login.conf, define the resources and information provided for users. Each user is assigned a class, and each class has limits on the system resources available. When you change the limits on a class, all users get the new limits when they next log in. Set a user’s class when creating the user account, or change it later with chpass(1).

Class Definitions

The default login.conf starts with the default class, the class used by accounts without any other class. This class gives the user basically unlimited access to system resources and is suitable for application servers with a limited number of users. If this meets your needs, don’t adjust the file at all.

Each class definition consists of a series of variable assignments that define the user’s resource limits, accounting, and environment. Each variable assignment in the class definition begins and ends with a colon. The backslash character is a continuation character to indicate that the class continues on the next line, which makes the file more readable. Here’s a sample of the beginning of one class:

default:\
      :passwd_format=sha512:\
        :copyright=/etc/COPYRIGHT:\
        :welcome=/etc/motd:\
--snip--

This class is called default . I’ve shown three of the dozens of variables in this class. The variable passwd_format , for example, is set to sha512 . These variable assignments and the class name describe the class, and you can change the user’s experience on the system by assigning the user to another class.

Some of login.conf ’s variables don’t have a value and instead change account behavior just by being present. For example, the requirehome variable takes effect just by being included in the class. If this value is present, the user must have a valid home directory.

       :requirehome:\

After editing login.conf, you must update the login database to make the changes take effect.

# cap_mkdb /etc/login.conf

This rebuilds the database file /etc/login.conf.db that’s used for fast lookups, much like /etc/spwd.db.

The default /etc/login.conf includes several example classes of users. If you want an idea of what sort of restrictions to put on users for various situations, check those examples. The following section offers ideas about what can be set in a login class. For a complete listing of supported settings in your version of FreeBSD, read man login.conf(5).

Resource Limits

Resource limits allow you to control how much of the system any one user can monopolize at any one time. If you have several hundred users logged in to one machine and one of those users 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 one user can monopolize, you can make the system more responsive for all users.

Table 9-2 defines the resource-limiting login.conf variables.

Table 9-2: Some login.conf Variables for Limiting Resource Use

Variable

Description

cputime

The maximum CPU time any one process may use

filesize

The maximum size of any one file

datasize

The maximum memory size of data that can be consumed by one process

stacksize

The maximum amount of stack memory usable by a process

coredumpsize

The maximum size of a core dump

memoryuse

The maximum amount of memory a process can lock

maxproc

The maximum number of processes the user can have running

openfiles

The maximum number of open files per process

Sbsize

The maximum socket buffer size a user’s application can set

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

Current and Maximum Resource Limits

In addition to the limits listed previously, you can specify current and maximum resource limits. Current limits are advisory, and the user can override them at will. This works well on a cooperative system, where multiple users willingly share resources but you want to notify those users who exceed the standard resource allocation. Many users want to be good citizens, and readily cooperate when they’re told they’re pushing their limits.3 Users cannot exceed maximum limits.

If you don’t specify a limit as current or maximum, FreeBSD treats it as a maximum 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 a maximum limit on the number of processes the user can have, use this input:

        --snip--
        :maxproc-cur: 30:\
        :maxproc-max: 60:\
        --snip--

One counterpart to resource limits is resource accounting. These days, accounting isn’t as important as it was when today’s inexpensive computers would cost millions of dollars, so we won’t discuss it in this book. It’s more important to restrict a single user from consuming your system than to bill for every CPU cycle someone uses. You should know that the capability exists, however.

If you need more complicated resource restrictions, investigate rctl(8).

Class Environment

You can also define environment settings in /etc/login.conf. This can work better than setting them in the default .cshrc or .profile because login.conf settings affect all user accounts immediately upon their next login. Some shells, such as zsh(1), don’t read either of these configuration files, so using a class environment sets the proper environment variables for those users.

All of the environment fields recognize two special characters. A tilde (~) represents the user’s home directory, while a cash symbol ($) represents the username. Here are a few examples from the default class that illustrate this:

        :setenv=MAIL=/var/mail/$,BLOCKSIZE=K,FTP_PASSIVE_MODE=YES:\
        :path=/sbin /bin /usr/sbin /usr/bin /usr/games /usr/local/sbin /usr/
local/bin /usr/X11R6/bin ~/bin:\

By using the $ character, the environment variable MAIL is set to /var/mail/<username> . Similarly, the last directory in the PATH variable is the bin subdirectory in the user’s home directory .

Table 9-3 lists some common login.conf environment settings.

Table 9-3: Common login.conf Environment Settings

Variable

Description

hushlogin

If present, no system information is given out during login.

ignorenologin

If present, these users can log in even when /var/run/nologin exists.

manpath

A list of directories for the $MANPATH environment variable.

nologin

If present, the user cannot log in.

path

A list of directories for the $PATH environment variable.

priority

Priority (nice) for the user’s processes (see Chapter 21).

requirehome

User must have a valid home directory to log in.

setenv

A comma-separated list of environment variables and their values.

shell

The full path of a shell to be executed upon login. This overrides the shell in /etc/master.passwd. The user’s $SHELL, however, contains the shell from the password file, resulting in an inconsistent environment. Playing games with this is an excellent way to annoy your users.

term

The default terminal type. Just about anything that tries to set a terminal type overrides this.

timezone

The default value of the $TZ environment variable.

umask

Initial umask setting; should always start with 0, see builtin(1).

welcome

Path to the login welcome message, usually /etc/motd.

Remember, changes to a class affect all users in that class. If a user needs a change from the class settings, you’ll need to change their class.

Password and Login Control

Unlike the environment settings, many of which can be set in places other than the login class, most login and authentication options can be controlled only from the login class. Here are some common authentication options:

passwd_format

This option sets the cryptographic hash used to store passwords in /etc/master.passwd. The default is sha512, for SHA512 hashing. Other permissible options are des (DES), blf (Blowfish), md5, and sha256 (SHA256). DES and Blowfish are most useful when you want to share password files between different Unix-like operating systems, but are very weak. SHA256 is for compatibility with older password files, from before SHA512 was the default.

mixpasswordcase

If present, FreeBSD complains if the user changes his password to an all-lowercase word. Despite the name, all-uppercase passwords satisfy this option.

host.allow, host.deny

These values let users in this class use rlogin and rsh. Avoid them like the fuzzy green meat your creepy roommate tried to feed you that one time.

times.allow

This option allows you to schedule when users may log in with a comma-delimited list of days and times. Days are given as the first two letters of the day’s name (Su, Mo, Tu, We, Th, Fr, and Sa). Time is in standard 24-hour format. For example, if a user can log in only on Wednesdays between 8 AM and 5 PM, you’d use this entry:

        :times.allow=We8-17:\

times.deny

This option allows you to specify a time window when the user can’t log in. Note that this does not kick off users who are already logged in. The format is the same as for times.allow. If times.allow and times.deny overlap, times.deny takes precedence.

You can’t make that overworking developer go home, but you can keep him from opening another terminal window.

File Flags

All Unix-like operating systems have the same filesystem permissions, assigning read, write, and execute privileges for a file to the file’s owner, its group, and all others. FreeBSD extends the permissions scheme with file flags, which work with permissions to enhance your system’s security.

Many flags have different effects depending on the system securelevel, which we’ll cover in the next section. Understanding securelevels requires an understanding of file flags, while file flags rely on securelevels. For the moment, just nod and smile when you encounter a mention of securelevels; all becomes clear in the next few pages.

A few file flags are useful only in specialized cases. We’ll look only at the most commonly useful flags. See chflags(1) for the complete list.

Many flags have multiple names; while only one name appears in ls(1) output, you can use any name at the command line. These alternate names exist because people got tired of getting an error when they typed sappend instead of sappnd. Here, I show the flag’s primary name first and then the user-friendly aliases.

sappnd, sappend

This system-level, append-only flag can be set only by root. Files with this flag can be added to but can’t be removed or otherwise edited. This flag is particularly useful for log files. Setting sappnd on a user’s .history file can be interesting if the account is compromised. Since a common intruder tactic is to remove .history or symlink it to /dev/null so that the admin can’t see what happened, sappnd ensures that script kiddies cannot cover their tracks in this manner. It’s almost funny to review the record of someone trying to remove a sappnd file; you can almost see the attacker’s frustration grow as he tries various methods.4 This flag can’t be removed when the system is running at securelevel 1 or higher.

schg

Only root can set the system-level immutable flag. Files with this flag set can’t be changed in any way. They can’t be edited, moved, replaced, or overwritten. Basically, the filesystem itself prevents all attempts to alter this file. The flag can’t be removed when the system is running at securelevel 1 or greater.

sunlnk

Only root can set the system-level undeletable flag on a file. The file can be edited or altered, but it can’t be deleted. This isn’t as secure as the previous two flags because if a file can be edited, it can be emptied. It’s still useful for certain circumstances, however. I’ve used it when a program insisted on deleting its own log files upon a crash. It’s not generally useful to set on any standard system files, however. This flag can’t be removed when the system is running at securelevel 1 or higher.

uappnd

This user-level, append-only flag can be set only by the file owner or root. Like the system-level append-only flag sappnd, a file with this flag set can be added to but not otherwise edited or removed. This flag is most useful for logs from personal programs and the like; it’s primarily a means to let users prevent accidental removal of their own files. The owner or root can remove this flag.

uchg

This user-level, immutable flag can be set only by the owner or root. Like the schg flag, this immutable flag prevents anyone from changing the file. Again, root can override this, and it can be disabled by the user at any securelevel. This flag helps prevent mistakes, but it’s not a way to secure your system.

uunlnk

This user-level, undeletable flag can be set only by the owner or root. A file with this flag set can’t be deleted by the owner. Root can override that, and the user can turn this flag off at any time, making this mostly useless.

Setting and Viewing File Flags

Set flags with chflags(1). For example, to be sure that nothing replaces a server’s kernel, you could do this:

# chflags schg /boot/kernel/kernel

You’d need to remove this flag to perform system updates.

You can recursively change the flags on an entire directory tree with the -R flag. For example, to make all of /bin directory immutable, run this command:

# chflags -R schg /bin

And boom! Your basic system binaries can’t be changed.

To see what flags are set on a file, use ls -lo.

# ls -lo log
-rw-r--r--  1 mwlucas  mwlucas  sappnd 0 Nov 12 12:37 log

The sappnd entry tells us that the system append-only flag is set on this log. For comparison, if a file has no flags set, it looks like this:

# ls -lo log
-rw-r--r--  1 mwlucas  mwlucas  - 0 Nov 12 12:37 log

The hyphen in place of the flag name tells us that no flag has been set.

An out-of-the-box FreeBSD install doesn’t have many files marked with flags, but you can flag anything you want. On one system that I fully expected to be hacked, I went berserk with chflags -R schg in various system directories to prevent anyone from replacing system binaries with trojaned versions. It might not stop an attacker from getting in, but imagining their frustration improved my mood.

To remove a file flag, use chflags and a no in front of the flag name. For example, to unset the schg flag on your kernel, enter this command:

# chflags noschg /boot/kernel/kernel

That said, you must be running at securelevel –1 to unset many flags. So, without further ado, let’s discuss securelevels and what they mean to you.

Securelevels

Securelevels are kernel settings that change basic system behavior to disallow certain actions. The kernel behaves slightly differently as you raise the securelevel. For example, at low securelevels, file flags can be removed. A file might be flagged immutable—but you can remove the flag, edit the file, and reflag it. When you increase the securelevel, the file flag can’t be removed. Similar changes take place in other parts of the system. Taken as a whole, the behavior changes that result from increased securelevels either frustrate or stop an intruder. Enable securelevels at boot with the rc.conf option kern_securelevel_enable="YES".

Securelevels complicate system maintenance by imposing restrictions on your behavior. After all, many system administration tasks are also things intruders might do to cover their tracks. For example, at certain securelevels, you can’t format or mount new hard drives while the system is running. On the other hand, securelevels hamper intruders even more than they hamper you.

Securelevel Definitions

Securelevels come in 5 degrees: –1, 0, 1, 2, and 3, with –1 being the lowest and 3 the highest. Once you enable securelevels with the kern_securelevel_enable rc.conf option, you can set the securelevel at boot with the kern_securelevel rc.conf variable. You can raise the securelevel at any time, not just at boot, but you can’t lower it without rebooting into single-user mode. After all, if you could lower the securelevel at any time, so could your intruder!

The effects of each securelevel vary depending on your FreeBSD release. To get the latest information, read security(7).

Securelevel –1

The default provides no additional kernel security whatsoever. If you’re learning FreeBSD and are frequently changing your configuration, remain at securelevel –1 and use the built-in file permissions and other Unix safeguards for security. Flags like sappnd and schg will work, but chflags(1) can easily remove the flags.

Securelevel 0

Securelevel 0 is used only during booting and offers no special features over securelevel –1. When the system reaches multiuser mode, however, the securelevel is automatically raised to 1. Setting kern_securelevel=0 in /etc/rc.conf is effectively the same as setting kern_securelevel=1. Securelevel 0 is helpful if you have startup scripts that perform actions prohibited by securelevel 1.

Securelevel 1

At securelevel 1, the basic secure mode, things become interesting:

  • System-level file flags may not be turned off.
  • You can’t load or unload kernel modules (see Chapter 6).
  • Programs can’t write directly to system memory via either /dev/mem or /dev/kmem.
  • Nothing can access /dev/io.
  • You can’t enter the kernel debugger with the debug.kdb.enter sysctl.
  • You can’t panic the system with the debug.kdb.panic sysctl.
  • Mounted disks can’t be written to directly. (You can write files to disk; you just can’t address the raw disk devices.)

The most obvious effect of securelevel 1 for ordinary users is that the BSD-specific filesystem flags can’t be altered. If a file is marked system-level immutable, and you want to replace it, too bad.

Securelevel 2

Securelevel 2 has all the behaviors of securelevel 1, with two additions:

  • Disks can’t be opened for writing, whether mounted or not.
  • You can’t alter system time by more than one second.

Both of these seem irrelevant to new sysadmins, but they provide important security protections. Although Unix provides handy tools, like text editors to write files, it’s also possible to bypass both those tools and the actual filesystem to access the underlying ones and zeros on the hard drive. Poking at the hard drive lets you change any file regardless of the file permissions. The only time this commonly happens is when you install a new hard drive and must create a filesystem on it. Normally, only the root user can write directly to the disk in this manner. At securelevel 2, even root can’t use newfs(8), zpool(8), and so on.

Similarly, another old hacker trick is to change the system time, edit a file, and change the time back. That way, when the administrator looks for files that might be causing trouble, the tampered file appears to have been untouched for months or years and hence doesn’t seem an obvious source of concern.

Securelevel 3

Securelevel 3 is the network secure mode. In addition to the settings of securelevels 1 and 2, you can’t adjust packet filter rules. The firewall on your host is immutable. If you have a system with packet filtering or bandwidth management enabled and those rules are well tuned and unlikely to change, you can use securelevel 3.

Which Securelevel Do You Need?

The securelevel appropriate for your environment depends entirely upon your situation. If you’ve just put a FreeBSD machine into production and are still fine-tuning it, leave the securelevel at –1. Once your system is tuned, however, you can raise the securelevel. Most production systems run just fine at securelevel 2.

If you use one of FreeBSD’s packet filtering or firewall packages, securelevel 3 might look tempting. Be very sure of your firewall rules before you enable this, however! Securelevel 3 makes it impossible to change your firewall without disrupting your connection. Are you 100 percent certain that none of your customers will ever call in to say, “Here’s a check. Now give me more servers!”?

What Won’t Securelevels and File Flags Accomplish?

Consider a case where someone compromises a CGI script on your web server, uses that to bootstrap into a shell, and then uses the shell to bootstrap himself into root access.

If you’ve set the securelevel accordingly, perhaps this attacker will become frustrated because not only can’t she replace your kernel with her specially compiled one, she also can’t even load a kernel module. No problem—she can still replace assorted system programs with trojaned versions so that the next time you log in, your new version of login(1) sends your password to an anonymous web-based mailbox or to an internet newsgroup.

So, to protect your key files, you run around doing chflags schg -R /bin/*, chflags schg -R /usr/lib, and so on. Fine. If you forget one file—say, something obscure like /etc/rc.bsdextended —your intruder can edit that file to include chflags -R noschg /. She can then reboot your system late at night when you might not notice. How often do you sit down and exhaustively audit your /etc/rc files?

You think that your system is safe, with every file completely protected. But what about /usr/local/etc/rc.d, the local program startup directory? The system boot process tries to execute any executable file in this directory that contains a line starting with #PROVIDE: (see Chapter 17 for why). Your intruder could therefore do a lot of damage by placing a simple shell script there. After all, /etc/rc raises the securelevel at the end of the boot process. What if she were to create a shell script that kills the running /etc/rc before it could raise the securelevel and then she turned around and ran his own /var/.hidden/rc.rootkit to finish bringing the system up?

Of course, these are only a couple of possibilities. There are others, limited only by your intruder’s creativity. Remember that system security is a thorny problem with no easy solution. Once intruders have a command prompt, it’s you against them. And if they’re any good, you won’t even notice the penetration until it’s too late. By following good computing practices and keeping your system up to date, you can stop them from intruding in the first place. Do not allow securelevels to make you lazy!

Living with Securelevels

If you’ve been liberal with the schg flag, you’ll soon find that you can’t upgrade or patch your system conveniently. The fact is, the same conditions that make intruders’ lives difficult can make yours a living hell if you don’t know how to work with them.

If you’ve frozen /etc/rc.conf with schg, you must lower the securelevel to change the programs running on your system. Of course, the securelevel setting is in that file, so in order to edit it, you must take control of the system before /etc/rc runs. That means you must boot into single-user mode (as discussed in Chapter 4), mount your filesystems, run chflags noschg on the files in question, and continue booting. You can even entirely disable securelevels in /etc/rc.conf and work normally while the system runs or add commands to /etc/rc.local so they take effect before the securelevel is set. You’ll restore service more quickly that way but lose the protections of the file flags.

After you’ve finished maintenance, you can raise (but not lower) the securelevel by changing the kern.securelevel sysctl to your desired securelevel.

# sysctl kern.securelevel=3

Now that you can control file changes, let’s consider controlling access to your system from the network.

Network Targets

Intruders normally break into applications that listen to the network, not the operating system itself. An operating system may or may not help defend a piece of software against network attacks, but the intrusion itself starts with the application. One way to reduce the number of attacks that can be carried out against your server is to identify all of the programs that are listening to the network and disable any that aren’t strictly necessary. FreeBSD provides sockstat(1) as an easy way to identify programs that are listening to the network.

We cover sockstat in detail in Chapter 8; running sockstat -4 shows all open IPv4 TCP/IP ports. Every network port you have open is a potential weakness and a potential target. Shut down unnecessary network services and secure those you must offer.

It’s a good idea to regularly review which ports are open on your systems because you might learn something that surprises you. You might find that some piece of software you’ve installed has a network component that you weren’t aware of and that it’s been quietly listening to the network.

Once you know what’s running, how do you turn off what you don’t need? The best way to close these ports is to not start the programs that run them. Network daemons generally start in one of two places: /etc/rc.conf or a startup script in /etc/rc.d. Programs that are integrated with the main FreeBSD system, such as sendmail(8), sshd(8), and rpcbind(8), have flags in rc.conf to enable or disable them, as do many add-on programs. See Chapter 4 for details on enabling and disabling programs at startup.

Network probes are strange in that you really don’t know when someone pokes at your hosts. To see how much of this goes on, set log_in_vain to 1 in /etc/rc.conf on one of your public servers. This tells the kernel to log all connection attempts to closed ports. When someone checks your host for a nonexistent telnet, Squid, or database listener, the kernel logs the attempt to /var/log/messages. Watch that log only long enough to realize clear down to your marrow that the whole internet really is out to get you—and then disable log_in_vain.

Putting It All Together

Once you have only the necessary network ports open and you know which programs are using those ports, you know which programs you must be most concerned about securing. If the FreeBSD security team sends out an announcement of a problem with a service you don’t run, you can safely delay implementing a fix until your next maintenance window. If, however, the security team announces a hole in programs you’re using, you know you have to implement a fix as soon as possible. If they announce a serious security problem with a piece of network software you’re using, you know you must act quickly. Simply being able to respond intelligently and quickly to real risks helps protect you against most intruders. Tools such as file flags and securelevels minimize the damage successful intruders can do. Finally, using groups to restrict your own system administrators to particular sections of the system can protect your computers from both accidental and deliberate damage.

Let’s shift gears now and talk storage.