The sudo utility allows users to run commands as another, or privileged, user. Sudo has features not often used by administrators. This article demonstrates some of these features, such as include files, timeouts, and logical operations.

Share:

David Tansley (david.tansley@btinternet.com), System Administrator, Ace Europe

David TansleyDavid Tansley is a freelance writer. He has 15 years of experience as a UNIX administrator, using AIX the last eight years. He enjoys playing badminton, then relaxing watching Formula 1, but nothing beats riding and touring on his GSA motorbike with his wife.


developerWorks Contributing author
        level

06 October 2009

Also available in Chinese Russian

Introduction

Sudo is a utility that allows systems administrators to give users or groups the ability to run commands as another user. In other words, command privileges can be delegated, without compromising the other user's password. This process is carried out by the root user making sudo entries in the /etc/sudoers file. The file is edited using the visudo command. When delegating authority, there is an element of trust required that the responsibility you are handing over will be used with respect. Let's put a urban myth to rest here: sudo is not used purely to allow users to run certain commands as root; it is mainly used so another user can be delegated to run an application or system command as the application user/owner. If you currently have sudo installed on your system, it will not overwrite your existing sudoers file. But as always, make a backup copy of /etc/sudoers and read the upgrade instructions.


Installing sudo

Download the latest version of sudo.

This article is not an introduction of sudo. Rather, it looks at different sudo features.

For this demonstration I have used sudo version 1.7.2. If you are running AIX® 5.3 , be sure you have the current gcc version, which is 4.0.0.

# export LIBPATH=/usr/lib
# ./configure --with-aixauth
# make
# make install

To confirm the version installed and to view the build options as well as the current installed configuration, as root, use:

# sudo -V 
Sudo version 1.7.2
Sudoers path: /etc/sudoers
Authentication methods: 'aixauth'

< rest of output truncated>

Since version 1.6.9, some system environment variables, like LIBPATH, are stripped before sudo executes the given script or command. This can cause issues with existing commands that are run under sudo. However, there is a way around this, mostly.

By default:

  • sudo is placed in /usr/local/bin. Use this to run commands as another user.
  • visudo is placed in /usr/local/sbin. Use this to edit the sudoers file.
  • THe sudoers file, if not already present, is placed in /etc. This file holds the sudo entries.

The sudoers file

The /etc/sudoers (commonly known just as sudoers) file governs who and what can be run using sudo. These entries are carried out by the root user or a user who has root privileges. The most basic form of a sudo entry in sudoers is:

<user> <host> = <user to alias> <password required> < command to run>

To view what commands a user can run and what other constraints are assigned under sudo, as the user, run:

sudo -l

To run a sudo command, the general format is:

sudo -u < user to run as> <command to run>

Log it

All commands run as sudo are logged using syslog to /var/adm/messages using the entry in the /etc/syslog.conf file:

*.debug   /var/adm/messages

However, I believe sudo commands should be logged to a separate file, which makes it easier to view and investigate sudo commands that have been run. Of course, this also helps with keeping an eye on failed sudo events. Create the file /var/adm/sudo.log, and then in the /etc/sudoers file, put the following entry:

Defaults logfile=/var/adm/sudo.log
Defaults !syslog

Now all sudo events executed successfully or not go to /var/adm/sudo.log.


Managing sudoers

Over time, your sudoers file will grow with more and more entries, which is to be expected. This could be because more application environments are being placed on the server, or because of splitting the delegation of currents tasks down further to segregate responsibility. With many entries, typos can occur, which is common. Making the sudoers file more manageable by the root user makes good administrative sense. Let's look at two ways this can be achieved, or at least a good standard to build on. If you have many static entries (meaning the same command is run on every machine where sudo is), put these into a separate sudoers file, which can be achieved using the include directive.

Having many entries for individual users can also be time consuming when adding or amending entries. With many user entries, it is good practice to put these into groups. Using groups, you can literally group users together, and the groups are valid AIX groups.

Now look at these two methods more closely.

Include file

Within large-enterprise environments, keeping the sudoers file maintained is an important and regularly required task. A solution to make this chore easier is to reorganize the sudoers file. One way to do this is to extract entries that are static or reusable, where the same commands are run on every box. Like audit/security or storix backups or general performance reports, with sudo you can now use the include directive. The main sudoers file can then contain the local entries, and the include file would barely need editing as those entries are static. When visudo is invoked, it will scan sudoers when it sees the include entry. It will scan that file, then come back to the main sudoers and carry on scanning. In reality, it works like this. When you exit out of visudo from the main sudoers file, it will take you to the include file for editing. Once you quit the include, you are back to the AIX prompt. You can have more than one include file, but I cannot think of a reason why you would want more than one.

Let's call our secondary sudoers file sudo_static.<hostname>. In the examples in this demonstration the hostname I am using is rs6000. In the main sudoers file, make the entry as follows:

#include /etc/sudo_static.rs6000

Next, add some entries to the /etc/sudo_static.rs6000 file. You do not have to put in all the sudoers directives or stanzas. If this file contains entries where they are not required, don't include them. For example, my include file contains only the following text, and nothing more.

You can use the %h, instead of typing the actual hostname:

#include /etc/sudo_static.%h.

I personally do not use this method because I have experienced returning extra characters on the hostname. This issue is fixed in sudo 1.7.2 p1.

bravo     rs6000 = (root) NOPASSWD: /usr/opt/db2_08_01/adm/db2licd -end
bravo     rs6000 = (root) NOPASSWD: /usr/opt/db2_08_01/adm/db2licd
bravo     rs6000 = (db2inst) NOPASSWD: /home/db2inst/sqllib/adm/db2start
bravo     rs6000 = (db2inst) NOPASSWD: /home/db2inst/sqllib/adm/db2stop force

When you run visudo, and you save and quit the file, visudo will inform you to click Enter to edit the include sudoers file. Once you have edited the file, sudo will pick up on syntax errors if any, as with the main file. Alternatively, to edit the include file directly, use:

visudo -f /etc/sudo_static.rs6000

.

Using groups

Users belonging to a valid AIX group can be included in sudoers, making the sudoers file more manageable with fewer entries per user. When reorganizing the sudoers entries to include groups, you may have to create a new groups under AIX to include users that are only allowed to use sudo for certain commands. To use groups, simply prefix the entries with a '%'. Assume you have groups called devops and devuat, and with those groups you have the following users:

# lsgroup -f -a users devops

devops:
        users=joex,delta,charlie,tstgn

 # lsgroup -f -a users devuat
devuat:
        users=zebra,spsys,charlie

For the group devops to be allowed to run the /usr/local/bin/data_ext.sh command as dbdftst.

For the group devuat to be allowed to run the commands :/usr/local/bin/data_mvup.sh, /usr/local/bin/data_rep.sh as dbukuat.

We could have the following sudoers entries:

%devops rs6000 =  (dbdftst) NOPASSWD: /usr/local/bin/data_ext.sh
%devuat rs6000 =  (dbukuat) /usr/local/bin/data_mvup.sh
%devuat rs6000 =  (dbukuat) /usr/local/bin/data_rep.sh

Notice in the previous entries, the group devops users will not be prompted for their password when executing /usr/local/bin/data_ext.sh; however, the group devuat users will be prompted for their password. User "charlie" is a member of both groups (devops and devuat), so he can execute all the above commands.


Timeout with sudo

Sudo has a feature that uses time tickets to determine how long since the last sudo command was run. During this time period, the user can re-run the command without being prompted for the password (that's the user's own password). Once this time allotment has ended, the user is prompted for the password again to re-run the command. If the user gives the correct password, the command is executed, the ticket is then re-set, and the time clock starts all over again. The ticket feature will not work if you have NOPASSWD in the user's entry in sudoers. The default timeout is five minutes. If you wish to change the default value, simply put an entry in sudoers. For example, to set the timeout value for user "bravo" on any commands he runs to 20 minutes, you could use:

Defaults:bravo timestamp_timeout=20

To destroy the ticket, as the user, use:

$ sudo -k

When the ticket is destroyed, the user will be prompted for his password again, when running a sudo command.

Please do not set the timeout value for all users, as this will cause problems, especially when running jobs in batch and the batch takes longer to run than normal. To disable this feature, use the value -1 in the timestamp_timeout variable. The time tickets are directory entries with the name of the user located in /var/run/sudo.


Those variables

As discussed earlier, sudo will strip out potentially dangerous system variables. To check out what variables are kept and which ones are stripped, use sudo -V. The output will give you a listing of preserved and stripped variables. Stripping out the LIBPATH is clearly an inconvenience. There are a couple of ways around this--either write a wrapper script or specify the environments on the command line. Looking at the wrapper script solution first, suppose you have an application that stops or starts a DB2® instance. You could create a bare-bones script that would keep the variables intact. In Listing 1. rc.db2, notice that you source the instance profile, which in turn exports various LIBPATH and DB2 environment variables, keeping the environment variable intact, by using:

 . /home/$inst/sqllib/db2profile

For completeness, the entries in sudoers to execute this is and not strip out any system environment variables are:

bravo     rs6000 = (dbinst4) NOPASSWD: /home/dbinst4/sqllib/adm/db2start
bravo     rs6000 = (dbinst4) NOPASSWD: /home/dbinst4/sqllib/adm/db2stop force
bravo     rs6000 = (dbinst4) NOPASSWD: /usr/local/bin/rc.db2 stop db2inst4
bravo     rs6000 = (dbinst4) NOPASSWD: /usr/local/bin/rc.db2 start db2inst4

Note in this example, user "bravo" can execute the above commands as user "dbinst4." Typically, the user would run:

sudo -u dbinst4 /usr/local/bin/rc.db2 stop db2inst4
sudo -u dbinst4 /usr/local/bin/rc.db2 start db2inst4
Listing 1. rc.db2
#!/bin/sh
# rc.db2
# stop/start db2 instances

# check to see if db2 inst is runningdb2_running(){state=`ps -ef |grep db2sysc 
                    |grep -v grep| awk '$1=="'${inst}'" { print $1 }'`
if [ "$state" = "" ] 
then 
    return 1 
else 
    return 0 
fi}

usage ()

{
echo "`basename $0` start | stop <instance>"
}

# stop db2 
stop_db2 ()
{
echo "stopping db2 instance as user $inst" 
    if [ -f /home/$inst/sqllib/db2profile ]; then 
        . /home/$inst/sqllib/db2profile 
else 
    echo "Cannot source DB2..exiting" 
exit 1
fi 
    /home/$inst/sqllib/adm/db2stop force
}

# start db2
start_db2 ()
{
echo "starting db2 instance as user $inst" 
    if [ -f /home/$inst/sqllib/db2profile ]; then 
        . /home/$inst/sqllib/db2profile 
else 
    echo "Cannot source DB2..exiting" 
exit 1
fi
/home/$inst/sqllib/adm/db2start
} 

# check we get 2 params
if [ $# != 2 ]
then 
    usage 
    exit 1
fi

inst=$2 

case "$1" in 
Start|start) 
    if db2_running  
        then  
        echo "db2 instance $inst appears to be already running"
         exit 0 
    else 
        echo " instance not running as user $inst..attempting to start it" 
        start_db2 $inst

        fi
        ;; 
Stop|stop) 
    if db2_running 
        then 
        echo "instance running as $inst..attempting to stop it"
        stop_db2 $inst 
    else 
        echo "db2 instance $inst appears to be not running anyway" 
        exit 0 
    fi 
    ;; 
*) usage  
;;
esac

The other way to preserve system environment variables is to use the Defaults !env_reset directive, like in sudoers:

Defaults !env_reset

Then from the command line, specify the environment variable name with its value:

$ sudo LIBPATH=″/usr/lib:/opt/db2_09_05/lib64″ -u delta /usr/local/bin/datapmp

If you do not put the !env_reset entry in, you will get the following error from sudo when you try to run the command:

sudo: sorry, you are not allowed to set the following environment variables: LIBPATH

If you find that sudo is also stripping out other environment variables, you can specify the variable name in sudoers so that sudo keeps those variables intact (with the Defaults env_keep += directive). For instance, suppose sudo was stripping out the application variables DSTAGE_SUP and DSTAGE_META from one of my suodo-ised scripts. To preserve these variables, I could put the following entries in sudoers:

Defaults env_keep += "DSTAGE_SUP"
Defaults env_keep += "DSTAGE_META"

Notice that I give the variable name and not the variable value. The values are already contained in my script like this:

export DSTAGE_SUP=/opt/dstage/dsengine; export DSTAGE_META=/opt/dstage/db2

Now when the sudo script is executed, the above environment variables are preserved.


Securing the sudo path

A default PATH within sudoers can be imposed using the secure_path directive. This directive specifies where to look for binaries and commands when a user executes a sudo command. This option clearly tries to lock down specific areas where a user runs a sudo command, which is good practice. Use the following directive in sudoers, specifying the secure PATH with its search directories:

Defaults secure_path="/usr/local/sbin:/usr/local/bin:/opt/freeware/bin:/usr/sbin"

Getting restrictive

Restrictions can be put in place to restrict certain commands to users. Assume you have a group called dataex, whose members are "alpha," "bravo," and "charlie." Now, that group has been allowed to run the sudo command /usr/local/bin/mis_ext * , where the asterisk represents the many parameters passed to the script. However, user "charlie" is not allowed to execute that script if the parameter is import. This type of condition can be met by using the logical NOT '!' operator. Here is how that is achieved in sudoers:

 %dataex rs6000 = (dbmis) NOPASSWD: /usr/local/bin/mis_ext *
 charlie rs6000 = (dbmis) NOPASSWD: !/usr/local/bin/mis_ext import

Note that the logical NOT operator entries go after the non-restrictive entry. Many conditional NOT entries can be applied on the same line; just make sure that they are comma separated, like so:

charlie rs6000 = (dbmis) NOPASSWD: /usr/local/bin/aut_pmp *

charlie rs6000 = (dbmis) NOPASSWD: !/usr/local/bin/aut_pmp create,
!/usr/local/bin/aut_pmp delete, !/usr/local/bin/aut_pmp amend

When in visudo, do not think just saving the sudo entry and staying in visudo will make the changes effective; it won't. You must exit visudo for the changes to take effect.


Rolling out sudo commands

Rolling out sudo commands to remote hosts in an enterprise environment is best done using a ssh script as root, and the keys should have been exchanged between the hosts, for password-less logins. Let's look at one example of how to do this. With geographically remote machines, if you get a hardware issue of some sort (disk or memory), the IBM® engineer will be on-site to replace the failing hardware. There will be occasions when they require the root password to carry out their task. One procedure you might want to put in place is for the engineer to gain access to root they must use sudo. Informing the engineer prior to the visit of the password would be advantageous. Listing 2 demonstrates one way you could roll out this configuration. Looking more closely at Listing 2, use a for loop containing a list of hosts you are pushing out to. (Generally, though, you would have these hosts in a text file and read them in using a while loop.) Using the 'here' document method, make a backup copy of sudoers, and an entry is then appended to sudoers, like so:

# -- ibmeng sudo root
ibmeng host1 = (root) NOPASSWD:ALL

Next, the user "ibmeng" is created, and the password is set for the user using chpasswd. In this demonstration, it is ibmpw. A message is then appended to their profile, informing the user how to sudo to root. So when the engineer logs in, he is presented with the message:

IBM Engineer, to access root account type: sudo -u root su -

Of course the account for ibmeng would be locked after the visit.

Listing 2. dis_ibm
#!/bin/sh
# dis_ibm
dest_hosts='host1 host2 host3 host4'

for host in $dest_hosts
do
echo "doing [$host]"

$ssh -T -t -l root $host<<'mayday'
host=`hostname`
cp /etc/sudoers /etc/sudoers.bak

    if [ $? != 0 ]
    then
    echo "error: unable to cp sudoers file"
    exit 1
    fi
echo "# -- ibmeng sudo root\nibmeng $host = (root) NOPASSWD:ALL">>/etc/sudoers

mkuser su=false ibmeng
if [ $? = 0 ]
    then
    echo "ibmeng:ibmpw" | chpasswd -c
    else
    echo "error: unable to create user ibmeng and or passwd"
    exit 1
fi
    chuser gecos='IBM engineer acc' ibmeng
if [ -f /home/ibmeng/.profile ]
    then
    echo "echo \"IBM Engineer, to access root account type: sudo -u root su -"\"
>>/home/ibmeng/.profile
fi
mayday
done

Conclusion

Sudo allows you to control who can run what commands as whom. But you must be able to understand the features of sudoers fully to gain maximum understanding of its implications and responsibility.

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into AIX and Unix on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=AIX and UNIX
ArticleID=433360
ArticleTitle=Make sudo work harder
publish-date=10062009