Zabbix3.4をdockerで運用してVulsと連携させる 〜 その3 〜 ZabbixとVulsの連携

More than 1 year has passed since last update.


はじめに

その1ではZabbixを、その2ではVulsをインストールしました。

Zabbix3.4をdockerで運用してVulsと連携させる 〜 その1 〜 Zabbixのインストール

Zabbix3.4をdockerで運用してVulsと連携させる 〜 その2 〜 Vulsのインストール

いよいよ両者を連携させてみます。

Software Design 2017年10月号にVulsとZabbixの連携方法が載っているので、そちらを参考にしました。


目標


  • Zabbixのホスト情報からVulsの設定ファイル(config.toml)を自動生成する

  • Vulsのスキャン結果をZabbixへ連携する

  • Zabbixで脆弱性情報を表示する


Zabbix側での事前準備

Zabbixのホスト情報を用いてVulsの設定ファイルを作成するので、スキャン対象のサーバーをZabbixの監視対象にします。


【スキャン対象サーバー側】Zabbix Agentのインストールと設定

この作業はスキャン対象サーバーで行います。

Zabbix Agentをインストールします。

wget http://repo.zabbix.com/zabbix/3.4/ubuntu/pool/main/z/zabbix-release/zabbix-release_3.4-1+trusty_all.deb

sudo dpkg -i zabbix-release_3.4-1+trusty_all.deb
sudo apt-get update
sudo apt-get install zabbix-agent

Zabbix Agentの設定ファイルを編集します。

sudo vim /etc/zabbix/zabbix_agentd.conf

Server=の後ろをZabbixサーバーのIPアドレスに書き換えましょう。

設定が終わったらZabbix Agentを起動します。

sudo /etc/init.d/zabbix-agent start


スキャン対象サーバーをZabbixの監視対象に登録

Zabbixにログインし、設定→ホストから「ホストの作成」をクリックして、監視対象サーバーのIPアドレスを入力します。

テンプレートはTemplete OS LinuxTemplate_Vulsを選択しました。

スクリーンショット 2017-10-05 16.25.52.png

Template_Vulsは以下のURLからダウンロードしてZabbixにインポートしてください。

https://github.com/usiusi360/vuls_autoscan_for_zabbix


config.tomlの自動生成

Zabbixの設定からVulsの設定ファイルを自動生成する方法です。

VulsとZabbixを連携させるだけなら、この章は飛ばしても構いません。

下記URLからcreate_config.shを取得します。

https://github.com/usiusi360/vuls_autoscan_for_zabbix/

wget https://github.com/usiusi360/vuls_autoscan_for_zabbix/raw/master/create_config.sh

wget chmod 700 create_config.sh

ZABBIX_SERVERZABBIX_USERおよびZABBIX_PASSを自分の環境に合わせて書き換えましょう。

url="http://${ZABBIX_SERVER}/zabbix/api_jsonrpc.php"

は以下のように書き換える必要がありました。

url="http://${ZABBIX_SERVER}/api_jsonrpc.php"

実行にはjqが必要なので、入っていなければインストールします。

brew install jq

config.toml.masterを作成します。

create_config.shconfig.toml.masterの内容とZabbixサーバーのホスト情報を組み合わせてconfig.tomlを生成します。


config.toml.master

[default]

port = "22"
user = "vuls"
keyPath = "/home/vuls-user/.ssh/id_rsa"

内容は自分の環境に合わせて書き換えてください。

前回作成したconfig.tomlは退避しておきます。

mv config.toml config.toml.back

create_config.shを実行するとconfig.tomlが生成されました。

./create_config.sh


生成されたconfig.toml

[default]

port = "22"
user = "vuls"
keyPath = "/home/vuls-user/.ssh/id_rsa"
[servers]
[servers."Zabbix上のホスト名"]
host = "ホストのIPアドレス"

Zabbix上で、ステータスが「有効」でエージェントの状態が正常(緑色)になっているホストの情報だけが追加されます。

私の環境ではなぜかVirtualBox上のUbuntuに入れたZabbix agentをdocker上のZabbix serverからうまく認識できなかったので、別マシンにZabbix agentを入れて確認しました。

Linuxサーバー以外のホスト設定も拾ってしまうので、MacやWindowsのZabbix Agentが存在する環境ではスクリプトを改良する必要があるでしょう。

自動化する必要がなければ、スクリプトを使わずに手で書いた方が手っ取り早いです。


Vulsのスキャン結果をZabbixへ連携する


スクリプトの準備

vuls_autoscan_for_zabbix.shをダウンロードします。

wget https://github.com/usiusi360/vuls_autoscan_for_zabbix/raw/master/vuls_autoscan_for_zabbix.sh

chmod 700 vuls_autoscan_for_zabbix.sh

このスクリプトはローカル環境のVulsを前提としているので、docker上のVulsで動くように以下のように書き換えました。


vuls_autoscan_for_zabbix.sh

#!/bin/bash

ZABBIX_SERVER="localhost"
RETRY=3
PAST_YEAR=2
#PROXY="-http-proxy=http://proxy.hogehoge.co.jp:10080"

SSH_PATH="/Users/username/.ssh"

########################

VULS_HOME=`cd $(dirname $0) && pwd`
#VULS_LOG="${VULS_HOME}/results"
VULS_LOG="/vuls/results"

vuls_comm="docker run --rm --volumes-from vuls-data -v ${SSH_PATH}:/home/vuls-user/.ssh:ro -v ${VULS_HOME}:/vuls/config -v /etc/localtime:/etc/localtime:ro -e TZ=Asia/Tokyo vuls/vuls"
go_cve_dictionary_comm="docker run --rm -it --volumes-from vuls-data vuls/go-cve-dictionary"
goval_dictionary_comm="docker run --rm -it --volumes-from vuls-data vuls/goval-dictionary"

my_logger() {
local priority="user.info"
logger -i -p $priority -t `basename $0` "$1"
}

update() {
local target=$1

local last_year=`date +%Y`
local first_year=`expr ${last_year} - ${PAST_YEAR} + 1`
local years=""
for i in `seq ${first_year} 1 ${last_year}`
do
years="${years} $i"
done

for i in `seq 1 ${RETRY}`
do
$go_cve_dictionary_comm fetch${target} ${PROXY} -years $years
if [ $? -eq 0 ];then
my_logger "[INFO] Update success. [${target}]"
break
else
if
[ $i -lt $RETRY ];then
my_logger "[INFO] Update retry. [${target}] (count=$i)"
sleep 10
else
my_logger "[ERROR] Update retry over. [${target}] (count=$i)"
fi
fi
done

}

update_oval() {
local target=$1
local option="$2"

for i in `seq 1 ${RETRY}`
do
$goval_dictionary_comm fetch-${target} ${PROXY} ${option}
if [ $? -eq 0 ];then
my_logger "[INFO] Update-OVAL success. [${target}]"
break
else
if
[ $i -lt $RETRY ];then
my_logger "[INFO] Update-OVAL retry. [${target}] (count=$i)"
sleep 5
else
my_logger "[ERROR] Update-OVAL retry over. [${target}] (count=$i)"
fi
fi
done

}

scan(){
$vuls_comm scan -deep -config=/vuls/config/config.toml -ssh-native-insecure
if [ $? -eq 0 ];then
my_logger "[INFO] Scan success."
else
my_logger "[ERROR] Scan fail."
exit 1
fi
}

report(){
$vuls_comm report -format-json -cvedb-path=/vuls/cve.sqlite3 -ovaldb-path=/vuls/oval.sqlite3 -format-short-text -config=/vuls/config/config.toml -lang=ja
if [ $? -eq 0 ];then
my_logger "[INFO] Scan success."
rm -rf ${VULS_LOG}
docker cp vuls-data:/vuls/results ${VULS_LOG}
else
my_logger "[ERROR] Scan fail."
exit 1
fi
}

send_zabbix(){
files="${VULS_LOG}/current/*.json"
for filepath in $files; do
TARGET_NAME=`basename $filepath .json`
if [ "${TARGET_NAME}" == "all" ]; then
continue
fi
zabbix_sender -z ${ZABBIX_SERVER} -s ${TARGET_NAME} -k nvd_count -o `cat $filepath | jq '.ScannedCves? | length'`
zabbix_sender -z ${ZABBIX_SERVER} -s ${TARGET_NAME} -k nvd_max -o `cat $filepath | jq '[.ScannedCves[] | .CveContents.nvd.Cvss2Score]+[0] | max'`
done
}

rotate(){
firstDay=`date '+%Y-%m-01'`
agoYear=`date -d "$firstDay 1 months ago" '+%Y'`
agoMonth=`date -d "$firstDay 1 months ago" '+%m'`
mkdir ${VULS_LOG}/$agoYear-$agoMonth > /dev/null 2>&1
mv ${VULS_LOG}/$agoYear$agoMonth* ${VULS_LOG}/$agoYear-$agoMonth > /dev/null 2>&1
}

#======

cd ${VULS_HOME}
if [ $? -ne 0 ];then
my_logger "[ERROR] path not found [${VULS_HOME}]"
exit 1
fi

## update ##
update nvd
update jvn

## update oval ##
#update_oval redhat "5 6 7"
#update_oval debian "7 8 9 10"
#update_oval ubuntu "12 14 16"
#update_oval oracle

## vuls scan ##
scan

## vuls report ##
report

## send zabbix ##
send_zabbix

## rotate ##
#rotate

exit 0


ZABBIX_SERVERSSH_PATHは環境に合わせて書き換えてください。

コメントアウトは必要に応じて外してください。

rotateはdocker用に修正していないので、コメントアウトを外しても正常に動かないはずです。

(2017/10/12追記) 最初、dockerに-itオプションを付けていましたが、cronから実行すると"the input device is not a TTY"というエラーが出て動かなかったため、外しました。


スキャン結果格納用フォルダの作成

Vulsのスキャン結果を格納するフォルダを作成します。

/vuls/results/currentが絶対パスのリンクになっているので、絶対パスで作らないと動きません。

改良したいところです。

sudo mkdir /vuls

sudo chmod 777 /vuls


zabbix_senderのインストール

スクリプトの実行にはzabbix_senderが必要です。

残念ながら公式dockerイメージはないようなのでローカルにインストールします。

Macでは--without-server-proxyオプション付きでzabbixをインストールすると、Zabbix Agentのみがインストールされますが、この時zabbix_senderも一緒にインストールされます。

brew install --without-server-proxy zabbix


Zabbix連携スクリプトの実行

./vuls_autoscan_for_zabbix.sh

スクリプトを実行すると以下の処理が行われます。


  • 脆弱性データベースの更新(NVD, JVN)

  • OVALデータの更新(コメントアウトを外した場合)

  • スキャン実行

  • レポート作成

  • Zabbixへレポートを連携

正常に連携が完了するとZabbix側でmax vulnerability(脆弱性スコアの最大値)とnumber of vulnerabilities(脆弱性の件数)が更新されます。

スクリーンショット 2017-10-06 16.01.10.png

前回チェック時から検知数が増えた場合にアラートが上がります。

スクリーンショット 2017-10-06 16.24.23.png

グラフも表示できます。

これはapt-get upgradeを行った直後にスクリプトを動かした結果です。

脆弱性の件数が減っていることがわかりますね。

スクリーンショット 2017-10-06 16.19.24.png

あとはスクリプトをcronで毎日実行すれば脆弱性の検知はばっちりですね。


課題


  • Vuls設定ファイルの自動生成スクリプト


    • Zabbix上の設定からVulsの設定ファイルを生成する際に、Vuls未対応のOSでも生成されてしまう



  • Zabbix連携スクリプト


    • zabbix_senderもdockerで動かしたい(公式イメージにこだわらなければある)

    • Vulsのスキャン結果を任意の場所に格納できるようにしたい



  • Vuls本体


    • Mac上のdockerコンテナもVulsのスキャン対象にできればいいのに(Linux上のコンテナはスキャン可能)


      • 今はホストOSがVuls非対応の時点でエラーになってコンテナスキャンされない

      • ホストOSはスキャンせず、コンテナだけスキャン対象にするオプションができれば解決






最後に

ZabbixとVulsは単独でも強力なツールですが、組み合わせるといっそう便利になります。

Docker上で動かせば、環境の移行も楽ですよ。