trivy-actionを使ってDocker imageの脆弱性診断をやってみた

## はじめに
皆さんこんにちはasmgです。今回は、セキュアな状態で、Dockerimageを作成するために、GitHubActions上でtrivyを使いDocker imageの脆弱性診断をやってみたいと思います。

## 本記事の対象者
本記事は、GitHub Actionsでtrivyを利用したことをない人を対象としています。
また、trivy-actionをとりあえず触りたいと言う人でも触れるような構成で書いていきたいと思います。

## 本記事のゴール
trivy-actionをGitHub Actions上で動かし、Docker imageの脆弱性診断を行い、GitHub上のSecurityページまたは、issueに情報を記録するところまでをゴールにしたいと考えています。
## 今回使うもの
### ツール類
* GitHub Actions
* 脆弱性診断ツール: trivy
* 公開アクション: trivy-action

## 脆弱性診断ツール trivyとは？

> コンテナイメージ、ファイルシステム、Git リポジトリの脆弱性、および設定の問題を検出するシンプルで包括的なスキャナツール。 Trivyは、 OSパッケージ（Alpine、RHEL、CentOSなど）および言語固有のパッケージ（Bundler、Composer、npm、yarnなど）の脆弱性を検出する。
また、 Terraform、Dockerfile、KubernetesなどのInfrastructure as Code (IaC) ファイルをスキャンし、デプロイメントの攻撃リスクにさらす潜在的な設定問題を検出する。

※https://github.com/aquasecurity/trivy をDeepLで翻訳後要約

## 注意点
trivy-actionの出力結果をプライベートリポジトリで利用するためには以下のライセンスを契約しておく必要があります。(※1)
* GitHub One ライセンス
https://docs.github.com/en/get-started/learning-about-github/githubs-products#github-one
* Enterprise + Code scanning オプションライセンス
https://github.com/features/security#security-prevent

(※1)GitHubのCode Scanningを使ってみる
https://blog.motikan2010.com/entry/2020/10/11/%E3%80%90%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E3%80%91GitHub%E3%81%AECode_Scanning%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%8B

## 実際にやってみる
### 手順
trivy-actionのドキュメントに乗っている方法をやってみたいと思います。
https://github.com/aquasecurity/trivy-action/blob/master/README.md
trivy-actionのスキャン以下のような方法があります。 
* Docker imageをスキャンする
* Github repositoryをスキャンする方法
* プライベートレジストリをスキャンする方法 など...

今回は、"Docker imageをスキャンする" を採用し、Dockerfileをbuild後、imageの脆弱性診断をしていこうと思います。

以下が実際に作成したコード全体になります。
```yaml:trivy-github-action.yaml
name: docker-build
on:
  pull_request:
    branches:
      - main
    types:
      - opened
jobs:
  step:
    runs-on: ubuntu-latest
    steps:
        - name: Checkout code
          uses: actions/checkout@v2
        - name: dockerfile build 
          run : |
            docker build -t python_code。
        - name: run trivy for docker image
          uses: aquasecurity/trivy-action@master
          with:
            image-ref: 'python_code'
            format: 'sarif'
            output: 'trivy-results.sarif'
            ignore-unfixed: true
            vuln-type: 'os,library'
            severity: 'CRITICAL,HIGH'

        - name: Upload Trivy scan results to GitHub Security tab
          uses: github/codeql-action/upload-sarif@v2
          with:
            sarif_file: 'trivy-results.sarif'

```
#### トピック:GitHub Actionsの動作条件
実行条件は、main branchにPRが作成された時をトリガーに脆弱性診断を実行したいと思います。
```yaml:実行条件
name: docker-build
on:
  pull_request:
    branches:
      - main
    types:
      - opened
```
今回は、テストで動かしているためPRを作成した時のみ動作させていますが通常脆弱性診断を行う場合、作成時のみではなく、PRの更新時等にも実行する必要があります。その場合には、typesにsynchronizeを付ける必要があります。
```yaml:実行条件(PRを作成/更新した場合)
name: docker-build
on:
  pull_request:
    branches:
      - main
    types:
      - opened
      - synchronize
```
このようなトリガー条件は、読者の皆さんのタイミングで実行させる必要がありますので適宜変更してください。

#### 実行結果
##### 特に問題がない場合（脆弱性を確認できなかった場合）
![スクリーンショット 2022-05-28 21.18.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567360/97940924-dd60-7e07-ee4b-6d912f25aa94.png)
##### 問題があった場合（脆弱性を確認できた場合）
今回は、 実際に脆弱性のあるライブラリを使って実行してみました。
* 該当ライブラリ: flask==0.12.3
ライブラリ:https://pypi.org/project/Flask/0.12.2/
脆弱性情報:https://snyk.io/vuln/pip:flask

Security> Code scanning alert
![スクリーンショット 2022-05-29 14.25.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567360/b69ebe3b-3772-2cef-dae8-2e5cb5a22ca2.png)

## trivy-actionを使ってissueに登録する
GitHub Code scanning上のCode scanning alertに、プライベートリポジトリで通知させる場合ライセンス料が発生します。
なので、公式には書かれていませんが、本記事ではtrivy-actionの出力結果をGitHub Code scanning上のCode scanning alert以外に出力する方法を検討してみました。
### Github issueに出力する方法
```yml:GitHub issueに通知させる
name: docker-build
on:
  pull_request:
    branches:
      - main
    types:
      - opened
      - synchronize

jobs:
  step:
    runs-on: ubuntu-latest
    steps:
        - name: Checkout code
          uses: actions/checkout@v2
        - name: dockerfile build 
          run : |
            docker build -t python_code .
        - name: run trivy for docker image
          uses: aquasecurity/trivy-action@master
          with:
            image-ref: 'python_code'
            format: 'table'
            output: 'trivy-results.table'
            ignore-unfixed: false
            exit-code: '1'
            vuln-type: 'library'
            severity: 'MEDIUM,HIGH,CRITICAL'
        - id: trivy_output
          name: output_file
          if: failure()
          run: |
            output_data=$(cat ./trivy-results.table)
            output_data="${output_data//$'\n'/\\n}"
            echo "::set-output name=std_out::$output_data"
        - name: create_issue
          if: failure()
          uses: actions/github-script@v3
          with:
            github-token: ${{secrets.GITHUB_TOKEN}}
            script: |
              const { GITHUB_SERVER_URL, GITHUB_REPOSITORY, GITHUB_RUN_ID } = process.env
              var trivy_output_data = "${{ steps.trivy_output.outputs.std_out }}"
              console.log(trivy_output_data);
              github.issues.create({
                owner: context.repo.owner,
                repo: context.repo.repo,
                title: 'Security Alert',
                body: "```"+`${trivy_output_data}`+"```",
              })
```
上記のようなワークフローでコードを実行すると、issueに表を出力することができます。
![スクリーンショット 2022-05-31 13.40.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/567360/505c5c89-24c4-b89d-76fb-e26520644969.png)
この方法を使うとプライベートリポジトリでも、trivy-actionの出力結果を通知することが可能になると思います。

## おまけ:trivyのカスタマイズ方法(input)
trivyの設定は、 GitHub Actionのwith:で設定します。
```yml:trivyの設定部分
- name: run trivy for docker image
  uses: aquasecurity/trivy-action@master
  with:
   #ここに設定を入れる
```

今回は、 重要そうなものだけをピックアップして載せます。
| name        |  Description                |
| ----        | ----                        |
|  scan-type  |スキャンタイプを選べる[image/fs]   |
|  format     |formatを選べる[table/json/sarif]|
|  output     |結果をファイルに保存する ex) trivy-results.sarif|
|  ignore-unfixed  |未修正の脆弱性を無視するかどうかを選ぶ [true/false]|
|  vuln-type  |なんの脆弱性を診断するかを選ぶことができる [os/library]|
|  severity   |スキャンして表示する脆弱性の重大度を選ぶことができる[UNKNOWN/LOW/MEDIUM/HIGH/CRITICAL]|

詳しくは、ドキュメントを参照
https://github.com/aquasecurity/trivy-action/blob/master/README.md
## 最後に
GitHub Actionsを使って、Docker imageの脆弱性診断を行なって、github上のSecurityページに情報を記録するところまでの流れを実際にやってみました。
今後の課題としては、この脆弱性診断の結果を踏まえてDockerレジストリにimageをpushするなどの条件を加えるなどができると良いと考えています。
最後までご覧いただきありがとうございました。