Vuls on EC2 & Slackで筋トレする

# はじめに
※こちらは [KWC Advent Calendar 2022](https://qiita.com/advent-calendar/2022/kwc) の記事です。

本記事は、Linux向けの脆弱性スキャンツールであるvuls をAWS EC2 上にインストールし、結果をSlackに通知する手順を記したものです。
vulsはSSHで対象のサーバーにログインし、脆弱性スキャンを実施するツールです。

サーバーの脆弱性チェックに日々追われる方は、少なからずいらっしゃるかと存じます。vulsによりサーバーが自動でスキャンされ、影響を受けている脆弱性問題の通知を受け取ることができ、時間の節約に繋がります。空いた時間は筋トレなど有効にご活用ください。

# 前提
以下をご準備ください。
・RHEL on Amazon EC2
・Slackにおける Webhook URL
・普段お使いの筋トレグッズ、トレーニングできる十分なスペース

今回はスキャンサーバ、スキャン対象サーバともにRHEL9としており、Red Hat社の脆弱性情報を対象にチェックします。

# 全体の流れ
主に、次のステップで進めていきます。
- 前提パッケージのインストール
- Go言語のインストール
- goval-dictionaryのインストール
- vulsのインストール
- vulsの設定
- スキャンの実行
- スキャン結果の表示
- slackへの通知

以下の作業は、AWS EC2上のRHEL9に、rootユーザーでログインした状態で実施します。

# 前提パッケージのインストール

後々の手順で必要となるため、必要なパッケージをインストールします。
```
# dnf install sqlite git gcc make wget
```

# GO言語のインストール
執筆時点で最新版のGo言語をインストールしています。新しいバージョンが公開されていましたら、最新版のご利用をおすすめします。
```
# wget https://go.dev/dl/go1.19.4.linux-amd64.tar.gz
# tar -C /usr/local -xzf go1.19.4.linux-amd64.tar.gz
# mkdir $HOME/go
```

環境変数を設定します。
エディタで次のファイルを開き、以下3行の内容を記入して保存ください。
```
# vi /etc/profile.d/goenv.sh
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
```

環境変数を適用します。
`go version`コマンドにて、バージョンが表示されれば完了です。
```
# source /etc/profile.d/goenv.sh
# go version
go version go1.19.4 linux/amd64
```

# goval-dictionaryのインストール
Red Hatの脆弱性情報を入手するため、goval-dictionaryをインストールします。
```
# mkdir -p $GOPATH/src/github.com/vulsio
# cd $GOPATH/src/github.com/vulsio
# git clone https://github.com/vulsio/goval-dictionary.git
# cd goval-dictionary
# make install
```
筆者の環境では、インストールに数分程度の時間を要しました。適宜ストレッチやトレーニングをしながら、身体を温めてお待ちください。

RHEL9の脆弱性情報をダウンロードします。
結果は `$HOME/vuls/oval.sqlite3` に格納しています。
```
# mkdir $HOME/vuls
# goval-dictionary fetch redhat 9 --dbpath $HOME/vuls/oval.sqlite3
```

# vulsのインストール
vuls本体をインストールします。

```
# mkdir -p $GOPATH/src/github.com/future-architect
# cd $GOPATH/src/github.com/future-architect
# git clone https://github.com/future-architect/vuls.git
# cd vuls/
# make install
```

# vulsの設定
vulsの設定を実施します。本記事の例では、スキャン対象サーバ名を `test-rhel9` としており、設定ファイルは `$HOME/vuls/config/test-rhel9.toml` としています。

```
# cd $HOME
# mkdir $HOME/vuls/{config,result}
# vi $HOME/vuls/config/test-rhel9.toml
(例)
---
[slack]  // Slack通知用の設定
hookURL = "https://hooks.slack.com/services/.../xxxxxxxxxx" // Slackにおける Webhook URL
channel = "#vuls-notify" // 通知先のチャネル名
iconEmoji = ":icon-scanner:" // 通知時のSlackアイコン 
authUser = "vuls for RHEL9" //通知時の名前

[ovalDict]
type = "sqlite3"
SQLite3Path = "/root/vuls/oval.sqlite3"

[servers]

[servers.test-rhel9] // スキャン対象サーバの情報
host = "10.220.1.110" // スキャン対象サーバのIPアドレスまたはホスト名
port = "22" // SSHでの接続ポート
user = "ec2-user" // スキャン時、SSHでログインする際のユーザー
keyPath = "/root/.ssh/id_rsa" // SSH接続時に利用する鍵ファイル
scanMode = ["fast"]
```

:::note info
[servers] セクションには、複数のサーバーを指定することが可能です。1つの設定ファイルを用いて、複数サーバーにスキャンを実行することもできます。

一方、執筆時点のvulsの実装では、サーバーに応じてSlackの通知先やアイコンを変更したい場合、サーバーごとに設定ファイルを分ける必要があります。

ご利用のサーバー構成、運用要件およびご自身のトレーニングメニューとの兼ね合い等を考慮いただき、適切な構成をご検討ください。
:::

# dnfキャッシュの作成(RHEL8, RHEL9の場合)
後の手順にてRHEL8やRHEL9サーバーをスキャンしようとすると、次のようなエラーが出力されることがあります。
```
stdout: Error: Cache-only enabled but no cache for 'rhel-9-appstream-rhui-rpms'
:
```
このため、RHEL8またはRHEL9サーバーをスキャンする場合には、スキャン対象サーバにログインして次のコマンドを実行ください。
```
# dnf makecache
```

# スキャンの実行
次のコマンドにて、スキャンを実施します。
```
# vuls scan -config /root/vuls/config/test-rhel9.toml -results-dir /root/vuls/result test-rhel9
```
スキャンが正常に完了すると、次のような結果が出力されます。
```
Scan Summary
================
test-rhel9      redhat9.1       380 installed


To view the detail, vuls tui is useful.
To send a report, run vuls report -h.
```

`vuls scan`コマンドのオプション `-results-dir` で指定したディレクトリ配下に、スキャン結果が格納されます。
また、最新のスキャン結果を指す `/root/vuls/result/current` がシンボリックリンクファイルとして保存されます。
```
# ls -l /root/vuls/result/
total 0
drwx------. 2 root root 29 Dec  9 08:49 2022-12-09T08:49:08Z
drwx------. 2 root root 29 Dec  9 09:16 2022-12-09T09:16:07Z
lrwxrwxrwx. 1 root root 38 Dec  9 09:16 current -> /root/vuls/result/2022-12-09T09:16:07Z
```


# スキャン結果の表示
スキャンの結果は、次のコマンドで確認できます。
```
# vuls report -config /root/vuls/config/test-rhel9.toml -results-dir /root/vuls/result/ -cvss-over=70
:
test-rhel9 (redhat9.1)
======================
Total: 0 (Critical:0 High:0 Medium:0 Low:0 ?:0)
0/0 Fixed, 0 poc, 0 exploits, cisa: 0, uscert: 0, jpcert: 0 alerts
380 installed

No CVE-IDs are found in updatable packages.
380 installed
```
この場合、`No CVE-IDs are found in updatable packages.` と出力されており、該当した脆弱性問題はありません。

# Slackへの通知
`vuls report` コマンドに `-to-slack` オプションを付与することで、Slackへの通知が行われます。
```
(例)
vuls report -config /root/vuls/config/test-rhel9.toml -results-dir /root/vuls/result/ -to-slack
```

Slackでの出力例は次のとおりです。
![vuls-for-test-rhel9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2982088/930ed34d-c884-7177-a794-d49b7ff8f563.png)
素晴らしい上腕二頭筋をお持ちですね。

ちなみに、RHEL9ではなく当社の別環境のubuntuサーバーをスキャンした場合ではありますが、脆弱性問題を検知した際のSlack通知をご紹介します。※ホスト名はマスクしています。

![vuls-cve.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2982088/e6ae12d7-3366-874d-3aca-02cf6e8942fe.png)

上記の例では、インストールされている snapd に対して脆弱性問題 CVE-2022-3328 が検知されています。

`vuls report`コマンドでは特にオプションを指定しないと、スキャン対象のサーバーが影響を受けているすべての脆弱性問題がSlackへ通知されます。構築してから数カ月や数年経過しているサーバーの場合には、大量の脆弱性情報が通知されてしまい、Slack上における他の通知が流れてしまう恐れがあります。

次のステップにて、日々運用におけるおすすめ設定の一例を記載します。屈伸などでリラックスしながらご参照ください。

# 日々運用にあたって
あくまで一例ではありますが、設定例を紹介します。
- shukujitu(*)を導入し、平日のみ脆弱性情報のアップデートおよびスキャンを実行
- 7日以上経過したスキャン結果を削除

(*)[Github: shukujitsu](https://github.com/soh335/shukujitsu)

cronでの設定例は次のとおりです。
```
# crontab -l
---
# update dictionary 
25 0 * * 1-5 /usr/bin/shukujitsu -n `/bin/date +\%s` || /root/vuls/goval-dic-update.sh
# scan and report
0 9 * * 1-5 /usr/bin/shukujitsu -n `/bin/date +\%s` || /root/vuls/vuls-scan-and-report.sh
# delete old scan files
30 0 * * * /usr/bin/find /root/vuls/results/*/ -mtime +7 -type d | xargs rm -rf
```

脆弱性情報のアップデートは、次のコマンドで実施しています。
```
# cat /root/vuls/goval-dic-update.sh
#!/bin/bash
/root/go/bin/goval-dictionary fetch redhat 9 --dbpath /root/vuls/oval.sqlite3 >/dev/null 2>&1
```

脆弱性スキャンおよびSlackへの通知は、次のような形で実施しています。
```
# cat /root/vuls/vuls-scan-and-report.sh
!/bin/bash -x  
host_list=(test-rhel9.toml) # add here when you add scan-target
output_score=7.0

for host in ${host_list[@]}
do
    /root/go/bin/vuls scan -config /root/vuls/config/${host}.toml -results-dir /root/vuls/results/${host} -quiet ${host}
    :
    /root/go/bin/vuls report -diff-plus -config /root/vuls/config/${host}.toml -results-dir /root/vuls/results/${host} -cvss-over=$output_score -to-slack > /dev/null 2>&1
done
```
- 複数サーバーを管理したい場合、host_list にホスト名を追加することで順次スキャンできます。サーバ毎に通知やアイコン等を分けたい場合に便利です。
- Slackへの通知の際、`vuls report`コマンドの `-diff-plus` オプションを使うことで、最新と前回の差分の脆弱性問題のみ通知します。これにより必要な通知のみが表示されます。
- `-cvss-over`により、指定したcvss scoreよりも低い脆弱性問題は表示されなくなります。

このような形で、脆弱性スキャン作業が自動化できます。毎日ベンダ公開の脆弱性情報ページを開き、目視でチェックするタスクから解放されるかと存じます。空いた時間は筋トレなど有効にご活用ください。

# あとがき
今回はRHEL9サーバーに対して、vulsを用いて脆弱性問題を自動スキャンする手順を記載しました。

冒頭にて、普段ご利用されている筋トレグッズのご用意をお願いし、作業途中でせっかく身体を温めていただいたにもかかわらず、中盤以降さほど時間がかかる工程が存在しなかったため、身体が温めっぱなしになっていることが発覚しました。配慮が足りなかった点につきましてお詫びいたします。十分にトレーニングおよびクールダウンを実施された上で、安全かつ快適なトレーニングもといサーバー運用をご継続ください。

※本記事は筋トレのことをしばしば考えている筆者によって構成されています。本記事の随所に読みにくい点がありましたことを、重ねてお詫び申し上げます。

[KWC Advent Calendar 2022](https://qiita.com/advent-calendar/2022/kwc)はまだまだ続きますので、良ければ他の記事もご覧ください。

# 参考文献
- [Github: shukujitsu](https://github.com/soh335/shukujitsu)
- [脆弱性検知ツールVulsの、きちんと動く構築手順](https://zenn.dev/myuki/articles/d0c1f780d45e8b)
- ~~なかやまきんに君『ウケる筋トレ』学研プラス, 2018.~~