go-auditとfluentdとElasticsearchでつくるLinux監査システム
はじめに
Linuxのauditフレームワークはシステム上のセキュリティに関係する各種イベントを収集し、システム上で行われた動作を追跡するものである。それ自体がなにかセキュリティを強化するものではなく、問題を追跡し分析しなければ脅威に対するアクションをとることはできない。
一般的にはauditd
デーモンによりLinuxカーネルから監査ログを出力することが多いと思う。ただこの監査ログは決して人間によって読みやすいものではなく、ausearch
やaureport
など付属するコマンドをつかって監査ログを検索・表示することになる。
下記はvagrant
ユーザがsudo tail -f /var/log/audit/audit.log
した際の監査ログ出力の抜粋。
# sudo ausearch -i
type=PROCTITLE msg=audit(07/12/2021 15:31:55.950:66) : proctitle=sudo tail -f /var/log/audit/audit.log
type=PATH msg=audit(07/12/2021 15:31:55.950:66) : item=1 name=/lib64/ld-linux-x86-64.so.2 inode=265245 dev=fc:03 mode=file,755 ouid=root ogid=root rdev=00:00 nametype=NORMAL cap_fp=none cap_fi=none cap_fe=0 cap_fver=0 cap_frootid=0
type=PATH msg=audit(07/12/2021 15:31:55.950:66) : item=0 name=/usr/bin/sudo inode=272025 dev=fc:03 mode=file,suid,755 ouid=root ogid=root rdev=00:00 nametype=NORMAL cap_fp=none cap_fi=none cap_fe=0 cap_fver=0 cap_frootid=0
type=CWD msg=audit(07/12/2021 15:31:55.950:66) : cwd=/home/vagrant
type=EXECVE msg=audit(07/12/2021 15:31:55.950:66) : argc=4 a0=sudo a1=tail a2=-f a3=/var/log/audit/audit.log
type=SYSCALL msg=audit(07/12/2021 15:31:55.950:66) : arch=x86_64 syscall=execve success=yes exit=0 a0=0x5592e98d4cd0 a1=0x5592e98d28d0 a2=0x5592e98cefd0 a3=0x8 items=2 ppid=2299 pid=9927 auid=vagrant uid=vagrant gid=vagrant euid=root suid=root fsuid=root egid=vagrant sgid=vagrant fsgid=vagrant tty=pts1 ses=42 comm=sudo exe=/usr/bin/sudo key=(null)
さて、こういった監査ログ出力群を精査して以下の質問に簡単に答えられるだろうか?
- 過去1週間で外向けTCP接続がいくつ作成されたか
- TCP接続を作成したプロセスとユーザの比率はどうなっているか
…はい、とっても難しい。どうせならダッシュボードで可視化しておいてほしい。
やりましょう、go-auditとfluentdとElasticsearchで。
以下はUbuntu20.04で検証した際の手順。他のディストリビューションの方は適宜読み替えるように。
go-auditによる監査ログ収集
go-auditとは
go-audit
はauditd
と同じくLinuxカーネルから様々なsyscall呼び出しなどの監査ログを収集するもの。明らかな違いとしては、go-audit
がJSON出力をサポートしていること。fluentd/logstashなどのモダンなログ収集ツールとの連携が容易になる。またコンフィグもYAML形式でありシンプルに管理運用できるところがメリットだろう。
より詳細な説明はSlackのセキュリティアーキテクトである作者による記事を参照すること。
go-auditのインストール
go-audit
はGoで作成されたツール。アーキテクチャごとにクロスコンパイルされたバイナリがあるので、これを利用するのが簡単でよい。
$ curl -sf https://gobinaries.com/slackhq/go-audit | sudo PREFIX=/usr/sbin sh
go-audit
のコンフィグはYAML形式である。
ディスク容量などキャパシティ管理方針に依るところではあるが、可能な限りgo-audit
では収集ログをフィルタせず収集先(ここではElasticsearch)で絞り込み・精査するのが良いと思う。万が一セキュリティ異常が発生した場合には、何が有用なログなのかその時にならないとわからないものなので。
$ sudo mkdir /etc/go-audit
$ cat << EOL | sudo tee /etc/go-audit/go-audit.yml
message_tracking:
enabled: true
log_out_of_order: false
max_out_of_order: 500
log:
flags: 0
# 収集するイベントタイプの範囲を指定する
# 1100-1199: userspace messages
# 1200-1299: auditd daemon messages
# 1300-1399: syscall event messages
# 参考: https://github.com/torvalds/linux/blob/master/include/uapi/linux/audit.h
# 参考: https://github.com/linux-audit/audit-userspace/blob/master/lib/libaudit.h
events:
min: 1100
max: 1399
# ログはファイル出力する
output:
file:
enabled: true
attempts: 2
path: /var/log/go-audit/go-audit.log
mode: 0644
user: root
group: root
# 収集ルール
# 必要なルール(重要ファイルのopenなど)はここで追加する
# 参考: https://github.com/Neo23x0/auditd/blob/master/audit.rules
rules:
- -b 1024
- -a exit,always -S connect -k netconns_out
- -a exit,always -S listen -k netconns_in
- -a exit,always -S execve -k execve
# go-audit自身がnetlinkソケットに接続したログはフィルタする
filters:
- syscall: ""
message_type: 1305
regex: .*
EOL
では起動する。
もしサービス化するのであればこちらの公式systemdユニットファイルを利用するとよい。
$ sudo go-audit -config /etc/go-audit/go-audit.yml
さてこれで何らかのNetwork IOやプロセス起動をすると逐一/var/log/go-audit.log
に出力されるようになっている。
下記はvagrant
ユーザがsudo tail -f /var/log/go-audit/go-audit.log
した際の監査ログ出力の抜粋。
{
"sequence": 1053,
"timestamp": "1626105161.783",
"messages": [
{
"type": 1300,
"data": "arch=c000003e syscall=257 success=yes exit=0 a0=55b5827dfaf0 a1=55b5827df360 a2=55b582819870 a3=8 items=2 ppid=10366 pid=10539 auid=1000 uid=1000 gid=1000 euid=0 suid=0 fsuid=0 egid=1000 sgid=1000 fsgid=1000 tty=pts3 ses=47 comm=\"sudo\" exe=\"/usr/bin/s },
{
{
"type": 1302,
"data": "item=0 name=\"/etc/shadow\" inode=6948426 dev=fc:03 mode=0100640 ouid=0 ogid=42 rdev=00:00 nametype=NORMAL cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0"
},
{
"type": 1327,
"data": "proctitle=7375646F007461696C002D66002F7661722F6C6F672F676F2D61756469742E6C6F67"
}
],
"uid_map": {
"0": "root",
"1000": "vagrant"
}
}
fluentdで監査ログを転送する
fluentdのインストール
細かくは説明しないので公式手順を参照すること。
$ curl -L https://toolbelt.treasuredata.com/sh/install-ubuntu-focal-td-agent4.sh | sudo sh
あわせてgo-audit
ログ形式をElasticsearch等のデータストアで利用しやすい形に再フォーマットするfluentdプラグインfluent-plugin-go-audit-parserを作ったので、こちらもインストールしておく。
go-audit
はパフォーマンスを優先しコードは最小限に保たれていて、いくつかのログフィールドはデコードされていない。このfluentdプラグインによって、これらのログはデコードされ容易に検索できるよう整形される。
$ sudo td-agent-gem install fluent-plugin-go-audit-parser
次にgo-audit
によって出力された/var/log/go-audit.log
を監視し、Elasticsearchに転送するfluentdコンフィグを用意する。
$ cat << EOL | sudo tee /etc/td-agent/td-agent.conf
<source>
@type tail
@id go-audit.tail
path /var/log/go-audit/go-audit.log
<parse>
@type json
</parse>
tag audit
</source>
<filter audit>
@type go_audit_parser
@id go-audit.parse
</filter>
<match audit>
@type elasticsearch
@id go-audit.elasticsearch
hosts 127.0.0.1:9200
logstash_format true
logstash_prefix audit
reload_connections false
suppress_type_name true
<buffer>
@type file
path /tmp/go-audit.elasticsearch
flush_interval 60s
retry_max_times 1
</buffer>
</match>
fluentd-plugin-go-audit-parser
で整形されたgo-audit
ログは次のようなフォーマットになる。
# {
# "sequence": 5180,
# "messages": {
# "syscall": {
# "type": 1300,
# "arch": "c000003e",
# "syscall": 257,
# "success": "yes",
# "exit": "4",
# "a0": "ffffff9c",
# "a1": "7ff30a34b1a1",
# "a2": "80000",
# "a3": "0",
# "items": "1",
# "ppid": "4835",
# "pid": 4891,
# "auid": { "id": 1000, "name": "vagrant" },
# "uid": { "id": 1000, "name": "vagrant" },
# "gid": 1000,
# "euid": { "id": 0, "name": "root" },
# "suid": { "id": 0, "name": "root" },
# "fsuid": { "id": 0, "name": "root" },
# "egid": 1000,
# "sgid": 1000,
# "fsgid": 1000,
# "tty": "pts2",
# "ses": 32,
# "comm": "sudo",
# "exe": "/usr/bin/sudo",
# "key": "etcpasswd"
# },
# "path": {
# "type": 1302,
# "item": "0",
# "name": "/etc/shadow",
# "inode": 6948416,
# "dev": "fc:03",
# "mode": "0100640",
# "ouid": { "id": 0, "name": "root" },
# "ogid": 42,
# "rdev": "00:00",
# "nametype": "NORMAL",
# "cap_fp": "0",
# "cap_fi": "0",
# "cap_fe": "0",
# "cap_fver": "0",
# "cap_frootid": "0"
# },
# "proctitle": {
# "type": 1327,
# "proctitle": "sudo tail -f /var/log/go-audit/go-audit.log"
# }
# },
# "message_types": [ "syscall", "path", "proctitle" ]
# }
2021-07-15 04:43:01.795056298 +0000 audit: {"sequence":5180,"messages":{"syscall":{"type":1300,"arch":"c000003e","syscall":257,"success":"yes","exit":"4","a0":"ffffff9c","a1":"7ff30a34b1a1","a2":"80000","a3":"0","items":"1","ppid":"4835","pid":4891,"auid":{"id":1000,"name":"vagrant"},"uid":{"id":1000,"name":"vagrant"},"gid":1000,"euid":{"id":0,"name":"root"},"suid":{"id":0,"name":"root"},"fsuid":{"id":0,"name":"root"},"egid":1000,"sgid":1000,"fsgid":1000,"tty":"pts2","ses":32,"comm":"sudo","exe":"/usr/bin/sudo","key":"etcpasswd"},"path":{"type":1302,"item":"0","name":"/etc/shadow","inode":6948416,"dev":"fc:03","mode":"0100640","ouid":{"id":0,"name":"root"},"ogid":42,"rdev":"00:00","nametype":"NORMAL","cap_fp":"0","cap_fi":"0","cap_fe":"0","cap_fver":"0","cap_frootid":"0"},"proctitle":{"type":1327,"proctitle":"sudo tail -f /var/log/go-audit/go-audit.log"}},"message_types":["syscall","path","proctitle"]}
ではfluentdサービスを起動する。
$ sudo systemctl start td-agent
これで監査ログがElasticsearchへ転送されはじめた。よかったね。
ただし、まだ転送先のElasticsearchを立ち上げていないので、エラーが発生してしまう。急いで立ち上げよう。
Elasticsearchで監査ログを収集する
ElasticsearchとKibanaのインストール
$ wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
$ sudo apt-get install apt-transport-https
$ echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-7.x.list
$ sudo apt-get update && sudo apt-get install elasticsearch kibana
$ sudo systemctl start elasticsearch
$ sudo systemctl start kibana
はい、これでElasticsearchにaudit-*
というインデックスが作成された。
あとは良しなに検索ができる。
Kibanaで確認してみる
こんな感じ。
軽くダッシュボードも作ってみるとこんな感じ。
これで冒頭に設定した問いにいつでも答えられるね。
楽しい🎉
Discussion
調べていたら、ここに超絶有能なaudit.rulesを管理している方がいた。これは利用しない手はないぞ。
go-audit
を整形するfluentdプラグインを作ってrubygemsに登録したので、記事も修正した。でもまあaudit-beatって便利だよね。