Linux
脆弱性
vuls

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

More than 1 year has passed since last update.

vulsは脆弱性確認を楽にしてくれる可能性を秘めた、サーバー運用者にとって期待のツールです。

前回書いた記事に開発者の @kotakanbe@github さんから有益な情報を頂いたのと、その後有益な記事を参考にしたのとで、大幅に文章を修正しました。 あまりに更新量が多かった (=簡略化できた)ので、別記事としています。

具体的には

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

なお、導入手順は基本的にオフィシャルに従えば良いのですが、AWSベースで書かれています。本記事はオンプレ用に端折った手順のメモです。

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

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

$ vuls -v
vuls 0.1.4

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

事前準備

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

Go言語環境の構築

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

$ 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公開鍵設定の手順例です。

### 認証用の公開鍵を作る
[[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で作業することを想定しています。

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

cve-dictionaryの初期構築

go-cve-dictionaryインストール

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

$ 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権限を取らないと)、おそらく権限不足で失敗するかと思います。

$ 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年に発生した脆弱性について、辞書を取得します。
数分は時間が掛かるので注意しましょう。

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

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

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

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

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

vulsを準備

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

$ 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のアップデート

### 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に戻るのを忘れないようにご注意を。

$ 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のパスを指定することもできます。
以下は設定毎にディレクトリを作っている例です。

$ 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プラグインが検査対象サーバーにインストールされます。

$ 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に書いたサーバーに対しては一気に実行してしまいます ので、稼働中のホストへのスキャンは気をつけてください。

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

$ 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/ ディレクトリに出力されています。

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

定期的な実行を考える

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

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

とりあえずcronに仕込んじゃいましょう。
コマンドの中身そのものはそれぞれ簡単なワンライナーで、「過去2年間の最新情報を取得する」+「過去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ファイルを作成します。

$ 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

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

という場合はこちらの記事が参考になります。

最後に

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

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

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