[Oracle Linux] OS標準コマンドでOSの操作ログを取得する

# はじめに

表題のとおりなのだが、3rdパーティ製品を使わずに、OSの監査ログを取得する方法を考えてみる。

調べてみると、方法としては以下の3つみたい

- scriptコマンド
- psacctツール
- audit

## 検討
### script vs psacct
scriptとpsacctの比較は、[クラメソさんの記事](https://dev.classmethod.jp/articles/logging_operation_using_script_and_psacct/)を参考にしました。

scriptとpsacctの比較の場合、scriptコマンドの方がよさそう。
psacctは設定が楽なのはいいんだけど、調整可能項目が少ない。コマンド引数と実行結果がわからないのも、実用を考えるととても微妙。デーモンプロセスがスクリプトからコマンドたくさん実行していると、そのコマンドも大量に記録されるので、特定のインタラクティブ・セッションから、何のコマンドが実行されたかを見るのがつらい。

ただ、scriptは改竄が容易なのがネック。その点、psacctはrootでなければ改竄できない。

### script vs audit
auditでの設定方法は、RHELの[ナレッジベース(1193303)](https://access.redhat.com/solutions/1193303)を参考にしました。

auditのログは、rootでなければ改竄できないのと、いつ・誰が・何したをaureportで一覧表示できるのはよい。ただ、「何した」の部分がめちゃくちゃわかりずらいのだよね。やっぱり。Backspaceやら十字キーとかの操作が入ってくると最終的に実行した文字列の判別が非常にしんどい。

そういう意味では、scriptも見ずらいのだけど、<b>scriptreplayコマンドを使うとめっちゃ見やすいのを知って、がぜんscript推しになってきた。</b> scriptreplayは、[RHELのナレッジベース(2142441)](https://access.redhat.com/solutions/2142441)を参考にしました。

## scriptログの改ざん対策を考える
scriptの問題は、改ざんが容易なこと。rootになれたら改ざんできてしまうのは、他のツールも同じなので考えない。それを気にする必要がある場合は、おとなしく3rd Party製品の利用を検討する。

これを緩和するために、ログの作成と同時にchattr +a して追記しかできないようにすればいいんじゃない？と思いついて、調べてみた。

これも、[RHELのナレッジベース(911493)](https://access.redhat.com/solutions/911493)からヒントをもらったのだが、inotifywaitコマンドを利用して、ファイルの作成をトリガーにコマンド実行できそう。

これを使って、ログイン→scriptコマンドの自動実行→ログファイルにchattr +aして追記しかできないようにしてみる

## やってみる

### inotify-toolsのインストール
デフォルトでは入ってなかったのでインストールする

```CONSOLE
# yum install inotify-tools
```

### ディレクトリ作成
操作ログを出力するディレクトリを作成。すべてのユーザがファイルを作成できるが、他のユーザが作成したファイルを削除できないようにスティッキービットを立てる

```CONSOLE
# mkdir /var/log/ope_log
# chmod 1777 /var/log/ope_log
```

### ログイン時にscriptコマンドが自動実行されるようにする

```CONSOLE
# cd /etc/profile.d/
# vi start_opelog.sh
```

以下のようなスクリプトを書く。

- scriptコマンドの実行対象を、以下の通りにする。
 - 親プロセスのPIDがsshdの場合 (そうしないと、start_opelog.shの実行がループしちゃう)
 - UIDが1000または5000以上 (OCIのOELつかってるので、1000はopcユーザ。その他ログイン用のユーザのuidを5000以上で作ってると仮定)

- ログファイル名にユーザ名と日付、SSHクライアントIPを埋め込んで、いつ・誰が・どこからをファイル名で判別できるようにする。
- scriptコマンドから抜けたら、exitしてセッションが終了するようにする。

```SHELL
# ---- start_opelog.sh ----
OPE_LOG_DIR=/var/log/ope_log

parent_process=$(ps -eo pid,comm |awk '$1=="'$PPID'" {print $2}')

time_prefix=$(date '+%Y%m%d%H%M%S')

# Get Client IP Address
if [[ -z ${SSH_CLIENT} ]]; then
    client_ip='UNKNOWN'
else
    client_ip=$(echo ${SSH_CLIENT} |grep -E -o -e "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
    if [[ -z ${client_ip}  ]]; then
        client_ip='UNKNOWN'
    fi
fi

log_name=${OPE_LOG_DIR}/$(whoami)_${time_prefix}_${client_ip}.log
timelog_name=${OPE_LOG_DIR}/$(whoami)_${time_prefix}_${client_ip}.time

# Check if parent process is sshd or not
is_parent_sshd() {
    if [[ ${parent_process} =~ sshd ]]; then
        return 0
    else
        return 1
    fi
}

# Check user uid
is_target_uid() {
    if [[ ${UID} -eq 1000 ]]; then
        return 0
    elif [[ ${UID} -ge 5000 ]]; then
        return 0
    else
        return 1
    fi
}

# If condition matches, start script and exit when script command ends
if is_parent_sshd && is_target_uid; then
  LANG=en_US.UTF-8; script -f -q -t 2>${timelog_name} ${log_name}
  exit
fi
```

ここまでで、ログイン用ユーザからログインした時にスクリプトログが取得できるようになっているはず
んで、このままだと改竄が容易なので、操作ログ出力先ディレクトリをinotifywaitで監視して、CREATE発生時にchattr +aするスクリプトをsystemdのサービスとして実装してみる。

## ファイル属性変更用のスクリプト作成

スクリプトを作成する

```CONSOLE
# mkdir /usr/local/script
# vi /usr/local/script/chattr_ope_log.sh
```

こんな感じinotifywait でcreateイベントだけ拾ってファイルが存在したらchattr +aする。

```SHELL
#!/bin/bash

OPE_LOG_DIR=/var/log/ope_log

while read create_event
do
    target_dir=$(echo ${create_event} |awk '{print $1}')
    target_file=$(echo ${create_event} |awk '{print $NF}')
    target_file_path=${target_dir}/${target_file}

    if [[ -f ${target_file_path} ]]; then
        chattr +a ${target_file_path}
    fi
done < <(/usr/bin/inotifywait -e create -mq ${OPE_LOG_DIR})
```

これを、systemdにサービスとして登録する

```CONSOLE
# vi /etc/systemd/system/chattr_ope_log.service
```

とりあえず、こんな感じ

```CONSOLE
[Unit]
Description = harden operation log permission

[Service]
ExecStart = /usr/local/script/chattr_ope_log.sh
Restart = always
Type = simple

[Install]
WantedBy = multi-user.target
```

作ったファイルに実行権限付与して

```CONSOLE
# chmod 755 /usr/local/script/chattr_ope_log.sh
```

登録！

```
# systemctl enable chattr_ope_log --now
# systemctl status chattr_ope_log 
● chattr_ope_log.service - harden operation log permission
   Loaded: loaded (/etc/systemd/system/chattr_ope_log.service; enabled; vendor preset: disabled)
   Active: active (running) since Sun 2021-11-07 14:39:29 JST; 3s ago
 Main PID: 8056 (chattr_ope_log.)
    Tasks: 3
   CGroup: /system.slice/chattr_ope_log.service
           ├─8056 /bin/bash /usr/local/script/chattr_ope_log.sh
           ├─8057 /bin/bash /usr/local/script/chattr_ope_log.sh
           └─8058 /usr/bin/inotifywait -e create -mq /var/log/ope_log

Nov 07 14:39:29 arm01 systemd[1]: Started harden operation log permission.

```

この状態で、ログインすると、scriptが自動実行されていて、

```
# w
 14:41:05 up 71 days,  2:15,  2 users,  load average: 0.00, 0.00, 0.00
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
opc      pts/2    202.xxx.xxx.xxx   14:40    0.00s  0.01s  0.00s script -f -q -t /var/log/ope_log/opc_20211107144048_xx.xx.xx.log
opc      pts/4                     14:40    0.00s  0.02s  0.00s w

# > /var/log/ope_log/opc_20211107144048_202.xx.xx.xx.log
bash: /var/log/ope_log/opc_20211107144048_202.xx.xx.xx.log: Operation not permitted
$ rm /var/log/ope_log/opc_20211107144048_202.xx.xx.xx.log
rm: cannot remove ‘/var/log/ope_log/opc_20211107144048_202.xx.xx.xx.log’: Operation not permitted

→ 自分ではファイルを消せないし、viで修正しても修正できない！

```

うまくいっている感がある