💎

脆弱性検知ツールVulsをZabbixと連携する

2023/05/04に公開

はじめに

前回でVulsサーバの構築が完了しました。
https://zenn.dev/malwarekiddy/articles/52c8619f000d3b
今回は次のような仕組みを作って、Zabbixと連携する手順を記載します。
Zabbixとの連携

Zabbixとの連携

zabbix-sernderのインストール

zabbix-senderjqをインストールします。

$ sudo apt install jq zabbix-sender

vuls_autoscan_for_zabbix

usiusi360さんのGitHubリポジトリから、vuls_autoscan_for_zabbixのシェルスクリプトを取得します。

$ curl -O https://raw.githubusercontent.com/usiusi360/vuls_autoscan_for_zabbix/master/create_config.sh
$ curl -O https://raw.githubusercontent.com/usiusi360/vuls_autoscan_for_zabbix/master/vuls_autoscan_for_zabbix.sh
$ chmod 700 create_config.sh vuls_autoscan_for_zabbix.sh

create_config

Zabbixに登録されているホストを自動で取得してくれて、それらをVulsスキャン対象ホストとして自動でconfig.tomlを作成してくれるシェルスクリプトです。
自分の環境に合わせて編集が必要ですので、まず原本を退避させます。

$ cp create_config.sh create_config.sh.org

create_config.shに自分のZabbixサーバの情報を記載します。
加えて、ここではZabbixAPIのURLをhttpsに変更しました。

$ vi create_config.sh
$ diff create_config.sh create_config.sh.org
3,5c3,5
< ZABBIX_SERVER="{YOUR ZABBIX SERVER}"
< ZABBIX_USER="Admin"
< ZABBIX_PASS="xxxxxxx"
---
> ZABBIX_SERVER="localhost"
> ZABBIX_USER="Admin"
> ZABBIX_PASS="hogehoge"
10c10
< url="https://${ZABBIX_SERVER}/zabbix/api_jsonrpc.php"
---
> url="http://${ZABBIX_SERVER}/zabbix/api_jsonrpc.php"

config.toml.master

config.tomlを生成する元となる、マスターファイルなるものを作成します。
以下の記述があればマスターファイルとして機能します。

$ vi config.toml.master
[default]
port = "22"
user = "locadmin"
KeyPath = "/home/vulsuser/.ssh/xxxxx"

create_config.shを実行

create_config.shを実行すると既存のconfig.tomlが消えるので、実行前に退避しておきます。

$ cp config.toml config.toml.org

create_config.shを実行します。

$ ./create_config.sh

これでZabbixに登録されているホストがconfig.tomlに自動的に記載されます。
(エージェント監視しているものが追加されていることを確認しました。)
作成された内容を確認します。

$ cat config.toml

設定の調整

必要に応じてスラック通知の設定や、対象ホストに個別の鍵やユーザ名を設定します。
個別に指定しなかった項目は[default]に記載の値が適用されます。

[slack]
hookURL = "https://hooks.slack.com/services/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"
channel = "#vuls"

[default]
port = "22"
user = "locadmin"
KeyPath = "/home/vulsuser/.ssh/xxxxx"

[servers]
[servers.name1]
host = "192.168.1.1"
user = "xxxxx"

[servers.name2]
host = "scanserver1"
keyPath = "/home/vulsuser/.ssh/xxxxx"

[servers.name3]
host = "10.1.1.1"
user = "xxxxx"
~snip~

定期実行

vuls_autoscan_for_zabbixをcronで定期実行します。
月初のサーバチェックで使用するので、毎月1日の午前1時にvuls_autoscan_for_zabbixを実行します。2時にログローテーションも設定しました。

$ crontab -l
~snip~
#vuls autoscan for zabbix
00 1 1 * * /home/vulsuser/vuls_autoscan_for_zabbix.sh > /var/log/vuls/vuls.log 2>&1

#vuls results remove
00 2 1 * * find /home/vulsuser/results/ -mtime +180 -delete > /dev/null 2>&1

vuls_autoscan_for_zabbixの改造

作業当時は、vuls_autoscan_for_zabbix内にあるVulsのコマンド構文が上手く動作しなかったため、少し修正を加えました。例えば次のような箇所です。

+ go-cve-dictionary fetch ${target} ${PROXY} ${years}
- go-cve-dictionary fetch${target} ${PROXY} -years $years
+ goval-dictionary fetch ${target} ${PROXY} ${option}
- goval-dictionary fetch-${target} ${PROXY} ${option}

jqでスキャン結果のjsonを処理する部分も上手く動作しなかったため、機能の追加も兼ねて以下のように自作しました。

  • 初回スキャンは全ての脆弱性を、以降は増加分の脆弱性を報告する。
  • 以下の内容を報告する。
    • CVE ID
    • 影響を受けるパッケージ
    • CVSS3の最大スコア
    • 脆弱性の個数
send_zabbix(){
  files="${VULS_LOG}/current/*.json"
  for filepath in $files; do
    TARGET_NAME=`basename $filepath .json`
    echo -n >| /home/vulsuser/results/current/${TARGET_NAME}_cve_list_new
    cve_list_new="/home/vulsuser/results/current/${TARGET_NAME}_cve_list_new"
    echo -n >| /home/vulsuser/results/current/${TARGET_NAME}_diff_list
    diff_list="/home/vulsuser/results/current/${TARGET_NAME}_diff_list"
    echo -n >| /home/vulsuser/results/current/${TARGET_NAME}_score
    score="/home/vulsuser/results/current/${TARGET_NAME}_score"
    echo -n >| zabbix.log
    zabbix_log="zabbix.log"

    cve_list="/home/vulsuser/results/list/${TARGET_NAME}_cve_list"
    if [ ! -e ${cve_list} ]; then
	    touch ${cve_list}
    fi

    if [ "${TARGET_NAME}" == "all" ]; then
      continue
    fi
    cat $filepath | jq -r '..|.cveID? | select(.!=null)' | uniq >> $cve_list_new
    diff $cve_list_new $cve_list | egrep "^<" | cut -d " " -f 2- >> $diff_list
    if [ -s $diff_list ]; then
      zabbix_sender -z ${ZABBIX_SERVER} -s ${TARGET_NAME} -k nvd_count -o `grep -c '' $diff_list` >> $zabbix_log 2>&1
      zabbix_sender -z ${ZABBIX_SERVER1} -s ${TARGET_NAME} -k nvd_count -o `grep -c '' $diff_list` >> $zabbix_log 2>&1
      while read LINE; do
        pkg_name=`cat $filepath | jq '..|."'$LINE'"? | select(.!=null)' | jq -r '..|.name? | select(.!=null)' | perl -pe 's/\n/, /g' | perl -pe 's/, $//g'`
        cat $filepath | jq '..|."'$LINE'"? | select(.!=null)' | jq -r '..|.cvss3Score? | select(.!=null)' >> $score
        echo ${TARGET_NAME} cve_id ${LINE}, Packages: ${pkg_name} | zabbix_sender -z ZABBIX_SERVER -i - >> $zabbix_log 2>&1
      done < $diff_list
      zabbix_sender -z ${ZABBIX_SERVER} -s ${TARGET_NAME} -k nvd_max -o `cat $score | awk '{if(m<$1) m=$1} END{print m}'` >> $zabbix_log 2>&1
      zabbix_sender -z ${ZABBIX_SERVER1} -s ${TARGET_NAME} -k nvd_max -o `cat $score | awk '{if(m<$1) m=$1} END{print m}'` >> $zabbix_log 2>&1
      cp $cve_list_new $cve_list
    else
      zabbix_sender -z ${ZABBIX_SERVER} -s ${TARGET_NAME} -k nvd_count -o 0 >> $zabbix_log 2>&1
      zabbix_sender -z ${ZABBIX_SERVER1} -s ${TARGET_NAME} -k nvd_count -o 0 >> $zabbix_log 2>&1
      zabbix_sender -z ${ZABBIX_SERVER} -s ${TARGET_NAME} -k nvd_max -o 0 >> $zabbix_log 2>&1
      zabbix_sender -z ${ZABBIX_SERVER1} -s ${TARGET_NAME} -k nvd_max -o 0 >> $zabbix_log 2>&1
    fi
  done
}

また、スキャン結果やvuls_autoscan_for_zabbix実行中のエラーもスラックで通知するように追記しました。
my_loggerはあらかじめvuls_autoscan_for_zabbix内に記載されている関数です。

hook() {
  curl -s -S -X POST --data-urlencode "payload={\"channel\": \"${channel}\", \"username\": \"${username}\", \"text\": \"$1\" }" ${hookURL} > /dev/null 2>&1
}

scan(){
  echo -n >| vuls_scan.log
  scan_log="vuls_scan.log"
  vuls scan >> $scan_log
    if [ $? -eq 0 ];then
      my_logger "[INFO] Scan success."
    else
      my_logger "[ERROR] Scan fail."
      scan_err=`cat $scan_log`
      hook "[ERROR] Scan fail. $scan_err"
      exit 1
    fi
}

report(){
  echo -n >| vuls_report.log
  report_log="vuls_report.log"
  vuls report -format-json >> $report_log
    if [ $? -eq 0 ];then
      my_logger "[INFO] Scan success."
    else
      my_logger "[ERROR] Scan fail."
      report_err=`cat $report_log`
      hook "[ERROR] Scan fail. $report_err"
      exit 1
    fi
}

これでVulsとZabbixの連携についての手順は終了です。

Discussion