(今さら)scriptコマンドで操作ログを取得する

OSの操作ログを取得したい(しなければならない)という要求は、まぁけっこうあるだろう*1
セキュアOSではない従来のLinuxの機能で、比較的容易に実現可能な典型的な方式として、(1) scriptコマンド、(2) sudoコマンド、(3) acctコマンドを用いる3通りがある。この解説は、次の記事がいけてる。
http://www.atmarkit.co.jp/fsecurity/rensai/unix_sec06/unix_sec02.html


いずれも、特権ユーザのログを厳密に取得するものではないが、お手軽なので(感覚的には)けっこう使われているのではないかと思う。
で、ただ今、基盤チームのスミッコメンバなので、めでたく初実装することになった。今回は、シェルの起動時にscriptコマンドを実行することで*2、操作ログを自動的に取得することに。
どこにscriptコマンドを記載すればよいかは、次の記事を参考にすればわかる。
http://www.itmedia.co.jp/enterprise/articles/0803/10/news012.html


今回は、ログインシェル以外のシェル(“SSH”や“sudo -s”などで起動されるシェル)についてもscriptコマンドを実行する必要があるため、/etc/profile や ~/.bash_profile ではなく、/etc/bashrc ファイルや、ユーザごとの ~/.bashrc ファイルに、scriptコマンドを記載してみた。

# 無限ループに陥ります。
script /var/log/history/op_`whoami`_`date '+%Y%m%d%H%M%S'`_$$.log
exit

ところ、、無限ループに。どうも、2006年に同じ目にあっている人がいた(ちょっとうれしい)。が、根本解決はされなかった模様(ちょっとかわいそう)。確かに、実際にやってみないと気がつかないかも。上記のように、単純にscriptコマンドを記載してしまうと、scriptコマンドが起動するシェルも、さらにscriptコマンドを実行し、無限ループになってしまう。そこで、親プロセスがscriptコマンドでない場合(例えば“sshd”や“bash”の場合)にのみ、scriptコマンドを実行するように制御しなければならない。

_script="/usr/bin/script"
_p_proc=`ps aux | grep "${PPID}" | awk '{print $11}'`

# この分岐がポイントです。
if [ "x${_p_proc}" != "x${_script}" ]; then
    ${_script} -q /var/log/history/op_`whoami`_`date '+%Y%m%d%H%M%S'`_$$.log
    exit
fi
unset _script
unset _p_proc

おっけ。


2011/05/24追記

もう少しきちんとしたスクリプトにしないと、バグると判明。例えば、他のプロセスの起動引数に、親プロセスIDと同じ文字列が入ってしまっている場合など。確かに“ps aux | grep "${PPID}"”だけでは弱すぎる(“grep -v grep”すら入れてなかった)。
ちょっときたないが、次のように判定すればよいのかな(先に grep で絞っているのは、パフォーマンスがよいから)。

# 親プロセスが script ではない場合に 0 を返す。
# 例えば、bash や su や sshd などの場合。
is_parent_not_script(){
    while read _pid _p_proc
    do
        if [ "x${_pid}"     = "x${PPID}" ] && \
           [ "x${_p_proc}" != "x${_script}" ]; then
            return 0
        fi
    done << __EOC__
        `ps aux | grep "${PPID}" | awk '{ print $2 " " $11 }'`
    __EOC__
    return 1
}
2011/06/30追記

親プロセスがscriptコマンドでない場合にのみ、scriptコマンドを実行すると、色々問題が。。

まずは、GUIでログインできなくなった。なんとなくわかる。仕方なく、親プロセスが“sshd”、“bash”、“su”の場合にのみ、scriptコマンドを実行することにした。

それでも、sftpなどでログインできなくなる問題が。。親プロセスが“sshd”になるからだ。本当に仕方なく、親プロセスがsshdで、端末がnottyでない場合*3に、scriptコマンドを実行することにした。

*1:自分的には、なかったことがない。

*2:ちなみに、acctコマンドについては、コマンドオプションなどの情報が取得できないため、ダメだと言われたことがある。今回scriptコマンドを採用した理由ではないが。

*3:psコマンドで出力されるプロセス名が sshd: user@notty のようになるので、そこからひろった。