|
|
|
Introduction
|
|
The original sudoscript architecture was a simple response to the
problem of allowing software engineers to run root shells on their
workstations. Generally, these users would be the only ones running
sudoshell on their machine. This was fortunate, because the original
architecture made no allowance for multiple simultaneous
users. There was a single logging FIFO and a single log
file. Multiple users could invoke sudoshell, but all their output
would be thrown together in the log file, without any separation of
the various sessions. As a practical matter, user input could
usually be differentiated by looking at the prompt. But since PS1
is settable by the user, and therefore varies quite a bit, it
couldn't be counted on to allow such differentiation.
|
|
Tommy Smith's Idea
|
|
Some mechanism was required to allow session differentiation. I got
an email from Tommy Smith suggesting that separate log files could be
used to allow identification of sessions. This struck me as a very
good idea. I started to think about how that could be
implemented. Forking a logging daemon per session seemed like it
would be fairly easy. I had a prototype doing that pretty quickly.
About the same time, I was exploring an idea from Adam Morris that
would get rid of sudoscriptd altogether, and replace in with a
forked FIFO server in sudoshell. I took a look at his C code that
implemented this idea, and tried to replicate it in Perl. I ran into
difficulty with keeping the terminal session attached to
script(1). Regardless of whether I implemented the daemon in the
parent or the child, I couldn't keep stdin/stdout attached to the
'system script' call. That, and 22 Dell 1650 servers landing on my
desk stopped my experiments for a time.
|
|
The Second Idea
|
|
When I revisited the code, I decided that I didn't want to get rid
of the daemon, because it allowed me to manage the size of the logs
produced. Using script(1) meant that large quantities of data, far
in excess of that produced by sudo, could be produced by
sudoscript. Though I couldn't predict how much data a particular
user might produce, I was concerned about overflowing logging
partitions. In the 1.0 architecture, the singular daemon kept an
eye on the log file. With multiple daemons, I realized, this job
would be a lot more complicated. I thought through a couple of
schemes that had the front-end daemon orchestrating log file
rotation among the back-end loggers. The main difficulty was that
the daemon in a position to know about all outstanding sessions
didn't "own" the log files. This made it harder to ensure
a timely rotation so that data wasn't dropped, while making it easier
to miss logs that needed compressing because a back-end daemon had
shut down abnormally. I decided that the whole deal was a threat to
overall reliability of sudoscript.
|
I then got the idea of merging all the session data back into
another daemon. This daemon would manage a merger FIFO, and coalesce
all session data, pre-tagged with a session ID, back into a single
log file. The daemon could then manage the log in the same way as
the 1.0 daemon did. The overall architecture was significantly more
complicated, but it looked to me like it could be reliable if
properly implemented. And there were no frills, either. All the
complexity had a specific purpose that no simpler architecture I
could think of would achieve.
|
I also decided that I would use syslog to track session startup and
shutdown. A short digression regarding syslog is in order
here. Since I couldn't control the possibly large quantity of data
sudoscript would log, I was reluctant to use syslog for the
script(1) data itself. When managing my own log, I was able to
trade off between longevity and space in a way that made sense to
me. But if I was using syslog, I could end up imposing that trade off
on a system log file, where it might be less appropriate. For
example, sudoscript 2.0 uses the AUTHPRIV syslog facility. On Red
Hat Linux, the default syslog.conf puts messages with this facility
into /var/log/secure. If sudoscript were to log all of the script(1)
data through to /var/log/secure, then that file would have to turn
over frequently. This could flush other important security data out
of the system more quickly than appropriate. Also, the sheer
quantity of data, combined with the ugliness of script(1) output,
would make these logs harder to read. I could use a different
facility for the data, but another feature of syslog makes me
hesitate to use it. At many sites, particularly those that are Sun
based, remote loghosts are employed. I think it's a really bad idea
to log by-definition sensitive data over the network in the
clear. The large quantity is a factor here as well. Given these
considerations, I have decided to stick with a private log file.
|
|
The 2.0 Architecture
|
|
I have a rather large GIF image of
the 2.0 architecture. If you open this image in a separate window, it
might help you understand the explanations that follow.
|
When sudoscriptd starts, it opens a FIFO called
"/var/run/sudoscriptd/rendevous". It then forks a
child. The parent process is the sudoscript "master
daemon." The child process is the "merger daemon".
It opens a FIFO called "/var/run/sudoscriptd/merge". It
also opens the log file, "/var/log/sudoscript", for
append. The merger goes into a read loop on the merge FIFO. As data
appears on this FIFO, the merger checks the size of the log file and
forks a rotator/compressor if the size exceeds 2 MB. Meanwhile, the
master daemon has gone into a read loop on its FIFO.
|
When sudoshell starts, it contacts the master daemon over the
rendezvous FIFO. It sends a string like "HELO hbo
12345". The latter two fields are the username that invoked
sudoshell (taken from the sudo environment, if sudoshell was run, or
ran itself with sudo, or just 'root' if it was invoked with
privilege) and sudoshell's own process ID. Sudoshell then installs
a SIGHUP handler and calls Posix::pause to go to sleep. The master
daemon spawns a logger for the sudoshell session. This child opens
the merge FIFO for write, and creates a session FIFO using the
username and PID derived from the HELO string. It then signals the
PID given with a SIGHUP, and goes goes into a loop reading the
session FIFO, tagging the data received with the username and PID
and writing it to the merge FIFO. Sudoshell wakes up when it
receives the SIGHUP, and invokes script(1) on the session FIFO,
whose name it derives from its own username and PID.
|
When the user exits from script(1), sudoshell sends another string
to the frontend daemon, identical to the first, except that
"HELO" is replaced with "GDBY". Sudoshell then
exits. When the master daemon receives the GDBY message, it looks up
the session in a table it maintains. It uses the child PID stored
there to signal the session daemon with a SIGHUP. It then removes
that session from its table. The session daemon cleans up and exits
when it receives this signal.
|
When the master daemon receives a SIGHUP, it loops through its
session table and signals each associated logger with a SIGHUP.
It then does the same for the merger daemon.
|
|
Conclusion
|
|
So, that's the complete life-cycle of the 2.0
sudoscript system. As noted above, important events are logged
to syslog with facility AUTHPRIV. This provides a concise record
of session activity, which can be used to search the larger log file
for particular sessions.
|
| |
|
|
Document Maintainer: Howard Owen
(hbo@egbok.com)
Last Updated 6/22/03 5:56 PM |