sudo can help enforce strict security policies, but what about situations in which you don't want to restrict what commands your users run?
Maybe you're looking for a way to keep track of what your sysadmin team does as root, so you can quickly find out what happened when something goes wrong. Even if you're the only administrator, it's possible to make a bad error as root without realizing it. An audit trail allows you to go back and see exactly what you did type during that 3:00 AM hacking session.
As mentioned in [Hack #61], giving access to a shell with sudo means that you lose your audit trail the moment the root shell executes. One answer to this problem is sudoscript.
Another scenario where sudoscript is useful is one similar to the situation that caused me to write sudoscript in the first place. I was a sysadmin in a small startup whose engineers all had the root password. The IT crew all used sudo, but they had tried without success to convince the engineers to use it. Upon investigation, I discovered that the principal reason for this was the prohibition on running shells with sudo.
It quickly became clear that I wasn't going to be able to argue that sudo, as implemented, was equivalent to having a root shell; positions had hardened long before I showed up. So, I wrote sudoscript to bring these engineers back into the IT department's supported circle. It worked, and having the audit trail saved my bacon several times.
6.10.1 sudoscript Overview
sudoscript is a pair of Perl scripts. One is called sudoshell , or just ss. Contrary to its name, sudoshell is not a shell like tcsh or bash. Instead, it is a frontend script that uses authorization from sudo to run as root and runs script(1) on a FIFO (named pipe) managed by the second script. That script is a daemon, called sudoscriptd . It takes data from the FIFO opened by sudoscript and tags it with the user's name, PID, and a timestamp before writing it to a log file. This log file, /var/log/sudoscript, is managed by the daemon and rotated if its size exceeds 2 MB. The effect of all this is a root shell that saves its terminal input and output in a log file.
6.10.2 Is sudoscript Secure?
The answer is yes and no. The answer is "yes" because sudoscript doesn't confer any privilege of its own; it relies on sudo for that. For that reason, programming or architecture errors in sudoscript (which I have worked hard to avoid) shouldn't increase the security risk to a system. The user of sudoscript already has the privilege to do anything at all on the system.
The answer is "no" if you expect the audit trail provided by sudoscript to be bulletproof. It isn't. For one thing, an xterm will produce a shell that is not audited. Additionally, the FIFO that the scripts use must be writable by the effective user running it. If that effective user is root, then of course there are many, many ways to avoid the audit trail. Simply killing the daemon (but not sudoshell) would do the trick nicely, for example.
The moral is: don't give sudoscript to users you don't trust with root. If you have to give it to such users, though, it is probably better than nothing.
6.10.3 Using sudoscript
Build sudoscript from source in the ports tree or install it from a binary package. (Note that both are misnomers with respect to sudoscript, since it is pure Perl. These mechanisms install the scripts and supporting files.) If you want to enable only root shells, sudoscript configuration is easy. Add an entry like the following to /usr/local/etc/sudoers:
Cmnd_Alias SS = /usr/local/bin/sudoshell, /usr/local/bin/ss
You can then grant sudoscript access to chosen users through the usual mechanisms. For example:
%wheel ALL=SS joe joesbox=SS
Now when a user runs ss:
% ss The sudoscriptd doesn't appear to be running! Would you like me to start it for you? (requires root sudo privilege)? yes This will be a one-off startup of the daemon. You may have to arrange for it to be started when the system starts, if that's what you want. See the INSTALL file in the distribution for details. sudoscriptdwaiting for the daemon ..done Script started, output file is /var/run/sudoscript/ssd.test_root_1667/test1667.fifo #
The INSTALL file mentioned lives in /usr/local/share/doc/sudoscript-version/, along with lots of other documentation.
As shown in the example, sudoshell will start sudoscriptd if it isn't running already. You probably want to add sudoscriptd to the system startup, which you can do by renaming /usr/local/rc.d/sudoscriptd.sh.sample to /usr/local/rc.d/sudoscriptd.sh. Unfortunately, this script isn't a true rc-style startup script in the manner of SysV init, in that it doesn't have start and stop targets; however, this will change in the next release. (As of this writing, sudoscript is at Version 2.1.1.)
sudoscript can enable shells as users other than root. This could be handy for auditing activity of the dba user, for instance. If you want to use this feature, you must also add a Unix group called ssers. If this group exists when sudoscriptd starts, it will make some changes to the files in /var/run/sudoscript (where the FIFOs live) to accomodate group access to those files. This has security implications in that anyone in the ssers group will have access to the FIFOs being used by any other concurrent user of sudoscript. Both the user that will run ss and the user ss will enable must be in the ssers group.
To get nonroot shells to work, you also have to change your sudoers entries like so:
Host_Alias DBBOXES = db1,db2,db3 Cmnd_Alias SS = /usr/local/bin/sudoshell, \ /usr/local/bin/ss Cmnd_Alias SSASDBA = /usr/local/bin/sudoshell -u dba, \ /usr/local/bin/ss -u dba %wheel ALL=SS joe joesbox=SS datamonkey DBBOXES=(dba) SSASDBA
Once the ssers group and the preceding entries in are place:
% id uid=1004(datamonkey) gid=1004(datamonkey) groups=1004(datamonkey), 92(ssers) % ss -u dba Password: Script started, output file is /var/run/sudoscript/ssd.datamonkey_dba_2223/datamonkey2223.fifo bash-2.05b$ id uid=1005(dba) gid=1005(dba) groups=1005(dba), 92(ssers)
6.10.4 The sudoscript Log File
The sudoscript log file lives in /var/log/sudoscript. It contains entries like the following:
Mon Dec 22 00:32:19 New logger for datamonkey with pid 2223 Mon Dec 22 00:32:19 datamonkey:2223 Script started on Mon Dec 22 00:32:19 2003 Mon Dec 22 00:32:25 datamonkey:2223 bash-2.05b$ id Mon Dec 22 00:32:25 datamonkey:2223 uid=1005(dba) gid=1005(dba) groups=1005(dba), 92(ssers) Mon Dec 22 00:49:09 datamonkey:8603 bash-2.05b$ vi .bashrc (Tons and tons of garbage) Mon Dec 22 00:49:54 datamonkey:8603 bash-2.05b$ exit Mon Dec 22 00:49:54 datamonkey:8603 Mon Dec 22 00:49:54 datamonkey:8603 Script done on Mon Dec 22 00:49:54 2003 Mon Dec 22 00:49:54 logger (datamonkey,8603) caught signal. Exiting
This looks pretty bad! The problem is that the script command faithfully stores all the input and output in the shell, including all the escape codes that the terminal emulator turns into things like cursor movement and screen refreshes. The problem is particularly acute when the user enters a full screen editor, such as vi. There are two approaches to this problem that help turn the gibberish into useful data. First, this sed script from Unix Power Tools, Third Edition (O'Reilly) will remove simple escape codes from script output.
#!/bin/sh # Public domain. # Put CTRL-M in $m and CTRL-H in $b. # Change \010 to \177 if you use DEL for erasing. eval `echo m=M b=H | tr 'MH' '\015\010'` exec sed "s/$m\$// :x s/[^$b]$b// t x" $*
Run the previous output through this script. You'll see something like:
Mon Dec 22 00:32:19 New logger for datamonkey with pid 2223 Mon Dec 22 00:32:19 datamonkey:2223 Script started on Mon Dec 22 00:32:19 2003 Mon Dec 22 00:32:25 datamonkey:2223 bash-2.05b$ id Mon Dec 22 00:32:25 datamonkey:2223 uid=1005(dba) gid=1005(dba) groups=1005(dba), 92(ssers) Mon Dec 22 00:49:09 datamonkey:8603 bash-2.05b$ vi .bashrc (Still tons of garbage) Mon Dec 22 00:49:54 datamonkey:8603 ESC[Mon Dec 22 00:49:54 datamonkey:8603 bash-2.05b$ exit Mon Dec 22 00:49:54 datamonkey:8603 Mon Dec 22 00:49:54 datamonkey:8603 Script done on Mon Dec 22 00:49:54 2003 Mon Dec 22 00:49:54 logger (datamonkey,8603) caught signal. Exiting
That's a more intelligible version of the output, but the vi session is still scrambled. We can take advantage of the fact that we probably are running the same terminal emulator as the user. If we snip out just the vi session from the log and then cat it to the screen, we get:
This is a normal line in a file Why does this look so bad?? ~ ~ .. many more ~ lines.. ~ ~ ~ :q
That's recognizable as a vi screen. In fact, our screen has been updated several times, once for every time the screen was refreshed in the original session. The final display shows the final state of the vi session.
6.10.5 See Also