[改訂版]vulsを使って脆弱性の自動スキャンを実現する


[vuls](https://github.com/future-architect/vuls)は脆弱性確認を楽にしてくれる可能性を秘めた、サーバー運用者にとって期待のツールです。

[前回書いた記事](http://qiita.com/Ets/items/31b0b7394b8dd5491e61)に開発者の @kotakanbe@github さんから有益な情報を頂いたのと、その後有益な記事を参考にしたのとで、大幅に文章を修正しました。 **あまりに更新量が多かった** (＝簡略化できた)ので、別記事としています。

具体的には

- **go-cve-dictionaryのdaemon化が不要だった** ため、手順を省略しました。
- 日本語脆弱性辞書を扱うようにしました。
- 上記変更に伴い、実行例や実行コマンド例も変えました。

なお、導入手順は基本的に[オフィシャル](https://github.com/future-architect/vuls)に従えば良いのですが、AWSベースで書かれています。本記事はオンプレ用に端折った手順のメモです。

以下、環境はCentOS7で、2016/06/14に実施した手順です。
本手順で入るgo-cve-dictionaryおよびvulsのバージョンは以下です。

```bash
$ go-cve-dictionary -v
go-cve-dictionary 0.1.1

$ vuls -v
vuls 0.1.4
```

*前回の記事で作ったサーバーで入れ直しをしているため、多少手順の記載漏れをしているかもしれません。*


# 事前準備

ここでの作業はCentOS7上で、全てrootユーザーを想定しています。

## Go言語環境の構築

事前に必要なパッケージをインストールします。
goはtarでダウンロードするので、ちょっとめんどくさいですね。

```bash
$ yum -y install sqlite git gcc
$ wget https://storage.googleapis.com/golang/go1.6.2.linux-amd64.tar.gz
$ tar -C /usr/local -xzf go1.6.linux-amd64.tar.gz
```

`/etc/profile.d/goenv.sh`を作成してこのように記載します。

```/etc/profile.d/goenv.sh
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
```

一回ログアウトして再度ログインすれば、`goenv.sh`が適用されてgoコマンドが使用できるはずです。

## SSH公開鍵認証の設定

vulsはAnsibleやChefのように、公開鍵認証のSSHを使用しているようです。検査対象サーバー（ここではローカルホスト）には事前にSSH周りの設定をしておきましょう。

このあたり、AnsibleやChefなどのSSHユーザー管理の話と同じですので、ここでは割愛します。
私の環境ではAnsibleに使用しているユーザーを使用することにしました。

本手順ではこのユーザーを **「sshuser」** と呼ぶことにします。

なお、以下はSSH公開鍵設定の手順例です。

```bash
### 認証用の公開鍵を作る
[[vulsサーバー]]
$ su - sshuser

[sshuser] $ ssh-keygen -t rsa
[sshuser] $ cat ~/.ssh/id_rsa.pub

### この内容を検査対象サーバーのSSHログインユーザーに設定
[[検査対象サーバー]]
[sshuser] $ vim ~/.ssh/authorized_keys
[sshuser] $ chmod 600 ~/.ssh/authorized_keys
```

sshuserで、SSHで対象サーバーにノーパスで入れることを確認しましょう。


# 導入手順

ここからの作業は、先ほどのsshuserで作業することを想定しています。

```bash
[[vulsサーバー]]
$ su - sshuser
```

## cve-dictionaryの初期構築

### go-cve-dictionaryインストール

go-cve-dictionaryをダウンロード＆インストールしましょう。結構重たいです。

```bash
$ go get github.com/kotakanbe/go-cve-dictionary

### 確認
$ type go-cve-dictionary
go-cve-dictionary is hashed (/home/sshuser/go/bin/go-cve-dictionary)

$ go-cve-dictionary -v
go-cve-dictionary 0.1.1
```

`go-cve-dictionary`がインストールされました。
※ 前回記事と違って、GOPATH(各自のホームディレクトリ以下)に実行ファイルがあれば十分です。



### ディレクトリの準備
ログディレクトリおよび辞書ディレクトリを作り、sshuserに書込権限を渡しておきます。
この部分はsudoしないと(root権限を取らないと)、おそらく権限不足で失敗するかと思います。

```bash
$ sudo mkdir /var/log/vuls
$ sudo chown sshuser:sshuser /var/log/vuls

$ sudo mkdir -p /var/vuls/dict
$ sudo chown sshuser:sshuser /var/vuls/dict
```


### 脆弱性辞書の準備

2002年〜2016年に発生した脆弱性について、辞書を取得します。
数分は時間が掛かるので注意しましょう。

```bash
$ for i in {2002..2016}; do go-cve-dictionary fetchnvd -dbpath=/var/vuls/dict/cve.sqlite3 -years $i; done
```

辞書ディレクトリにsqlite3ファイルができたことを確認します。

```bash
$ ls -l /var/vuls/dict/cve.sqlite3
-rw-r--r-- 1 sshuser sshuser 574898176 Jun  1 12:00 cve.sqlite3
```


日本語の脆弱性情報も追加しておきましょう。 **こちらはもっと時間が掛かります。**
日本語でなくても良い、という方は、この項目は飛ばしてください。

```bash
$ go-cve-dictionary fetchjvn -entire -dbpath=/var/vuls/dict/cve.sqlite3
```



## vulsを準備


vulsを手に入れる・・・のですが、 **ここで詰まる可能性がある** ので、続く手順を先に眺めてください。

```bash
$ go get github.com/future-architect/vuls
```

CentOSの標準yumリポジトリでは、git-1.X系列しかダウンロード出来ないはずです。
この場合、gitのバージョンが古いために`go get`が詰まってしまいます。

そこでwandiscoのリポジトリからgit-2.xをインストールします。

### git-2.xのインストール

本節の作業はrootユーザーで、あるいはsudoで実行してください。

`/etc/yum.repos.d/wandisco.repo`を作成します。

```/etc/yum.repos.d/wandisco.repo
[wandisco-git]
name=WANdisco Distribution of git
baseurl=http://opensource.wandisco.com/centos/7/git/$basearch
enabled=0
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-WANdisco
```

gitのアップデート

```bash
### GPGキーを取り込み
$ rpm --import http://opensource.wandisco.com/RPM-GPG-KEY-WANdisco
$ yum update git --enablerepo=wandisco-git

### バージョンアップ確認
$ git --version
git version 2.8.0
```

### 改めてvulsをインストール

では、改めてvulsを`go get`しましょう。
※ sshuserに戻るのを忘れないようにご注意を。

```bash
$ go get github.com/future-architect/vuls

### 確認
$ type vuls
vuls is hashed (/home/sshuser/go/bin/vuls)

$ vuls -v
vuls 0.1.4
```

`vuls`も入りました。
※ 前回記事と違って、GOPATH(各自のホームディレクトリ以下)に実行ファイルがあれば十分です。


# vulsによる脆弱性検査

## vulsの使用準備

### vuls設定ファイルの作成

先ほど作ったvulsサーバーにて`config.toml`ファイルを作成します。

現在のvulsはカレントディレクトリ以下にある`config.toml`ファイルを読み、カレントディレクトリ以下に結果を出力するのが基本動作です。-configオプションでconfig.tomlのパスを指定することもできます。
以下は設定毎にディレクトリを作っている例です。

```bash
$ mkdir hoge
$ cd hoge
$ vim config.toml
```

中身はこのように書いていきます。
詳しくはオフィシャルを参照してください。

```config.toml
[servers]

[servers.localhost]
host        = "127.0.0.1"
port        = "22"
user        = "sshuser"
keyPath     = "/home/sshuser/.ssh/id_rsa"
```


## 脆弱性スキャンの実行

### vuls prepare

SSH公開鍵ログイン設定が終わったら`vuls prepare`を実行します。
これにより、SSHの動作確認ができるとともに、[動作に必要なyumプラグイン](https://github.com/future-architect/vuls#usage-prepare)が検査対象サーバーにインストールされます。

```bash
$ vuls prepare

INFO[0000] Start Preparing (config: /root/config.toml)
[May 24 12:29:25]  INFO [localhost] Detecting OS...
[May 24 12:29:25]  INFO [localhost] (1/1) Detected localhost: centos 6.4
[May 24 12:29:25]  INFO [localhost] Detecting Container OS...
[May 24 12:29:25]  INFO [localhost] Installing...
[May 24 12:29:25]  INFO [localhost] Ignored: yum-plugin-security already installed
[May 24 12:29:25]  INFO [localhost] Installing yum-plugin-changelog...
[May 24 12:29:29]  INFO [localhost] Installed: yum-plugin-changelog
[May 24 12:29:29]  INFO [localhost] Success
```

### vuls scan

ここまで準備出来れば、脆弱性スキャンが実行できます。
**config.tomlに書いたサーバーに対しては一気に実行してしまいます** ので、稼働中のホストへのスキャンは気をつけてください。

レポートの出力形式はヘルプを見て、適当なものを選びましょう。

```bash
$ cd vulsの設定ファイルを作ったフォルダ

$ vuls scan -cve-dictionary-dbpath=/var/vuls/dict/cve.sqlite3 -report-text
### 日本語でレポートを出力する場合は以下
$ vuls scan -cve-dictionary-dbpath=/var/vuls/dict/cve.sqlite3 -report-text -lang=ja
```

結果は `results/current/` ディレクトリに出力されています。

```bash
$ ls -1 results/current/*.txt
all.txt
ホスト名.txt
```

# 定期的な実行を考える

定期的な実行をし続けることで、運用をもっと楽したいと思います。


## 脆弱性情報の定期的な更新

とりあえずcronに仕込んじゃいましょう。
コマンドの中身そのものはそれぞれ簡単なワンライナーで、「過去２年間の最新情報を取得する」+「過去1週間の日本語脆弱性辞書を更新する」というものです。


```/etc/cron.d/go-cve-dictionary_update
SHELL=/bin/bash
HOME=/home/sshuser
MAILTO=""

# Update CVE dict
37 05 * * * sshuser ${HOME}/go/bin/go-cve-dictionary fetchnvd -last2y -dbpath=/var/vuls/dict/cve.sqlite3 >/dev/null 2>&1
57 05 * * * sshuser ${HOME}/go/bin/go-cve-dictionary fetchjvn -week -dbpath=/var/vuls/dict/cve.sqlite3 >/dev/null 2>&1
```

**`HOME`環境変数及び実行ユーザーの`sshuser`は環境に合わせてください。**
また、実行時間は **負荷をかけないように適当に変えてください。**


# 定期的な脆弱性診断

定期的に診断を実行するなら、どうやって通知するのが適切か考える必要があります。
vulsはslackに投げる機能もあるのですが、脆弱性発見時は結構な文章量になることを想定して、メール通知で始めてみることにしました。

## メール通知を設定する

Cronで実行するためのconfigファイルを作成します。

```bash
$ mkdir /var/vuls/cron/config.toml
$ vim /var/vuls/cron/config.toml
```

自動実行して負荷をかけても問題ないサーバーをスキャン対象にしておきましょう。

このとき、configファイルの先頭に`[mail]`ブロックを作成します。
詳しくはオフィシャルの設定を見るとして、こんな感じでローカルのsmtpサーバーを使って送信してしまいます。

```/var/vuls/cron/config.toml
[mail]
smtpAddr      = "localhost"
smtpPort      = "25"
from          = "xxxxxx@mailaddress.xxxx"
to            = ["xxxxxx@mailaddress.xxxx"]
subjectPrefix = "[vuls]"

[servers]
### 以下スキャン対象のサーバーの指定
```

なお、オフィシャルでは`smtpPort`オプションはintegerに見えるのですが、文字列(ダブルクォートで囲む)じゃないとうまく動作しませんでした。この辺は近々直るはずです。

## Cronに設定する
この設定ファイルを使って`vuls scan`を実行する設定をCronに仕込みます。

本書の手順では、先ほどのconfigファイルは`/var/vuls/cron`に置いているので、vulsコマンド実行前に`cd`しています。コマンド末尾の出力言語部分は好みで選んでください。

```/etc/cron.d/go-cve-dictionary_scan
SHELL=/bin/bash
HOME=/home/sshuser
MAILTO=""

# Scan target machines
37 06 * * * sshuser cd /var/vuls/cron && ${HOME}/go/bin/vuls scan -cve-dictionary-dbpath=/var/vuls/dict/cve.sqlite3 -report-text -report-mail -lang=ja >/dev/null 2>&1
```

これで **毎朝嫌というほど脆弱性通知メールが来ることが期待できます。**
*# 当たり障りない脆弱性は対応を手抜きしちゃいますよね・・・*

こちらも、 **`HOME`環境変数及び実行ユーザーの`sshuser`は環境に合わせてください** ね。



### 重要な脆弱性だけ知りたい

すべて潰すのがツラい。重要な脆弱性だけ知りたい。
・・・という場合は、`-cvss-over=X`というパラメータを付けましょう。

例えば、CVSSスコア7以上だけを通知する場合は以下の様なcron設定になります。

```/etc/cron.d/go-cve-dictionary_scan
前略

# Scan target machines
37 06 * * * sshuser cd /var/vuls/cron && ${HOME}/go/bin/vuls scan -cve-dictionary-dbpath=/var/vuls/dict/cve.sqlite3 -report-text -report-mail -lang=ja -cvss-over=7 >/dev/null 2>&1
```

### もっとメール量を減らして差分だけを知りたい

という場合は[こちらの記事](http://qiita.com/usiusi360/items/90bad397c16c07a9630b)が参考になります。



# 最後に
以上で、脆弱性を自動スキャンしてメール通知する仕組みを構築しました。
前回の記事よりはすっきりした手順になっているかな、と思います。

数々の気付きを下記の記事から得ました。良い記事をありがとうございます。

- [脆弱性検知ツールVulsのレポート日本語化](http://qiita.com/usiusi360/items/6134f6ab62e8d77159ef)
- [脆弱性検知ツールVulsで前日分との差分だけレポートする](http://qiita.com/usiusi360/items/90bad397c16c07a9630b)

改めて、@kotakanbe@github さん、良いツールをありがとうございます！