Linux
RHEL
audit

RHELのAudit設定(ファイルアクセス監査)

システム上に存在する個人情報の扱いが厳しくなった昨今、そこにセンシティブなデータがあるなら、「いつ・だれが・なにを・どうした・どうなった」という記録を取り、不正なアクセスがなかったかを監査しなければいけない、というセキュリティ要件が一般的になっている。
というわけで、Linux標準機能(audit)を使用したファイルアクセス監査の設定と、レポート出力(加工)の覚書。

前提環境

  • RHEL7.3(maipo)
uname -a
Linux app 3.10.0-514.el7.x86_64 #1 SMP Wed Oct 19 11:24:13 EDT 2016 x86_64 x86_64 x86_64 GNU/Linux

アーキテクチャ

マニュアルに記載してある(英語版のほうが図に違和感がない気がする)。
簡単にいえば、カーネルの機能でシステムコール実行時にフィルタリングしつつ、"Audit daemon"が実際のログ記録をカーネルから情報取得して行う。
このフィルタリングの説明がわかりにくい。

システムコールを受け、これを user、task、または exit のいずれかのフィルターで振り分けます。システムが exclude フィルターを通過すると、前述のフィルターの 1 つに掛けられます。

man audit.rulesで確認すると。

The Linux kernel has 4 rule matching lists or filters as they are sometimes called. They are: task, exit, user, and exclude. The task list is checked only during the fork or clone syscalls. It is rarely used in practice.
The exit filter is the place where all syscall and file system audit requests are evaluated.
The user filter is used to filter (remove) some events that originate in user space. By default, any event originating in user space is allowed.So, if there are some events that you do not want to see, then this is a place where some can be removed. See auditctl(8) for fields that are valid.
The exclude filter is used to exclude certain events from being emitted. The msgtype field is used to tell the kernel which message types you do not want to record. This filter can remove the event as a whole and is not selective about any other attribute. The user and exit filters are better suited to selectively auditing events.

要約しようとしてみると、
task:forkcloneシステムコールのときのみで実際には使われない(誰得?)
exit:全てのシステムコールとファイルシステム監査要求が評価されたとき
user:ユーザ空間でのイベントをフィルタリングするのに使われる(uid, auid, gid, pid, subj_user, subj_role, subj_type, subj_sen, subj_clr, and msgtype)
exclude: 特定のイベントの発生を抑止する。msgtypeフィールドを除外条件に指定できる。ただし、そのタイプのイベントは全て対象外になるので、実際にはuserやexitフィルタのが使いやすい
となる。
改めて図示してみる。

461DF950-530D-4EFD-8F9E-36BED2D00126.png

整理すると、アプリケーションからシステムコールが呼出されると、カーネル内で4つのフィルタが働く。userはuidやpidなどのユーザ情報によるフィルタリング、taskは特定のシステムコール(fork, clone)のみ有効なフィルタリング、exitは全システムコールに対するフィルタリング、excludeは特定のイベント条件(msgtype)によるフィルタリングとなる。user, task, exitフィルタは個別に動作し、これらからのイベントはexcludeフィルタに送られ、そこで除外されなければユーザモードで起動しているauditdへ送信される。

ps -ef | grep auditで見ると、"[kauditd]"と"/sbin/auditd -n"がデーモンとして存在している。
strace -p $(pidof auditd)してみると、AF_NETLINK(カーネル・ユーザ空間通信)を介してソケット通信(recvfrom)して、ログに書き込み(write)するだけの比較的簡単なお仕事しかしていない。つまりauditdは純粋なロギング担当でしかないので、後でみる設定ファイルにもログファイル関連のものくらいしかない。

strace結果抜粋
epoll_wait(9, {{EPOLLIN, {u32=3, u64=4294967299}}}, 64, 59743) = 1
recvfrom(3, "\263\0\0\0L\4\0\0\0\0\0\0Z\31\0\0audit(1541119988"..., 8988, MSG_DONTWAIT, {sa_family=AF_NETLINK, pid=0, groups=00000000}, [12]) = 195
write(4, "type=USER_AUTH msg=audit(1541119"..., 199) = 199

お試し(システムコールの監査)

テスト時はauditctlでルールを追加する。基本的な文法は、auditctl -a action,list -S syscall -F filterkey=value -k keynameとなる。
actionには"always"(常にイベント発生)または"never"(イベントなし)のいずれかを指定する。
syscallには対象のシステムコールを指定する。
filterkey=valueには条件を指定する。指定できる条件(filterkey)はフィルタタイプによって異なるが、userなら"uid=0"(root実行)、"uid>=1000"(一般ユーザ(ただしuid=1000未満の一般ユーザはいない前提))など。

ここまでを一通り使って実際に試してみる(taskは使うことがなさそうなので無視)。

お題その1:一般ユーザがkillシステムコールを呼び出した

システムコール一覧はman syscallsからも確認できる。

auditctl -D                                                                       # 条件クリア
auditctl -a always,exit -F arch=x86_64 -F "uid>=1000" -S kill -k SYSCALL_KILL     # 一般ユーザ(uidが1000以上)がkill実行時
auditctl -l                                                                       # ルールをリスト

アーキテクチャ(arch)フィルタを指定しないと、指定したほうがいいよ、という警告メッセージが出力される。そして"-F arch=x86_64"は"-S"よりも前に指定しなければならないので、フィルタ条件(-F)を先に書くとおぼえてしまったほうがよさそう。

一般ユーザ(uid=1000)でkillを発行する。

sleep 10 &
kill $!

監査ログは"/var/log/audit/audit.log"がデフォルト出力先なので、これを見ればいいのだが、テキストファイルではあるもののHuman Friendlyとは言い難い。なので、ausearchという検索ツールを使用する。

ausearch -k SYSCALL_KILL --interpret --input-logs

"--interpret"(短縮形は"-i")はuidなどの数値を読みやすくHuman Friendlyな文字列表現に変換してくれる(逆引名前解決)。"--input-logs"は"auditd.conf"から生ログの場所を読込むするオプションで、任意のログファイルを入力とする場合は、"--input"(短縮形は"-if")でパスを指定する。なお、"--input-logs"オプションはcronから起動するときは必須とmanページに記載がある。strace ausearch -k SYSCALL_KILLの結果では指定しなくてもopen("/etc/audit/auditd.conf", O_RDONLY|O_NOFOLLOW) = 3と出てくるが、それはともかくausearchを実行すると、このような出力が得られる。

----
type=CONFIG_CHANGE msg=audit(2018年11月02日 14:06:02.683:1548) : auid=root ses=1 op="add_rule" key=SYSCALL_KILL list=exit res=yes 
----
type=OBJ_PID msg=audit(2018年11月02日 14:06:21.696:1549) : opid=7355 oauid=root ouid=piro oses=73 ocomm=sleep 
type=SYSCALL msg=audit(2018年11月02日 14:06:21.696:1549) : arch=x86_64 syscall=kill success=yes exit=0 a0=0x1cbb a1=SIGTERM a2=0x0 a3=0x1cbb items=0 ppid=7047 pid=7048 auid=root uid=piro gid=piro euid=piro suid=piro fsuid=piro egid=piro sgid=piro fsgid=piro tty=pts1 ses=73 comm=bash exe=/usr/bin/bash key=SYSCALL_KILL 

出力は"----"でイベントごとに区切られる。つまり一つのイベントにつき複数の行レコードからなる。ここから「いつ・どこで・だれが・なにを・どうした・どうなった」がわかる。
いつ:msgのaudit()内にタイムスタンプ(ミリ秒まで)とイベントIDが出力されている
どこで:2行目のtty=pts1でこの仮想ターミナルだとわかる
だれが:2行目にuid, gid, euid, suid, fsuid, egid, sgid, fsgidがバッチリ出力されている
なにを:1行目にOBJ_PID(対象のプロセスID)として情報がでているocomm(つまりkillされたプロセス)がsleepだとわかる
どうした:2行目のsyscall=killからkillが発行されたこと、またcomm=bashとexe=/usr/bin/bashからシェルビルトインのkillが発行されたとわかる(明示的に/usr/bin/killを使用すればcomm=kill、exe=/usr/bin/killになる)
どうなった:2行目のsyccess=yesから成功したとわかる

お題その2:特定のユーザは免除

ルールを追加して特定のユーザ(uid=1000)に関する監査ログは出力しないようにする。
-FによるフィルタはAND条件なので、たとえばauditctl -a always,exit -F arch=x86_64 -F "uid>=1000" -F "uid!=1000" -S kill -k SYSCALL_KILLとすることで可能。
なお、以下のようにしても意図した結果にはならないので注意。

auditctl -a never,user -F uid=1000 -S kill -k SYSCALL_KILL
Error: syscall auditing being added to user list

エラーになってauditctl -lでみても追加できていない。auditctl -a never,user -F uid=1000は追加可能だが、意図した結果にはならず、当該ユーザのkill実行が監査ログに出力される。
これは先に図示したとおり、user,task,exitの各フィルタからexlcludeを通ってauditdへイベント送信されるので、userでイベントが発生しなくてもexitで発生する(各システムコール実行に対し、userで弾かれたらその他のフィルタを通過しないというわけではない)。

擬似コード(あくまでもイメージ)
interface syscallFilter { boolean doFilter(SystemCall syscallObj); }

if(userFilterImpl.doFilter(syscallObj)) {              // userフィルタを通す
    if(excludeFilterImpl.doFilter(syscallObj)) {       // excludeフィルタを通す
        sendToAuditd(syscallObj);                      // Auditdへ送信 
    }
}
if(taskFilterImpl.doFilter(syscallObj)) {              // taskフィルタを通す
    if(excludeFilterImpl.doFilter(syscallObj)) {       // excludeフィルタを通す
        sendToAuditd(syscallObj);                      // Auditdへ送信 
    }
}
if(exitFilterImpl.doFilter(syscallObj)) {              // execフィルタを通す
    if(excludeFilterImpl.doFilter(syscallObj)) {       // excludeフィルタを通す
        sendToAuditd(syscallObj);                      // Auditdへ送信 
    }
}

audit設定関連

auditdの設定

前述のようにauditdはログ出力しかしない。"/etc/audit/auditd.conf"でログ関連の設定が可能(出力先ファイル、ファイルサイズ、ファイルシステム空き容量が逼迫したときの動作など)。

監査設定の永続化

auditctlで指定した内容は、auditd終了時に消える(/etc/audit/audit-stop.rulesに監査無効化とルール全削除設定がある)。起動時は設定ファイルの内容を読み込むので、永続的に監査ルールを設定するには"/etc/audit/audit.rules"を編集する。
書式はauditctlの引数そのまま。

お試し(ファイルの監査設定)

設定

"/secret"を監査対象とし、全てのアクセス(read, write, execute, attribute change)を拾うことにする。auditctlで、"-w"でwatch対象のファイルシステムオブジェクト(つまりファイルやディレクトリ)を指定し、"-p"で対象のアクション(read, write, execute, attribute change)を指定する。なお、ここでも"-F"によるフィルタは可能なので、例えば特定のユーザは除外したければ-F uid!=1000などとすればよい。

mkdir /secret
auditctl -D # 一旦初期化
auditctl -w /secret -p rwxa -k access_to_secret
auditctl -l
設定の永続化
echo '-w /secret -p rwxa -k access_to_secret' >> /etc/audit/audit.rules

監査ログ実録

なにをするとどんなログが出るのか確認しておく(成功と失敗)。

1.ファイルの新規作成(成功)

echo '#!/usr/bin/bash' > /secret/hoge
ausearch
type=PATH msg=audit(2018年11月02日 16:44:21.978:1745) : item=1 name=/secret/hoge inode=52069726 dev=fd:00 mode=file,644 ouid=root ogid=root rdev=00:00 objtype=CREATE 
type=PATH msg=audit(2018年11月02日 16:44:21.978:1745) : item=0 name=/secret/ inode=52069722 dev=fd:00 mode=dir,755 ouid=root ogid=root rdev=00:00 objtype=PARENT 
type=CWD msg=audit(2018年11月02日 16:44:21.978:1745) :  cwd=/root 
type=SYSCALL msg=audit(2018年11月02日 16:44:21.978:1745) : arch=x86_64 syscall=open success=yes exit=3 a0=0x13cabf0 a1=O_WRONLY|O_CREAT|O_TRUNC a2=0666 a3=0xfffffff0 items=2 ppid=2271 pid=2277 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=pts0 ses=1 comm=bash exe=/usr/bin/bash key=access_to_secret 

2.ファイルの更新(成功)

echo 'echo hogehoge' >>/secret/hoge
ausearch
type=PATH msg=audit(2018年11月02日 17:02:58.854:1774) : item=1 name=/secret/hoge inode=52069726 dev=fd:00 mode=file,644 ouid=root ogid=root rdev=00:00 objtype=NORMAL 
type=PATH msg=audit(2018年11月02日 17:02:58.854:1774) : item=0 name=/secret/ inode=52069722 dev=fd:00 mode=dir,755 ouid=root ogid=root rdev=00:00 objtype=PARENT 
type=CWD msg=audit(2018年11月02日 17:02:58.854:1774) :  cwd=/root 
type=SYSCALL msg=audit(2018年11月02日 17:02:58.854:1774) : arch=x86_64 syscall=open success=yes exit=3 a0=0x1492c00 a1=O_WRONLY|O_CREAT|O_APPEND a2=0666 a3=0xfffffff0 items=2 ppid=2271 pid=2277 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=pts0 ses=1 comm=bash exe=/usr/bin/bash key=access_to_secret 

3.ファイルの属性更新(成功)

chmod 755 /secret/hoge
ausearch
type=PATH msg=audit(2018年11月02日 17:09:17.747:1777) : item=0 name=/secret/hoge inode=52069726 dev=fd:00 mode=file,644 ouid=root ogid=root rdev=00:00 objtype=NORMAL 
type=CWD msg=audit(2018年11月02日 17:09:17.747:1777) :  cwd=/root 
type=SYSCALL msg=audit(2018年11月02日 17:09:17.747:1777) : arch=x86_64 syscall=fchmodat success=yes exit=0 a0=0xffffffffffffff9c a1=0x1c75130 a2=0755 a3=0x7ffe1de00860 items=1 ppid=2277 pid=8090 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=pts0 ses=1 comm=chmod exe=/usr/bin/chmod key=access_to_secret 

4.ファイルの実行(成功)

/secret/hoge
ausearch
type=PATH msg=audit(2018年11月02日 17:10:23.819:1785) : item=2 name=/lib64/ld-linux-x86-64.so.2 inode=1565 dev=fd:00 mode=file,755 ouid=root ogid=root rdev=00:00 objtype=NORMAL 
type=PATH msg=audit(2018年11月02日 17:10:23.819:1785) : item=1 name=/usr/bin/bash inode=50334042 dev=fd:00 mode=file,755 ouid=root ogid=root rdev=00:00 objtype=NORMAL 
type=PATH msg=audit(2018年11月02日 17:10:23.819:1785) : item=0 name=/secret/hoge inode=52069726 dev=fd:00 mode=file,755 ouid=root ogid=root rdev=00:00 objtype=NORMAL 
type=CWD msg=audit(2018年11月02日 17:10:23.819:1785) :  cwd=/root 
type=EXECVE msg=audit(2018年11月02日 17:10:23.819:1785) : argc=2 a0=/usr/bin/bash a1=/secret/hoge 
type=SYSCALL msg=audit(2018年11月02日 17:10:23.819:1785) : arch=x86_64 syscall=execve success=yes exit=0 a0=0x14a0520 a1=0x149c540 a2=0x148b6f0 a3=0x7fff81c58140 items=3 ppid=2277 pid=8097 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=pts0 ses=1 comm=hoge exe=/usr/bin/bash key=access_to_secret 
----
type=PATH msg=audit(2018年11月02日 17:10:23.820:1786) : item=0 name=/secret/hoge inode=52069726 dev=fd:00 mode=file,755 ouid=root ogid=root rdev=00:00 objtype=NORMAL 
type=CWD msg=audit(2018年11月02日 17:10:23.820:1786) :  cwd=/root 
type=SYSCALL msg=audit(2018年11月02日 17:10:23.820:1786) : arch=x86_64 syscall=open success=yes exit=3 a0=0x1a66fa0 a1=O_RDONLY a2=0x65676f682f7465 a3=0x8 items=1 ppid=2277 pid=8097 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=pts0 ses=1 comm=hoge exe=/usr/bin/bash key=access_to_secret 

5.ファイルの読込み(成功)

cat /secret/hoge
ausearch
type=PATH msg=audit(2018年11月02日 17:11:51.707:1787) : item=0 name=/secret/hoge inode=52069726 dev=fd:00 mode=file,755 ouid=root ogid=root rdev=00:00 objtype=NORMAL 
type=CWD msg=audit(2018年11月02日 17:11:51.707:1787) :  cwd=/root 
type=SYSCALL msg=audit(2018年11月02日 17:11:51.707:1787) : arch=x86_64 syscall=open success=yes exit=3 a0=0x7ffdf606276e a1=O_RDONLY a2=0x1fffffffffff0000 a3=0x7ffdf60602e0 items=1 ppid=2277 pid=8100 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=pts0 ses=1 comm=cat exe=/usr/bin/cat key=access_to_secret 

6.ファイルの削除(成功)

rm /secret/hoge
ausearch
type=PATH msg=audit(2018年11月02日 17:12:26.603:1788) : item=1 name=/secret/hoge inode=52069726 dev=fd:00 mode=file,755 ouid=root ogid=root rdev=00:00 objtype=DELETE 
type=PATH msg=audit(2018年11月02日 17:12:26.603:1788) : item=0 name=/secret/ inode=52069722 dev=fd:00 mode=dir,755 ouid=root ogid=root rdev=00:00 objtype=PARENT 
type=CWD msg=audit(2018年11月02日 17:12:26.603:1788) :  cwd=/root 
type=SYSCALL msg=audit(2018年11月02日 17:12:26.603:1788) : arch=x86_64 syscall=unlinkat success=yes exit=0 a0=0xffffffffffffff9c a1=0xf8c100 a2=0x0 a3=0x7ffc6d1e4ad0 items=2 ppid=2277 pid=8104 auid=root uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=pts0 ses=1 comm=rm exe=/usr/bin/rm key=access_to_secret 

7.ファイルの新規作成(失敗)

echo '#!/usr/bin/bash' > /secret/hogehoge
ausearch
type=PATH msg=audit(2018年11月02日 17:18:29.631:1809) : item=0 name=/secret/hoge inode=52069727 dev=fd:00 mode=file,700 ouid=root ogid=root rdev=00:00 objtype=NORMAL 
type=CWD msg=audit(2018年11月02日 17:18:29.631:1809) :  cwd=/home/piro 
type=SYSCALL msg=audit(2018年11月02日 17:18:29.631:1809) : arch=x86_64 syscall=open success=no exit=EACCES(許可がありません) a0=0x1b29f20 a1=O_WRONLY|O_TRUNC a2=0x1b6 a3=0xfffffff0 items=1 ppid=7047 pid=7048 auid=root uid=piro gid=piro euid=piro suid=piro fsuid=piro egid=piro sgid=piro fsgid=piro tty=pts1 ses=73 comm=bash exe=/usr/bin/bash key=access_to_secret 
----
type=PATH msg=audit(2018年11月02日 17:18:29.631:1808) : item=1 name=/secret/hoge inode=52069727 dev=fd:00 mode=file,700 ouid=root ogid=root rdev=00:00 objtype=NORMAL 
type=PATH msg=audit(2018年11月02日 17:18:29.631:1808) : item=0 name=/secret/ inode=52069722 dev=fd:00 mode=dir,755 ouid=root ogid=root rdev=00:00 objtype=PARENT 
type=CWD msg=audit(2018年11月02日 17:18:29.631:1808) :  cwd=/home/piro 
type=SYSCALL msg=audit(2018年11月02日 17:18:29.631:1808) : arch=x86_64 syscall=open success=no exit=EACCES(許可がありません) a0=0x1b29f20 a1=O_WRONLY|O_CREAT|O_TRUNC a2=0666 a3=0xfffffff0 items=2 ppid=7047 pid=7048 auid=root uid=piro gid=piro euid=piro suid=piro fsuid=piro egid=piro sgid=piro fsgid=piro tty=pts1 ses=73 comm=bash exe=/usr/bin/bash key=access_to_secret 

8.ファイルの更新(失敗)

echo 'echo hogehoge' >>/secret/hoge
ausearch
type=PATH msg=audit(2018年11月02日 17:19:42.724:1822) : item=0 name=/secret/hoge inode=52069727 dev=fd:00 mode=file,744 ouid=root ogid=root rdev=00:00 objtype=NORMAL 
type=CWD msg=audit(2018年11月02日 17:19:42.724:1822) :  cwd=/home/piro 
type=SYSCALL msg=audit(2018年11月02日 17:19:42.724:1822) : arch=x86_64 syscall=open success=no exit=EACCES(許可がありません) a0=0x1b2a040 a1=O_WRONLY|O_APPEND a2=0x1b6 a3=0xfffffff0 items=1 ppid=7047 pid=7048 auid=root uid=piro gid=piro euid=piro suid=piro fsuid=piro egid=piro sgid=piro fsgid=piro tty=pts1 ses=73 comm=bash exe=/usr/bin/bash key=access_to_secret 
----
type=PATH msg=audit(2018年11月02日 17:19:42.724:1821) : item=1 name=/secret/hoge inode=52069727 dev=fd:00 mode=file,744 ouid=root ogid=root rdev=00:00 objtype=NORMAL 
type=PATH msg=audit(2018年11月02日 17:19:42.724:1821) : item=0 name=/secret/ inode=52069722 dev=fd:00 mode=dir,755 ouid=root ogid=root rdev=00:00 objtype=PARENT 
type=CWD msg=audit(2018年11月02日 17:19:42.724:1821) :  cwd=/home/piro 
type=SYSCALL msg=audit(2018年11月02日 17:19:42.724:1821) : arch=x86_64 syscall=open success=no exit=EACCES(許可がありません) a0=0x1b2a040 a1=O_WRONLY|O_CREAT|O_APPEND a2=0666 a3=0xfffffff0 items=2 ppid=7047 pid=7048 auid=root uid=piro gid=piro euid=piro suid=piro fsuid=piro egid=piro sgid=piro fsgid=piro tty=pts1 ses=73 comm=bash exe=/usr/bin/bash key=access_to_secret 

9.ファイルの属性更新(失敗)

chmod 755 /secret/hoge
ausearch
type=PATH msg=audit(2018年11月02日 17:20:19.040:1830) : item=0 name=/secret/hoge inode=52069727 dev=fd:00 mode=file,744 ouid=root ogid=root rdev=00:00 objtype=NORMAL 
type=CWD msg=audit(2018年11月02日 17:20:19.040:1830) :  cwd=/home/piro 
type=SYSCALL msg=audit(2018年11月02日 17:20:19.040:1830) : arch=x86_64 syscall=fchmodat success=no exit=EPERM(許可されていない操作です) a0=0xffffffffffffff9c a1=0x13ba130 a2=0666 a3=0x7ffdd5982fc0 items=1 ppid=7048 pid=8138 auid=root uid=piro gid=piro euid=piro suid=piro fsuid=piro egid=piro sgid=piro fsgid=piro tty=pts1 ses=73 comm=chmod exe=/usr/bin/chmod key=access_to_secret 

10.ファイルの実行(失敗)

/secret/hoge
ausearch
type=PATH msg=audit(2018年11月02日 17:21:04.085:1832) : item=0 name=/secret/hoge inode=52069727 dev=fd:00 mode=file,700 ouid=root ogid=root rdev=00:00 objtype=NORMAL 
type=CWD msg=audit(2018年11月02日 17:21:04.085:1832) :  cwd=/home/piro 
type=SYSCALL msg=audit(2018年11月02日 17:21:04.085:1832) : arch=x86_64 syscall=execve success=no exit=EACCES(許可がありません) a0=0x1af29d0 a1=0x1b29950 a2=0x1af22a0 a3=0x7ffcc7db2c60 items=1 ppid=7048 pid=8143 auid=root uid=piro gid=piro euid=piro suid=piro fsuid=piro egid=piro sgid=piro fsgid=piro tty=pts1 ses=73 comm=bash exe=/usr/bin/bash key=access_to_secret 

11.ファイルの読込み(失敗)

cat /secret/hoge
ausearch
type=PATH msg=audit(2018年11月02日 17:21:24.223:1833) : item=0 name=/secret/hoge inode=52069727 dev=fd:00 mode=file,700 ouid=root ogid=root rdev=00:00 objtype=NORMAL 
type=CWD msg=audit(2018年11月02日 17:21:24.223:1833) :  cwd=/home/piro 
type=SYSCALL msg=audit(2018年11月02日 17:21:24.223:1833) : arch=x86_64 syscall=open success=no exit=EACCES(許可がありません) a0=0x7ffc753ad7ca a1=O_RDONLY a2=0x1fffffffffff0000 a3=0x7ffc753aca70 items=1 ppid=7048 pid=8147 auid=root uid=piro gid=piro euid=piro suid=piro fsuid=piro egid=piro sgid=piro fsgid=piro tty=pts1 ses=73 comm=cat exe=/usr/bin/cat key=access_to_secret 

12.ファイルの削除(失敗)

rm /secret/hoge
ausearch
type=PATH msg=audit(2018年11月02日 17:21:41.110:1834) : item=1 name=/secret/hoge inode=52069727 dev=fd:00 mode=file,700 ouid=root ogid=root rdev=00:00 objtype=DELETE 
type=PATH msg=audit(2018年11月02日 17:21:41.110:1834) : item=0 name=/secret/ inode=52069722 dev=fd:00 mode=dir,755 ouid=root ogid=root rdev=00:00 objtype=PARENT 
type=CWD msg=audit(2018年11月02日 17:21:41.110:1834) :  cwd=/home/piro 
type=SYSCALL msg=audit(2018年11月02日 17:21:41.110:1834) : arch=x86_64 syscall=unlinkat success=no exit=EACCES(許可がありません) a0=0xffffffffffffff9c a1=0x1bc6100 a2=0x0 a3=0x7ffee9ba15e0 items=2 ppid=7048 pid=8149 auid=root uid=piro gid=piro euid=piro suid=piro fsuid=piro egid=piro sgid=piro fsgid=piro tty=pts1 ses=73 comm=rm exe=/usr/bin/rm key=access_to_secret 

13.おまけ(監査にひっかからないようにするには)

ここまで見てきたとおり、ファイルシステムオブジェクトに対するシステムコールが記録される。ということは、ファイルシステム(に対するシステムコール)を介さないアクセスについては記録されないということになる。監査記録対象ディレクトリが個別のファイルシステムで、"/dev/vgsec/lvsec"というデバイス上に配置されているとしたら、例えば以下の行為は記録されない。

dd if=/dev/vgsec/lvsec of=/path/to/noaudit/directory/secretdata.data

レポーティング

audit.log自体を保存しておいてaureportで読めばそこそこHuman Friendlyであるものの、実運用では必要な事項(つまりいつどこでだれがなにをどうしてどうなった)のレポートのみを保存しておくということもある。

aureportでサマリを表示

aureportを使うとサマリを出力できる。

ausearch -k access_to_secret | aureport -f -i  
File Report
===============================================
# date time file syscall success exe auid event
===============================================
1. 2018年11月02日 16:36:39 /secret open no /usr/bin/touch root 1707
<以下略>

いつ・なにを・どうした・どうなったかはわかるが、誰が・どこで、という重要な情報が抜け落ちる(auidは監査者のuidであって実行者ではない)。さらに"--summary"をつけると件数と対象(なにを)しか出力されない(成功・失敗もわからない)。

というわけで、実運用的にはausearchの結果をフラット化(1行1イベントに加工)し、別途集計し直すという必要が生じてくる。このあたりフォーマット指定できるようにするなど、aureportコマンドの機能向上が望まれる。Javaでやるとしたらということで練習を兼ねてざっと書いてみたがあまり実用に耐えるレベルではない。。。

cls.png

1件の監査イベントがAuditEventの1インスタンスに相当する。監査イベントには複数行の記録があるので、それらをArrayListで持たせる。というデータ構造にしてみた。
細かなイベントの属性値はHashMapに放り込んで、監査レポートに必要な情報(いつ・どこで・だれが・なにを・どうした・どうなった)を返すメソッドを定義し、PATHやSYSCALL等のクラス実装する。
厄介なのは実際のログファイル(aureportの出力ファイル)を各インスタンスのフィールドにセットするところだが、ここは昔よくperlを書いていた感じで、正規表現と後方参照を駆使して半ば強引に取り出している。

以下、Javaでの正規表現の使い方やsplitといったperlではおなじみの機能の使い方の覚書(ちゃんとテストはしていない)。

監査行から年月日時分秒ミリ秒を取出す
    public static Date getTimestampFromLine(String line) {
        String exp = "msg=audit\\((\\d{4})\\D*(\\d{2})\\D*(\\d{2})\\D* (\\d{2}):(\\d{2}):(\\d{2})\\.(\\d{3}):(\\d*)";   //年、月、日、時、分、秒、ミリ秒、イベントIDを取得する正規表現
        Pattern p = Pattern.compile(exp);
        Matcher m = p.matcher(line);
        if (m.find()) {
            Calendar cal = Calendar.getInstance();
            cal.setTimeZone(TimeZone.getDefault());
            cal.set(
                    Integer.parseInt(m.group(1)),
                    Integer.parseInt(m.group(2)) - 1,
                    Integer.parseInt(m.group(3)),
                    Integer.parseInt(m.group(4)),
                    Integer.parseInt(m.group(5)),
                    Integer.parseInt(m.group(6)));
            cal.setTimeInMillis(cal.getTimeInMillis() + Integer.parseInt(m.group(7)));
            Date timestamp = cal.getTime();
            return timestamp;
        } else {
            return null;
        }
    }
イベントの属性HashMapを作る
    private static HashMap<String, String> getAuditEventItemAttrFromLine(String line) {
        String exp = "^.*\\) : *(.*)$";                 //個別属性文字列部分を取得する正規表現
        Pattern p = Pattern.compile(exp);
        Matcher m = p.matcher(line);
        if(m.find()) {
            String content = m.group(1);
            HashMap<String, String> attr = new HashMap<String, String>();
            for(String str : content.split(" ")) {
                String[] keyVal = str.split("=");
                attr.put(keyVal[0], keyVal[1]);
            }
            return attr;
        } else {
            return null;
        }
    }

有用なリソース(manページ)

man auditctl
man audit.rules
man syscalls
man auditd.conf