ニフクラ ブログ

ニフクラやクラウドの技術について、エンジニアが語るブログです。

NIFTY Cloudの脆弱性診断をJenkinsで自動実行し、結果をSlackに通知してみた

インターネットサービスを提供しているサーバーに脆弱性があると、侵入や改ざん、情報漏えいといったセキュリティインシデントを引き起こす原因となってしまうのは皆さんもよくご存じかと思います。

次々に発見されるOSの脆弱性はもちろん、ミドルウェアのバージョン変更によっても脆弱性が生じることもあるため、このような被害を防ぐためには、定期的にサーバーの脆弱性診断を行うことが重要です。

ニフティクラウド脆弱性スキャンを使えば、ご利用中のサーバーの脆弱性を簡単に検知することができます。

今回は、Jenkinsを使って脆弱性診断を自動で実行し、結果をSlackに通知する仕組みを構築してみました。 vss_jenkins

設定

スキャンテンプレートの作成

ニフティクラウドのコンパネにログインし、メニューより「脆弱性スキャン」のページへ移動してスキャンテンプレートを作成します。

vss_menu

今回はニフティクラウドのeast-1リージョンに構築した「webserver」の脆弱性をスキャンしてみます。

vss_template

Jenkins

脆弱性診断を自動で実行するためにJenkinsを構築します。 Jenkinsを起動するために、ニフティクラウドにCentOS 7.1のサーバーを作成します。

vss_create_instance

Jenkinsのインストール

サーバー作成が完了したら、サーバーにログインして以下のようにJenkinsをインストールします。

[root@jenkins]

# yum -y install java-1.8.0-openjdk
# curl -L -o  /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat-stable/jenkins.repo
# rpm --import https://jenkins-ci.org/redhat/jenkins-ci.org.key
# yum -y install jenkins
# chkconfig jenkins on
# systemctl start jenkins

インストールが完了したら、ブラウザーから http://サーバーのIPアドレス:8080 にアクセスするとJenkinsのUI画面が表示されます。 表示される画面にしたがってJenkinsの初期設定を実施し、ユーザーを作成してログインします。 この時「install suggested plugins」を選択しておきます。

vss_install_jenkins

Slackの通知設定

脆弱性スキャンの結果をSlackに通知させるために設定を行います。 SlackのApps&integrationから、Jenkinsで検索して追加し通知したいチャネルを指定します。 生成されたTokenはJenkins側で設定する必要があるのでメモを取っておきます。

vss_jenkins_search

vss_slack_jenkins

JenkinsのUIにて、[Jenkinsの管理]→[プラグインの管理]から利用可能タブで「slack」と検索し「Slack Notification Plugin」をインストールします。

vss_jenkins_slack_plugin

インストールが完了したら、[Jenkinsの管理]→[システムの管理]から「Global Slack Notifier Settings」セクションにて先ほどSlackから追加したJenkinsのTokenを入力して保存します。

vss_jenkins_slack_settings

CLIツールのインストール

Jenkinsから脆弱性スキャンを実行するためにはNIFTY Cloud 脆弱性スキャンAPIをリクエストする必要があります。 APIをリクエストするために、go-ncvs-cliというコマンドラインツールを作ってみました。 個人で作ったもので、ニフティクラウドの公式ツールではありませんが、基本的なAPI操作は可能となっています。 サーバーにて、以下のようにインストールします。

[root@jenkins]

# curl -L -o /usr/local/bin/ncvs-cli https://github.com/harikiriboy/go-ncvs-cli/releases/download/v1.0.0/ncvs-cli
# chmod +x /usr/local/bin/ncvs-cli

インストールが完了したら、JenkinsのUIで[Jenkinsの管理]→[システムの管理]から「グローバル プロパティ」セクションにて環境変数を設定します。 コンパネから取得したアクセスキーをシークレットキーを[AWS_ACCESS_KEY_ID] [AWS_SECRET_ACCESS_KEY] に入力します。 CLIを実行するために/usr/local/binにもパスを通しておきます。

vss_niftycloud_key

vss_jenkins_key

ジョブの作成

準備が整ったので、脆弱性スキャンを実行するジョブを作成します。 [新規ジョブの作成]から任意のジョブ名を入力して[Pipeline]を選択します。

vss_jenkins_create_job

「ビルドのパラメーター化」にチェックを入れ、文字列パラメーター[scan_template_name]を追加します。 vss_jenkins_param

「Pipeline」欄に以下のようなスクリプトを入力し、保存します。

vss_jenkins_script

    import groovy.json.JsonSlurperClassic

    node('master') {
        currentBuild.result = "SUCCESS"

        try {

            def scan_history_uuid

            stage('execute-scan') {
                def json = sh returnStdout: true, script: "ncvs-cli execute-scan -scan-template-name ${scan_template_name}"
                def resp = jsonParse(json)

                scan_history_uuid = resp.ScanHistory.ScanHistoryUUID
            }

            stage('describe-scan-histories') {
                waitUntil {
                    sleep 60
                    def json = sh returnStdout: true, script: "ncvs-cli describe-scan-histories -scan-template-name ${scan_template_name}"
                    def resp = jsonParse(json)
                    def scan_history = findScanHistory(resp.ScanHistories, scan_history_uuid)

                    echo "Status is ${scan_history.Status}"

                    if (scan_history.Status == "running") {
                        return false
                    }
                    if (scan_history.Status == "waiting") {
                        return false
                    }
                    if (scan_history.Status == "completed") {
                        return true
                    }
                    throw new RuntimeException("Status is ${scan_history.Status}.")
                }
            }

            stage('describe-scan-results') {
                def json = sh returnStdout: true, script: "ncvs-cli describe-scan-results -scan-history-uuid ${scan_history_uuid}"
                def resp = jsonParse(json)

                for (def scan_result: resp.ScanResults) {
                    def color
                    switch (scan_result.Severity) {
                        case 0:
                            color = "#357abd"
                            break
                        case 1:
                            color = "#4cae4c"
                            break
                        case 2:
                            color = "#bcd323"
                            break
                        case 3:
                            color = "#ee9336"
                            break
                        case 4:
                            color = "#ff0000"
                            break
                        default:
                            break
                    }
                    slackSend color: color, message: "NIFTY Cloud Vulnerability Scan Result\n  - ${scan_result.Rule.RuleName}\n  - see more information https://console.cloud.nifty.com/web/#vss/scan-history/${scan_history_uuid}"
                }
            }

        } catch (err) {
            currentBuild.result = "FAILURE"
            throw err
        }
    }

    @NonCPS
    def jsonParse(def json) {
        return new groovy.json.JsonSlurperClassic().parseText(json)
    }

    @NonCPS
    def findScanHistory(list, uuid) {
        return list.find {
            it.ScanHistoryUUID == uuid
        }
    }

実行

以上でジョブの設定は完了です。 試しにパラメーター付きビルドからジョブを実行してみます。 [scan_template_name]には実行したいスキャンテンプレート名を入力します。

vss_jenkins_exec

ジョブを実行すると、実際にスキャンが開始されます。 スキャンが完了し、結果がSlackに通知されていれば成功です。

vss_scan_result

あとは、このジョブを「定期的に実行」にしておけば継続的な脆弱性スキャンが自動で実行できますし、アプリケーションのデプロイジョブの後に実行するように設定しておくことで、環境更新後の脆弱性有無のチェックも自動で行えます。 Jenkinsで実行するScriptをカスタマイズして条件文を入れれば、CVSSスコア○○以上の場合のみ通知といったことも可能です。

まとめ

ニフティクラウド脆弱性スキャンを使えば、脆弱性スキャンを自動で簡単に実施することができました。 ニフティクラウドタイマーとも連携しているので、単純に脆弱性スキャンをスケジュール実行したい場合はタイマーを使うのもおすすめです。この場合は、自分でJenkinsサーバーを構築する必要もありません。

vss_timer

とても便利なので皆様も是非使ってみてください。