AWS Code系サービスのCICDパイプラインに脆弱性検査を簡単かつ柔軟に組み込む(Snyk CLI)

# TL;DR
CodeBuildで簡単にSnykCLIが呼べるから，パイプラインでの脆弱性検査も**自動で・簡単に・柔軟な設定**でできるよ！
(今回はCLIでやったけど、同様の手順でAPIも呼べるはず）

追記：
https://github.com/snyk-labs/snyk-cicd-integration-examples
Snyk公式でほぼ同じものがありました…
その他パイプラインへの統合サンプルもたくさんあるので、見てみると良いかもです。


# はじめに
タイトルの通りです．AWS Code系サービスを利用したCICDパイプラインにSnyk CLIによる脆弱性診断を入れてみます．
実際のところ脆弱性検査を手で行うことは多分あんまり無いため，パイプラインにどれだけ簡単に組み込めるか？今のフローの邪魔をせずに入れられるのか？というのが気になる方は多いかなぁと思ったので．

# 導入アーキテクチャ
アーキテクチャというと大仰ですが，導入方法というと語弊がありそうなので，あえてアーキテクチャという単語を使っています．
現状Snyk(もしくはそれに準じた脆弱性診断)をAWSのCICDパイプラインに盛り込むには以下の3つが少なくともあると思います．

1. Amazon Inspector
2. CodePipelineとの統合
3. 自前でCodeBuildからのSnykの呼び出しを実装

## Amazon Inspector
はAWSの脆弱性検査のマネージドサービスであり，SSMエージェントを導入したEC2インスタンス及び，ECRリポジトリについて提供されています．完全にSnykというわけではないですが，Snykの情報を利用して脆弱性対策が強化されています．[参考](https://aws.amazon.com/jp/about-aws/whats-new/2021/11/amazon-inspector-continual-vulnerability-management/)
ECR及び，EC2インスタンスの脆弱性対策をする場合はこれを利用するのが最もお手軽かと思います．

## CodePipelineとの統合
はSnyk[公式ドキュメント](https://docs.snyk.io/features/integrations/ci-cd-integrations/aws-codepipeline-integration)に載っているものです．CodePipelineのアーティファクトに対して，脆弱性検査を行うステージがAWSとの統合に追加されており，Pipeline中のデプロイ前などにソースの検査を行う場合は最も手軽な選択肢の一つと思います．
ただ，見落としていたら申し訳ないのですがこのscanは一部のプログラミング言語に限られていて，terraformなどなどCICDの利用ケースによってはうまくハマらない可能性があります．また，流石にCLIと比較すると設定や工程の柔軟さは劣ります．

## CodeBuildからのSnyk CLIを直接呼び出す．
は[AWSのブログ](https://aws.amazon.com/jp/blogs/devops/snyk-cli-scan-python-codecommit-codepipeline-codebuild/)で紹介されているアーキテクチャに近いものを想定しています．
CodeBuildからSnykCLIを呼び出してコードの検査を行うアーキテクチャで，SnykCLIを直接実行できるため，より柔軟な設定ができたり，Snykの全機能が利用できる強みがあると思います．
一方実装は流石に上２つに比べるとほんの少し難しくはなるので，本記事で一回試してみようというわけです．


ということで張り切って作っていきます．
(ただこれがベストプラクティスかはわからないので，Snykさんのベスプラに合致しなかったらごめんなさい…)
尚，PoC的なのなので全部GUIで作ります．悪しからず．

# アーキテクチャ
大体AWSの参考記事と同じですが，今回はこんな感じのアーキテクチャにしようと思います．
スキャン結果をSNSを通じて通知します．
尚，実際はSnykのAPI TokenをとってくるためのSecretsManager等は最低限必須となるように思います．あくまで簡易化したアーキテクチャです．
![arch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/35d8b020-34f4-0d17-cbc2-affc22a92853.png)

# AWSの基本コンポーネントを作る．
AWS周りの基礎設定を行います．Snykの…というよりAWSの設定になるため，AWSに知識があり，Snyk周りのみ知りたい方にとっては退屈な内容となるかと思います．そのような方は，[snyk周りの設定をする](#snyk周りの設定をする)まで飛ばしていただければと思います．

## CodeCommitを作る．
ということでCodeCommitを作ります．
![スクリーンショット 2021-12-12 18.50.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/7c8aaf24-d123-9341-a8cf-e7e565f8c3dc.png)

作ったリポジトリに適当にスキャン用のterraformファイルを main.tf の名前で作成しておきます．
terraformを選択したのは，コード以外にIaCもできるよ！という意図です．
main.tf は[terraform公式](https://learn.hashicorp.com/tutorials/terraform/aws-build)から借りてきました．
![スクリーンショット 2021-12-12 18.55.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/61384c91-6763-0ac7-eb34-e1c55746e3dd.png)


## CodeBuildを作る．
次にCodeBuildを作ります．スクショが無いパラメタはすべてデフォルトです．
ロギングはスクショ上ではOFFで作成しましたが，この検証中に結局ログを見たくてONにしたので，皆さんは最初からONで作成することをおすすめします…
![スクリーンショット 2021-12-12 18.59.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/582ae62c-9892-0680-c712-35d45cf444e8.png)
![スクリーンショット 2021-12-12 18.59.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/2ea3dd66-2059-12a3-e17f-970078368087.png)
![スクリーンショット 2021-12-12 19.00.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/795fcd31-70ea-cc12-e3d8-571690cc4ac2.png)
![スクリーンショット 2021-12-12 19.01.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/85a1d866-ee22-117e-a644-50a96e1c6979.png)

## PipeLineを作る．
CodeCommitとCodeBuildが揃ったので，これらをまとめ上げるPipelineを作成します．
先程作ったCodeCommitとCodeBuildを登録したパイプラインを作成します．特に特殊な設定はしません．
![スクリーンショット 2021-12-12 19.02.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/4ad16859-424c-9e19-66b9-c4680596dab9.png)
![スクリーンショット 2021-12-12 19.03.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/dcbde1fb-7b09-81b2-0c64-684cb32fb538.png)
![スクリーンショット 2021-12-12 19.03.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/11b348c0-1e03-1a48-d6bc-e13844735597.png)

## CodeBuildにSNSの通知を送る権限を与える．
今回はSNS通知を送信するところまでやりたいので，Snykの検査をしたCodeBuildが結果を送信できるよう，CodeBuildにSNS関連の権限を与えます．
検証なので雑にFullAccessを与えていますが，真面目に使うときはちゃんと権限設定しましょう（当然)

![スクリーンショット 2021-12-12 19.17.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/24d038c2-5ee8-c6f0-b4d5-f7f2be7ac9d5.png)

## SNSトピックとサブスクリプションを作る
送信先のSNSトピックを作って自分のメールアドレスをサブスクリプションに登録します．
特に変なことはしてないです．

![スクリーンショット 2021-12-12 19.14.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/e9c5b106-757b-0ec7-45b8-848f44973101.png)
![スクリーンショット 2021-12-12 20.55.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/5de42e7c-49ee-3d25-959a-a4bb19aae464.png)

#snyk周りの設定をする．
まずはSnykのアカウントを作ります．SnykのアカウントはGithubやGoogleなどを利用したソーシャルログインが可能なため，特に作成で詰まることは無いかと思いますし，他に記事を上げているかたも多いと思いますので，割愛します．

## APIトークンを発行する．
Snyk CLIを利用するので，APIトークンを発行します．
まずログインしたら，全体メニューを出すために，"check the full list of integrations"をクリックします．
![スクリーンショット 2021-12-12 22.59.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/851e6a68-acda-8b35-60e3-90c0f280920a.png)

APIトークンは右上の個人設定>AccountSettingsから出せます．
**いかにもな歯車マークからは出せないので気をつけて下さい(軽くハマった)**
![スクリーンショット 2021-12-12 22.59.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/85ca8c51-8c22-e65e-baad-62295b8edd52.png)

Click to Showをクリックして自分のAPI Tokenを控えます．
**Snykの記事を見る方には釈迦に説法かと思いますが，APIトークンの扱いには気をつけて下さい**

![スクリーンショット 2021-12-12 22.59.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/9715d728-8a3e-1809-0be1-4d49c12f3461.png)

## buildspeck.yamlを作成する．

CodeBuildのbuildspec.yamlを作成します．
私は以下のコードでやりました．トレースする方は適宜API Token等置き換えて使って下さい．
コメントは後から追記しているので，うまく動かない場合はコメントを消して使って下さい．
**API_TOKENをベタ書きしてますが，どう考えても良くない**ので，実運用する場合はAPI TokenをSecretsManagerから取得するなどの処理が必要かと思います．
同じく検証をする方は，終わったらAPIトークンの無効化と再発行をすることをオススメいたします．


ハマりどころとしては，
**脆弱性検査を走らせるときに， "|| true" にしないと，脆弱性が直せないとの理由でエラーが出てCodeBuildが失敗してしまいます．**

以下[ドキュメント](https://docs.snyk.io/features/snyk-cli/guides-for-our-cli/cli-reference)より該当部分の引用をします．
>--fail-on=all|upgradable|patchable Only fail when there are vulnerabilities that can be fixed.
all - fails when there is at least one vulnerability that can be either upgraded or patched.
upgradable - fails when there is at least one vulnerability that can be upgraded.
patchable - fails when there is at least one vulnerability that can be patched.
If vulnerabilities do not have a fix and this option is being used, tests will pass.
--dry-run (only in protect command) Don't apply updates or patches during protect command run.
-- [COMPILER_OPTIONS] Pass extra arguments directly to Gradle or Maven. E.g. snyk test -- --build-cache
|| true Sets the exit code of the scan to 0. Can be used to continue with a CI/CD pipeline even when there are vulnerabilities.
Below are flags that are influencing CLI behavior for specific projects, languages, and contexts:


```buildspec.yaml
version: 0.2

phases:
  install:
    commands:
      - npm install snyk@latest -g #snykのインストール．公式参照．

  build:
    commands:
      - snyk auth <API_TOKEN> #snykへのログイン．検証なのでAPIベタ書き(良くない)
      - snyk iac test main.tf --json-file-output=snyk.log || true # main.tfにsnykのIaC　脆弱性検査を走らせ，結果をsnyk.logにJSON形式で格納． || trueにしないと，脆弱性が直せないとの理由でエラーが出てCodeBuildが失敗してしまうので注意．
      - aws sns publish --topic-arn arn:aws:sns:ap-northeast-1:<ACCOUNT_ID>:snyk-scan-test --subject "SnykResult" --message $(printf '%s' $(cat snyk.log)) # SNSに出力したJSONの内容をそのままPublish.
```

このbuildspec.yamlを，Codecommitに buildspec.yaml の名前そのままでアップロードすると，このアップロードにも反応してPipelineが実行され，Snykの脆弱性検査結果がメールで送られてきます．

当然ですが，実際にはbuildの中で修正を試みて失敗したらSNS通知するとか，その他色々料理できるのでいくらでもなんでもできると思います．


## 動作確認
このようにパイプラインが成功し，予め登録したメールアドレスにJSONの内容が送られてきます．

![スクリーンショット 2021-12-12 20.54.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/450a7fcc-0425-24f3-e524-62db340c76a9.png)

![スクリーンショット 2021-12-12 21.02.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244678/2ec30453-0415-4e09-5850-0d8c7fd02f27.png)

# 終わりに
SnykはIntegrationが今後間違いなく拡充されていくかと思いますが，その過渡期には，これまでのビルドプロセスの問題だったり，検査対象を絞りたかったり，プロジェクトごとに要件が様々出てくることもあるかと思います．
そういった場合にもCLIをCodeBuildから実行することで比較的簡単に対応可能かも？というのを示せれば嬉しいなぁと思います．

また，今回はCICDに組み込む前提としましたが，CodeBuild Projectを，EventBridgeからCron実行することで定期的スキャンしたり，またそもそもCodeBuildでなくLambdaからの利用も可能かもしれません．

log4jの大混乱(CVE-2021-44228)等々，脆弱性対策の大事さは日に日にましているので，柔軟にほんの一部でも脆弱性対策を組み込みたいですね．