改訂版記事を こちら にアップしています!
++++++
最近、脆弱性発見が相次いでいて、嬉しいようなツラいような状態ですね。
vulsは脆弱性確認を楽にしてくれる可能性を秘めた、サーバー運用者にとって期待のツールです。
導入手順は基本的にオフィシャルに従えば良いのですが、AWSベースで書かれているので、オンプレ用に端折った手順をメモしておきます。
以下、環境はCentOS7で、2016/06/01に入れた際の手順です。
事前準備
事前に必要なパッケージをインストールします。
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
を作成してこのように記載します。
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
導入手順
cve-dictionaryサーバーの構築
ユーザーの用意
変な権限でサーバーが動くのは嫌なので、cve-dictionaryサーバー用ユーザー(vuls)を作ります。
UIDとか指定する場合は、よしなに。
$ useradd -s "/sbin/nologin" vuls
go-cve-dictionaryインストール
go-cve-dictionaryのダウンロードしましょう。結構重たいです。
$ go get github.com/kotakanbe/go-cve-dictionary
ところが2016/06/01のインストール時、ここでエラーが発生しました。
$ go get github.com/kotakanbe/go-cve-dictionary
# github.com/kotakanbe/go-cve-dictionary/server
go/src/github.com/kotakanbe/go-cve-dictionary/server/server.go:49: cannot use standard.New(bindURL) (type *standard.Server) as type engine.Server in argument to e.Run:
*standard.Server does not implement engine.Server (wrong type for SetLogger method)
have SetLogger(*"github.com/labstack/gommon/log".Logger)
want SetLogger("github.com/labstack/echo/log".Logger)
使っているライブラリの更新が災いしているようです。
暫定対処ですが、問題のライブラリを前のバージョンに戻します。
$ pushd go/src/github.com/labstack/echo
$ git log
commit a98843b6e58dcda9364099cc43c4d5b888baf00c
Author: Vishal Rana <vr@labstack.com>
Date: Tue May 31 18:29:11 2016 -0700
Logger as interface, fixed #533, fixed #349, fixed #304
Signed-off-by: Vishal Rana <vr@labstack.com>
commit 3e04718bf42e434bd027067272afc1947ddf2f8e
Merge: f73681b 9225ce0
Author: Vishal Rana <vr@labstack.com>
Date: Tue May 31 07:49:48 2016 -0700
Merge pull request #532 from 0x616E676572/add_cors_method
Add PATCH to default allowed methods
### Logger周りを変える前に戻す
$ git reset 3e04718bf42e434bd027067272afc1947ddf2f8e --hard
$ popd
$ go get github.com/kotakanbe/go-cve-dictionary
強引ですが、go-cve-dictionary
がインストールされました。
daemon化の準備
ビルドされたgo-cve-dictionary
をOSのPATHにコピー。
※ こうしないとrootユーザーのホームにバイナリがあり、vulsユーザーが実行できないため。
$ cp -p go/bin/go-cve-dictionary /usr/local/sbin/
ログディレクトリおよび辞書ディレクトリを作って、vulsユーザーに権限を渡しておきます。
$ mkdir /var/log/vuls
$ chown vuls:vuls /var/log/vuls
$ mkdir -p /var/vuls/dict
$ chown vuls:vuls /var/vuls/dict
脆弱性辞書の準備
2002年〜2016年に発生した脆弱性について、辞書を取得します。
数分は時間が掛かるので注意。
$ for i in {2002..2016}; do go-cve-dictionary fetchnvd -years $i; done
### sqliteファイルができたことを確認
$ ls -l cve.sqlite3
権限を変えて、辞書ディレクトリにsqlite3ファイルを移動します。
$ chown vuls:vuls cve.sqlite3
$ mv cve.sqlite3 /var/vuls/dict
$ ls -l /var/vuls/dict/cve.sqlite3
-rw-r--r-- 1 vuls vuls 574898176 Jun 1 12:00 cve.sqlite3
起動スクリプトの準備
起動スクリプト(systemdファイル)を作成します。
### 環境設定ファイル作成
$ vim /etc/sysconfig/go-cve-dictionary
BIND=0.0.0.0
PORT=1323
DBPATH=/var/vuls/dict/cve.sqlite3
### 起動スクリプト作成
$ vim /usr/lib/systemd/system/go-cve-dictionary.service
### 下記参照
go-cve-dictionary.service
の中身はこんな感じです。
httpdを参考にしているので、ちょっと過剰かもしれません。
[Unit]
Description=Go CVE Dictionary Server
After=network.target remote-fs.target nss-lookup.target
[Service]
Type=simple
EnvironmentFile=/etc/sysconfig/go-cve-dictionary
ExecStart=/usr/local/sbin/go-cve-dictionary server -bind=${BIND} -port=${PORT} -dbpath=${DBPATH}
ExecStop=/bin/kill ${MAINPID}
User=vuls
Group=vuls
PrivateTmp=true
[Install]
WantedBy=multi-user.target
go-cve-dictinaryを起動
ようやく準備ができました。go-cve-dictinary
を起動しましょう。
$ systemctl daemon-reload
$ systemctl start go-cve-dictionary
### 確認
$ systemctl start go-cve-dictionary
$ ps -ef | grep go-cve-dictionary
vuls 25721 1 0 12:27 ? 00:00:00 /usr/local/sbin/go-cve-dictionary server -bind=0.0.0.0 -port=1323 -dbpath=/var/vuls/dict/cve.sqlite3
$ netstat -alnp | grep go-cve | grep LISTEN
tcp6 0 0 :::1323 :::* LISTEN 25721/go-cve-dictio
vulsを準備
vulsを手に入れる・・・のですが、 ここで詰まる可能性がある ので、続く手順を先に眺めてください。
$ go get github.com/future-architect/vuls
CentOS6の標準yumリポジトリでは、git-1.X系列しかダウンロード出来ないはずです。
この場合、gitのバージョンが古いためにgo get
が詰まってしまいます。
そこでwandiscoのリポジトリからgit-2.xをインストールします。
git-2.xのインストール
/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
しましょう。
$ go get github.com/future-architect/vuls
入りました。
vuls
もOSのパスにコピーしておきましょう。
$ cp -p go/bin/vuls /usr/local/bin/
確認
$ /usr/local/bin/vuls -v
vuls 0.1.4
vulsによる脆弱性検査
vulsの使用準備
vuls設定ファイルの作成
先ほど作ったvulsサーバーにてconfig.toml
ファイルを作成します。
現在のvulsはカレントディレクトリ以下にあるconfig.toml
ファイルを読み、カレントディレクトリ以下に結果を出力するので、 検査のたびに適当なフォルダを作って、その中で作業するのが良さそうです。
$ mkdir hoge
$ cd hoge
$ vim config.toml
中身はこのように書いていきます。
詳しくはオフィシャルを参照してください。
[servers]
[servers.localhost]
host = "127.0.0.1"
port = "22"
user = "root"
keyPath = "/root/.ssh/id_rsa"
SSH公開鍵認証の設定
先ほどの設定ファイルで指定していることから想像できると思いますが、vulsはSSHの公開鍵認証を使用しているようです。検査対象サーバー(ここではローカルホスト)には事前にSSH周りの設定をしておきましょう。
なお、このようにSSHをしたりする関係で、 vulsユーザー(daemon用ユーザー)とvuls実行ユーザー(スキャン実行用ユーザー)は分けておいたほうが無難な気がします。
このあたり、AnsibleやChefなどのSSHユーザー管理の話と同じですので、ここでは割愛します。
私の環境ではAnsibleに使用しているユーザーを使用することにしました。
なお、以下はSSH公開鍵設定の手順例です。
### 認証用の公開鍵を作る
[[vulsサーバー]]
$ su - vuls実行ユーザー
[vuls実行ユーザー] $ ssh-keygen -t rsa
[vuls実行ユーザー] $ cat ~/.ssh/id_rsa.pub
### この内容を検査対象サーバーのSSHログインユーザーに設定
[[検査対象サーバー]]
[vuls実行ユーザー] $ vim ~/.ssh/authorized_keys
[vuls実行ユーザー] $ chmod 600 ~/.ssh/authorized_keys
SSHで対象サーバーに入れることを確認しましょう。
脆弱性スキャンの実行
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 -report-text
結果はカレントディレクトリに出力されています。
$ ls -l results/current
定期的な実行を考える
定期的な実行をし続けることで、運用をもっと楽したいと思います。
脆弱性情報の定期的な更新
とりあえずcronに仕込んじゃいましょう。
コマンドの中身は簡単なワンライナーで、「過去2年間の最新情報を取得する」というものです。
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
MAILTO=""
HOME=/home/vuls
# Update CVE dict
37 05 * * * vuls go-cve-dictionary fetchnvd -last2y -dbpath=/var/vuls/dict/cve.sqlite3 >/dev/null 2>&1
実行ユーザーは環境に合わせてください。
また、実行時間は 負荷をかけないように適当に変えてください。
定期的な脆弱性診断
この設定は、どうやって通知するのが適切か考える必要があります。
vulsはslackに投げる機能もあるのですが、脆弱性発見時は結構な文章量になることを想定して、メール通知で始めてみることにしました。
メール通知を設定する
Cronで実行するためのconfigファイルを作成します。
$ mkdir /var/vuls/cron/config.toml
$ vim /var/vuls/cron/config.toml
自動実行して負荷をかけても問題ないサーバーをスキャン対象にしておきましょう。
このとき、configファイルの先頭に[mail]
ブロックを作成します。
詳しくはオフィシャルの設定を見るとして、こんな感じでローカルのsmtpサーバーを使って送信してしまいます。
[mail]
smtpAddr = "localhost"
smtpPort = "25"
from = "xxxxxx@mailaddress.xxxx"
to = ["xxxxxx@mailaddress.xxxx"]
subjectPrefix = "[vuls]"
[servers]
### 以下スキャン対象のサーバーの指定
なお、オフィシャルではsmtpPort
オプションはintegerに見えるのですが、文字列(ダブルクォートで囲む)じゃないとうまく動作しませんでした。バグ?
Cronに設定する
この設定ファイルを使ってvuls scan
を実行する設定をCronに仕込みます。
vulsを実行するユーザー(対象ホストに公開鍵でSSHログインできるユーザー)のCronとして設定する必要があります。
私の場合、先ほどのconfigファイルはそのユーザーの/var/vuls/cron
に置いているので、vulsコマンド実行前にcd
しています。
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
MAILTO=""
HOME=vuls実行ユーザーのホームディレクトリ
# Scan target machines
37 06 * * * vuls実行ユーザー cd /var/vuls/cron && vuls scan -report-text -report-mail >/dev/null 2>&1
これで 毎朝嫌というほど脆弱性通知メールが来ることが期待できます。
# 当たり障りない脆弱性は対応を手抜きしちゃいますよね・・・
最後に
以上で、かなり強引ながら、脆弱性を自動スキャンしてメール通知する仕組みを構築しました。
作ってみてすぐに察しましたが、毎日脆弱性通知が来ると すぐに慣れて見なくなってしまう という懸念がありますね。このあたり、通知方法などをブラッシュアップしていかないといけない気がしています。
投稿日時を見るとわかりますが、ほとんどテストすることなく書いた記事なので間違っていたらすみません!
# 間違いに気付き次第修正します・・・