Hiding Root with sudo

While the proper use of groups can almost eliminate the need for root access to edit files, that won’t help with commands that can be run only by root. You could set up a cron job to reload the name server each day at midnight, but every piece of software occasionally needs a manual restart. Because root is an all-or-nothing affair, people who have one minor task to perform have traditionally needed the root password.

OpenBSD includes sudo(8) and its associated tools, which implement fine-grained access control for commands that can be run only by particular users. When configured properly, sudo lets normal users run specific programs as other users, including root. Configured improperly, sudo permits full root access. I’ll explain a basic sudo setup that covers almost all uses, but remember that many more combinations are possible. And don’t be afraid to read sudo(8), sudoers(5), and the documentation at the sudo home page (http://www.gratisoft.us/sudo/).

Why Use sudo?

The sudo tool provides benefits beyond fine-grained privilege control. Every command run via sudo is logged, making it very easy to track who did what. The senior sysadmin can change the root password and not give it out, even to people who have root-level access.

The sudo configuration file is designed to be shared across multiple systems, so one sudo policy can cover your entire network and every operating system. Admittedly, you’ll have trouble using a single sudo configuration on operating systems with wildly unique directory layouts, such as Mac OS X, but you can easily share one configuration among OpenBSD, other BSDs, Linux, and even OpenSolaris or AIX.

sudo Disadvantages

The most common problem with sudo is getting your users to accept it. People who have historically had access to the root account think they “lose something” by working through sudo.

The key to overcoming this is to give users access only to what’s required to perform the tasks for which they’re responsible. A junior administrator who complains about insufficient privileges has either overreached his responsibilities or needs more privileges. One sure way to discover what people actually do is to implement a minimal sudo permissions scheme and wait for complaints. If no one complains, they’re not working very hard.

The configuration syntax for sudo can be confusing because its configuration doesn’t closely resemble any other configuration file, and getting everything right can be difficult at first. The configuration file is actually well suited to its purpose, however. Once you understand it, adjusting privileges is quick and easy.

Note

More seriously, a faulty sudo setup can create the appearance of security while leaving gaps for a user to become root. Be sure to test sudo every time you make a change, and avoid the common configuration mistakes I document here.

Some users will do their best to push the limits of their access, for no other reason than to see if they can outsmart you. These users are best managed with a combination of careful configuration, administrative policy, and a cricket bat.

An Overview of the sudo Software

The sudo program is a setuid root wrapper that can run commands as any other user. Use sudo by giving it the command you want to run.

$ sudo /etc/rc.d/named restart

The sudo software compares the desired command (in this case, /etc/rc.d/named restart) to its internal list of permissions and privileges. If the configuration file allows that particular user to run that command as root, sudo runs it as root. And, because root can run any command as any user, sudo can also run commands as any arbitrary system user. You can use this fact to grant any user the ability to run specific commands as chosen users; for example, administrators of certain database servers must frequently run commands as the database user.

The sudo software is a suite with four pieces. The first piece is the actual sudo(8) command, the setuid root wrapper. The second is the configuration file /etc/sudoers, which describes who may run which commands as what user. (/etc/sudoers is fully documented in sudoers(5).) Third is the visudo(8) command that opens /etc/sudoers in an editor and checks the configuration file syntax before exiting. Finally, the sudoedit(8) program is specifically for editing files as another user.

The visudo(8) Command

If /etc/sudoers contains incorrect syntax, sudo will not run. If you rely on sudo to provide root-level access to the system and you break your sudoers file, you’ll lock yourself out of the root account and lose the ability to correct your error. That is bad.

Fortunately, the visudo(8) program provides some protection against this sort of error by locking /etc/sudoers so that only one person can edit the configuration at a time. It then opens /etc/sudoers in a text editor (vi by default, but it respects the $EDITOR environment variable). Make your changes and save your work. When you exit the editor, visudo should parse the file for syntactic correctness.

If visudo detects an error, it prints out the offending line number and asks what you want to do.

>>> /etc/sudoers: syntax error near line 34 <<<
What now?

Here, I’ve made an error near line 34. I can reedit the file to fix the error, quit without saving any changes, or force visudo to accept this file.

Press the E key, and visudo should return you to the editor. Go to the offending line, fix your error, save the file, and exit the editor again.

Enter the X key, and visudo should quit and revert the configuration file to its original state. Your changes will be lost, but that might be acceptable. It’s better to have an old, working configuration than a new, broken one.

Pressing Q forces visudo to accept the file, busted syntax and all. If sudo can’t parse /etc/sudoers, it will immediately exit. Essentially, you’re telling visudo to break sudo until you log in as root to fix the problem. If you think you understand /etc/sudoers better than visudo does, you’re probably wrong. Even if you’re right, you’re wrong.

The visudo program doesn’t guarantee that the configuration will do what you desire, only that the configuration parses and is valid. A properly formatted configuration that declares “No one may do anything via sudo” is perfectly acceptable to visudo.

The /etc/sudoers File

The /etc/sudoers file determines who may run which commands as which users. Never edit /etc/sudoers directly, even if you think you know exactly what change you want to make. Always use visudo to change /etc/sudoers.

The various sudoers sample configurations you’ll find are usually very complicated, as they demonstrate all the nifty things sudo can do. At this point, however, you want to do only simple, boring things, like giving particular users access to run specific commands. And the bare syntax is very simple. Every sudoers rule follows this format:

Username    host=command
  • The username is the username of the user who may execute the command, an alias for usernames, or a system group.

  • The host is the hostname of the system this rule applies to. You can share /etc/sudoers across multiple systems. This entry permits per-host rules.

  • The command space lists the commands this rule applies to. You must list the full path to each command, or sudo will not recognize it. If this weren’t a requirement, some untrustworthy soul could just adjust his $PATH to access renamed versions of commands.

For example, suppose I trust user sbaxter to run any command, on any system, as root. I use the keyword ALL to match all possible options for host and command:

sbaxter    ALL=ALL

As the lead sysadmin, I should know which duties I have assigned sbaxter, and exactly which commands he needs. Suppose sbaxter is my DNS minion. I control the actual editing of zone files with group permissions, but there are many legitimate occasions for him to stop, restart, or otherwise slap around the name server program. I want him to use the system script /etc/rc.d/named for this task, and this sudoers entry gives him permission to use the script on all machines.

sbaxter    ALL=/etc/rc.d/named

If I share this file across several machines, it’s likely that many of those machines don’t even run a name server. To restrict my minion’s access to only the DNS server, I’ll change the host field.

sbaxter    dns1=/etc/rc.d/named

Then again, sbaxter is the administrator of the email server mail1. This server is his responsibility, so he needs to run any command. I can set entirely different privileges for him on the mail server and still use the same sudoers file on all the systems.

sbaxter    dns1=/etc/rc.d/named
sbaxter    mail1=ALL

Yes, sbaxter can use visudo on mail1, but he already has full privileges on that machine. I’m comfortable with this, as he knows I’ll hold him responsible for any downtime.

Multiple Entries in a sudoers Field

Separate multiple entries in a single field with commas. For example, after a while, I get tired of sbaxter asking me to mount NFS shares on the DNS server, so I add mount_nfs to his privileges.

sbaxter    dns1=/etc/rc.d/named,/sbin/mount_nfs

He can now mount his own blasted NFS shares and leave me alone.

Running Commands As Non-root Users

Specify a username in parentheses before a command to say that the user can use sudo to run commands as a particular user. For example, my user dwsmith is a database administrator and needs to run any command as the user _postgresql on the database server db1.

dwsmith    db1 = (_postgresql) ALL

The _postgresql user can’t successfully run critical system programs like fdisk and newfs, but it can restart the database, back it up, and perform other database-administration tasks. By choosing a specific user, a specific machine, and a specific command, you can define arbitrarily complex sudoers policies.

Long Lines

If you have several commands, usernames, or hosts on a line, that line might become uncomfortably long. Use a backslash () to indicate that a rule continues on the next line.

sbaxter    dns1=/etc/rc.d/named,/sbin/mount_nfs, 
        /sbin/reboot, /sbin/dump

Use as many lines as you like to make your sudoers file easier to manage.

/etc/sudoers Aliases

Take several machines with different roles, add multiple sysadmins with differing privilege levels, and your /etc/sudoers file will quickly become complicated. When you have a few users with identical privileges and long lists of commands that you would like them to access, maintaining consistency in each user’s privilege list becomes tedious. Aliases simplify these tasks and make /etc/sudoers much more comprehensible, which makes your life easier.

An alias is a group of users, hosts, or commands. You can use aliases anywhere you would normally use users, hosts, or commands. You might, for example, create an alias called DATABASE_COMMANDS that contains all of the commands your database administrators need to run using sudo.

Let’s take database administrator dwsmith and use an alias to specify his commands.

dwsmith    db1 = (_postgresql) DATABASE_COMMANDS

This alias might not seem to save us much, but suppose we have several database administrators. We could create an alias called DBAs that includes all of them.

DBAs    db1 = (_postgresql) DATABASE_COMMANDS

Suddenly, this one line represents multiple rules. All of the database admins have identical sudo privileges, and when you discover that you need to give them access to an additional command, add the command to the alias, and it immediately becomes available to every database admin. There’s no tedious and error-prone copying of entries between users.

You must define an alias before you can use it, so aliases normally appear at the top of the file. Each alias is made up of a label identifying its type, a name, and a list of its items. Alias types include user aliases, run as aliases, host aliases, and command aliases.

User Aliases

A user alias is a group of users, and it is labeled with the string User_Alias. Put only usernames in this alias.

User_Alias    DBAs = dwsmith, kkrusch

Here, the user alias DBAs contains the users dwsmith and kkrusch. By using the alias in my sudoers rules instead of the usernames, I ensure that these users receive exactly the same sudo privileges.

You can use system groups in user aliases by prefacing them with a percent sign (%). I might create a group in /etc/groups called databaseteam, and make dwsmith and kkrusch part of that team.

%databaseteam db1 = (_postgresql) DATABASE_COMMANDS

Perhaps the most common usage of this is giving the wheel group unlimited sudo access.

%wheel ALL = ALL

This rule permits the wheel group to run any command as root through sudo. It doesn’t change the group members’ privileges, but gives them access via sudo. This is convenient for running single commands.

Run as Aliases

A run as alias is a list of users that other users can run commands as. For example, on certain application servers, the database admins need to run commands both as the database owner _postgresql and as the web server owner www. If the user must run commands as multiple users, however, you would need a separate sudoers entry for each target user.

A run as alias lets you group these accounts:

Runas_Alias    APPOWNER = _postgresql, www

You can now write a single rule allowing users to run commands as either _postgresql or www.

Host Aliases

A host alias is a list of hosts, defined as hostnames, IP addresses, or network blocks. Label host aliases with the string Host_Alias. Here are examples of all host alias types:

Host_Alias    DB = db1, db2, db3
Host_Alias    DMZ = 192.0.2.0/24
Host_Alias    FIREWALL = 192.0.2.1, 192.0.2.2, 192.0.2.3

Note

I warn elsewhere in this book about how security rules based on a hostname are vulnerable to DNS spoofing attacks. An intruder can’t spoof the machine’s local hostname, however, so you can safely use the hostname from /etc/myname in sudoers.

Command Aliases

A command alias is a list of commands. For example, you might have a command alias that includes all of the commands needed to back up the system or restore from a backup. They’re labeled with the string Cmnd_Alias.

Cmnd_Alias    BACKUPS = /bin/mt, /sbin/restore, /sbin/dump

You can tell a command alias to include everything in a particular directory by using a wildcard.

Cmnd_Alias	APPCOMMANDS = /home/appuser/bin/*

You can also list partial command names. For example, most of PostgreSQL’s commands begin with the pg_ prefix. To give a user access to these commands, use a wildcard like so:

Cmnd_Alias    APPCOMMANDS = /home/appuser/bin/*,/usr/local/bin/pg_*

If you find yourself writing command aliases that include paths like /sbin/*, stop and reconsider, because you’re essentially giving the user unlimited root access.

Using Aliases in /etc/sudoers

Use an alias exactly as you would normally list the user, command, or hostname. In the previous examples, I defined the user alias DBAs, the run as alias APPOWNER, the host alias DB, and the command alias APPCOMMANDS. Here’s how they might be used:

DBAs    DB = ALL

Here, the user group DBAs can run any command on any server in the DB group, as any user. The members of the group own the servers, and if they screw them up, it’s not my problem.

Well, this attitude sounds good, but the truth is that when they destroy the server, I must get involved. Even if it’s not my fault that they drove the database server into the ditch, it is my problem. I must lock down the commands that they can run, restricting them to only the commands in the APPCOMMANDS alias. So, the DBAs group can now run any command in APPCOMMANDS on the DB servers.

DBAs    DB = APPCOMMANDS

Then I discover that my database admins are either cleverer or dafter than I thought. They run certain database commands as root, creating log files owned by root. The unprivileged database user _postgresql cannot write to these log files, and so the application server crashes. Fixing this requires changing the permissions on those log files, but the database admins do not have permission to run chown. If I give them the ability to change the permissions on arbitrary files, I might as well just give them root access.

To keep this from happening again, I restrict their privileges so they can run their commands only as the application unprivileged users.

DBAs    DB = (APPOWNER) APPCOMMANDS

Everyone in the DBAs group can run any command in APPCOMMANDS, as any user in APPOWNER, on any server in DB. I can change their access by adding entries to the various aliases.

Without aliases, what would this rule look like?

dwsmith,kkrusch    db1,db2,db3 = (_postgresql,www) 
     /home/appowner/bin/*,/usr/local/bin/pg_*

That’s ugly, and it does exactly the same thing.

If you name your aliases well, you’ll find rules easier to understand. While these example aliases are fairly short, I’ve used aliases with up to 20 members. The resulting rules are appalling without aliases.

Note

Some of the permissions granted by sudo in this case are unnecessary. For example, the unprivileged web server user doesn’t need to run the various PostgreSQL utilities, and if www did try to run the database, nothing much would happen. If you don’t like this, make two separate rules. Either way, it’s tighter security than giving database administrators the root password.

Nesting Aliases

You can include aliases in aliases. Here, I combine two user aliases into a single alias for my application administrators:

User_Alias    APPADMINS = DBAs, WEBMASTERS

Alias Naming Conventions

It’s traditional, but not mandatory, to give aliases names in all capital letters to help differentiate them from users, hosts, and so on. And though it’s valid syntax, it’s best to avoid naming aliases after users or hosts. Here’s an example:

User_Alias    MWLUCAS    = mwlucas,pkdick,sbaxter,dwsmith

This would quickly drive me batty.[16]

You can also reuse alias names if they are for different types of aliases. For example, the following is perfectly legal, but perfectly offensive.

User_Alias    DB = dwsmith,kkrusch
Runas_Alias    DB = _postgresql,www
Host_Alias    DB = db1, db2, db3
Cmnd_Alias    DB = /usr/local/bin/pg_*, /home/appowner/bin/*
DB    DB = (DB) DB

If you do this, anyone who must debug your sudo configuration will curse your name. Even if you consider being cursed a job perk, this naming scheme makes your phone ring at inconvenient times.

Changing sudo’s Default Behavior

You can customize sudo’s behavior, or its behavior for certain users, hosts, or aliases, with the Defaults field. For example, one feature of sudo is that if you enter the wrong password, it insults you.

$ sudo -l
Password:
My pet rat can type better than you!
Password:

I typed my password incorrectly. sudo insulted me and offered me a chance to enter my password again. If I enter the wrong password three times, sudo exits.

Insulting the user is just fine in an open source environment, but if you’re in a company, someone will complain to management. You can either go to sensitivity training or proactively disable insults by adding the following line to sudoers:

Defaults !insults

The Defaults statement indicates that the following item affects one or more sudo defaults. The insults option controls insulting the user. The exclamation point (!) is a negation symbol. By putting an exclamation point in front of the option, you turn off the feature. The system will no longer insult users when they demonstrate that they cannot type as well as my pet rat.

$ sudo -l
Password:
Sorry, try again.
Password:

You can override defaults globally or on a per-alias basis.

Overriding Defaults per Host

To override the defaults on a per-host basis, use an @ symbol after Defaults and give either a host or a host alias. Here, I want to insult users who can’t type their password on caddis or on a machine in the alias APPSERVERS, while leaving insults disabled for all other servers:

Defaults !insults
Defaults@caddis insults
Defaults@APPSERVERS insults

This lets me enable or disable functions for any combination of servers.

Overriding Defaults per User

To change sudo defaults on a per-user basis, use a % and the user or user alias.

Defaults !insults
Defaults%lasnyder insults
Defaults%DBAs insults

It doesn’t matter where lasnyder logs in—I’m going to insult him, as well as the users in the DBAs alias. But database administrators are used to poor treatment by their software, and to not insult them would confuse and disappoint them.

Overriding Defaults per Command

You can also change how sudo behaves on a command-by-command basis by putting an exclamation point between Defaults and the command list.

Defaults !insults
Defaults!/sbin/newfs,/sbin/fsck insults
Defaults%APPCOMMANDS insults

Anyone who tries to use newfs(8) or fsck(8) (discussed in Chapter 8) and cannot type their password needs insulting. The application administration commands might not merit insults, but I can always claim it was an oversight.

Overriding Defaults per Run As

Lastly, you can change the defaults based on who the command is being run as. Use a right angle bracket (<) to indicate changing behavior for a run as alias.

Defaults !insults
Defaults<_postgresql insults
Defaults<APPOWNER insults

If a user runs a command as _postgresql, or as any user in the APPOWNER run as alias, and types his password incorrectly, he gets insulted.

Note

In the rest of this chapter, we’ll use Default widely. Please assume that each section includes the text “Restrict this as necessary by user, host, command, or run as.”

sudo and the Environment

Certain environment variables can cause problems. For example, $HOME is an obvious one—a user cannot create files in another user’s home directory. Others, such as LD_LIBRARY_PATH, can cause endless annoyance as well as security issues, as applications try to link against the wrong libraries. The sudo program can remove suspicious environment variables, completely reset the user’s environment, or be configured to preserve the original user’s environment.

The env_reset sudoers option is set by default. It purges all environment variables except LOGNAME, SHELL, USER, USERNAME, and anything beginning with SUDO_. You can change this behavior by disabling env_reset, but I strongly recommend against disabling environment purging.

Instead of letting users blindly carry all the random garbage in their environment along with them, create a list of necessary and safe environment variables that they can retain. You’ll see examples in OpenBSD’s default sudoers file using the env_keep option.

Defaults env_keep +="DESTDIR DISTDIR EDITOR FETCH_CMD FLAVOR FTPMODE GROUP MAKE"
Defaults env_keep +="MAKECONF MULTI_PACKAGES NOMAN OKAY_FILES OWNER PKG_CACHE"
Defaults env_keep +="PKG_DBDIR PKG_DESTDIR PKG_PATH PKG_TMPDIR PORTSDIR"
Defaults env_keep +="RELEASEDIR SHARED_ONLY SSH_AUTH_SOCK SUBPACKAGE VISUAL"
Defaults env_keep +="WRKOBJDIR"

The OpenBSD team deems these environment variables safe to pass into a new user account. The += means “add these to the existing list of items to keep.” The environment variables themselves are in quotation marks.

If you need to pass your SSH environment around your servers, you can use scp(1) and sftp(1) to move files to other servers. Read the documentation, create a list of approved environment variables, and add an entry.

Defaults env_keep += "SSH_CLIENT SSH_CONNECTION SSH_TTY SSH_AUTH_SOCK"

Note

The ability to copy files to other servers probably should be restricted to people in a certain group. Sysadmins might need to copy files to other servers, but many other users don’t need this access.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.82.154