LoginSignup
8
6

More than 3 years have passed since last update.

GitHub Actions + Trivy でDevSecOpsを実現する

Posted at

本番環境で稼働中のDockerイメージの脆弱性チェックを定期的に行なっていますか?
デプロイ時に脆弱性チェックをする方法はこちらの記事に書いていますが、運用フェーズに入ってからのセキュリティ対策について書いていませんでした。むしろ長い運用フェーズの方が脆弱性が多く発見されるので定期的なチェックは必須です。しかし、定期的なチェックは面倒だしコストがかかります。
そこで、GitHub ActionsTrivyを使って手軽に定期的に脆弱性スキャンが行える方法をご紹介します。ただスキャンするだけでは運用時には辛いので、脆弱性が発見されたらGitHubのIssueが作成されるようにします。そうすれば何に対処すればいいか各リポジトリごとに管理できますし、プルリクとIssueを関連づけてプルリクがマージされたらIssueもクローズみたいな運用をすれば対応済みかどうか分かりやすくなります。

GitHub Actions

Schedule

定期的にスキャンをするのでGitHub Actionsのschedule機能を使用します。schedule機能と言っても自体はcronです。例えば毎週月曜日の9:00(JST)にCIを回したいのであれば以下のようになります。
:warning: GitHub ActionsはUTCで動くので注意

on:
  schedule:
    - cron: '0 0 * * 1'

必要なコマンドのインストール

Trivyはもちろんですが、Issueを作成する上で以下のコマンドのインストールをします。

コマンド 説明
Trivy 脆弱性スキャンコマンド
hub Issue作成する
pandoc Trivyのスキャン結果をHTMLにする
- name: Install commands 
  run: |
    sudo apt install apt-transport-https gnupg
    wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
    echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -cs) main | sudo tee -a /etc/apt/sources.list.d/trivy.list
    sudo apt update
    sudo apt install --no-install-recommends trivy

    sudo snap install hub --classic

    VERSION=$(curl --silent "https://api.github.com/repos/jgm/pandoc/releases/latest" | \
                    grep '"tag_name":' | \
                    sed -E 's/.*"([^"]+)".*/\1/')
    curl -L -o pandoc.deb https://github.com/jgm/pandoc/releases/download/${VERSION}/pandoc-${VERSION}-1-amd64.deb
    sudo dpkg -i pandoc.deb

Dockerイメージのスキャン

Trivyでスキャンする時のオプションはこちらを参考にしてください。ここではseverity(重大度)をHIGH・CRITICALにして、脆弱性を発見したらexit codeを1にします。

- name: Scan Image
  run: trivy -q --severity HIGH,CRITICAL --exit-code 1 ${IMAGE_NAME} > tmp.txt

Issueの作成

脆弱性がなければ終了し、あればIssueをあげます。その前にIssueが見やすくなるようにTextベースのファイルをHTMLに変換します。echo -e "Security Alert\n" > result.htmlSecurity AlertがIssueのタイトル、trivyのスキャン結果は説明欄に記載、hubコマンドの-lオプションでsecurityラベルがつくようになっています。
GITHUB_TOKENGITHUB_PASSWORDに関してですが、トークンだけあればパスワードは不要とドキュメントに書いてあったのですが認証エラーになったので両方書いてあります。中身の値はどちらも同じですが...

- name: Create Issue
  if: failure()
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    GITHUB_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
  run: |
    pandoc tmp.txt -o tmp.html
    sed -i -e "s/<pre><code>//g" tmp.html
    sed -i -e "s/<\/code><\/pre>//g" tmp.html
    echo -e "Security Alert\n" > result.html
    cat tmp.html >> result.html
    sudo chown root:root /
    hub issue create -F result.html -l security

以下はテスト用にCIを走らせてみた結果です。

screenshot 2019-11-12 1.09.45.png

いい感じですね :smile:

完成形

完成形のWorkflowを書いておきます。IMAGE_NAMEGITHUB_USERを自身の環境に合わせて変更すれば動作するはずです。

name: Vulnerability scan

on:
  schedule:
    - cron: '0 0 * * 1'

env:
  IMAGE_NAME: alpine:3.10.1
  GITHUB_USER: homoluctus

jobs:
  scan:
    name: Scan images
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@master
        with:
          ref: master

      - name: Install commands
        run: |
          sudo apt install apt-transport-https gnupg
          wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
          echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -cs) main | sudo tee -a /etc/apt/sources.list.d/trivy.list
          sudo apt update
          sudo apt install --no-install-recommends trivy
          sudo snap install hub --classic

          VERSION=$(curl --silent "https://api.github.com/repos/jgm/pandoc/releases/latest" | \
                    grep '"tag_name":' | \
                    sed -E 's/.*"([^"]+)".*/\1/')
          curl -L -o pandoc.deb https://github.com/jgm/pandoc/releases/download/${VERSION}/pandoc-${VERSION}-1-amd64.deb
          sudo dpkg -i pandoc.deb

      - name: Pull images
        run: docker pull ${IMAGE_NAME}

      - name: Scan Image
        run: trivy -q --severity HIGH,CRITICAL --exit-code 1 ${IMAGE_NAME} > tmp.txt

      - name: Create Issue
        if: failure()
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITHUB_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
        run: |
          pandoc tmp.txt -o tmp.html
          sed -i -e "s/<pre><code>//g" tmp.html
          sed -i -e "s/<\/code><\/pre>//g" tmp.html
          echo -e "Security Alert\n" > result.html
          cat tmp.html >> result.html
          sudo chown root:root /
          hub issue create -F result.html -l security

      - name: Notify Result to Slack
        uses: homoluctus/slatify@master
        if: always()
        with:
          type: ${{ job.status }}
          channel: '#general'
          job_name: ':sniper: *Vulnerability Scan*'
          url: ${{ secrets.SLACK_WEBHOOK }}

これで定期的な脆弱性スキャンができるようになりました。
GitHub Actionsと様々なツールを組み合わせればDevOps/DevSecOpsが簡単に実現できますので、試してみる価値はあると思います。
余裕のある方はGitHubのdependabot、Dockerfileのリンター・スキャナであるdockleや有料ツールが豊富ですので、多角的に脆弱性チェックをするのもありでしょう。

8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6