モチベーション

DevOpsサイクルを素早く回し、柔軟で高速なフィードバック&リリースを進めるなかで、ときにセキュリティの観点が後回しになってしまうかもしれません。
そうなるとリリースまでにリスクを除去しきれない、リスクの発見が遅れるといったことが発生し、結果的に新機能のリリースが遅れてしまいます。

DevSecOps

DevOpsサイクルにセキュリティの自動化を組み込んだDevSecOpsという考え方があります。
サイクルの中にセキュリティを組み込み、できるだけ早い段階で問題・リスクを発見し対応することで安定したリリースにつなげます。(セキュリティのシフトレフト)

セキュリティについては専門知識も必要な部分がありますが、AWSでは様々なセキュリティ関連サービスが存在します。
参考:AWSクラウドセキュリティAWSアカウントのセキュリティ対策(iret.media)
セキュリティも専門チームだけが責任を負うのではなく、DevOpsで開発と運用が同じ目的と責任を共有するのと同じように責任を共有し、サイクルの中に組み込んでいくなかでこういったサービスやツールは活用していきたいところです。

一例として今回はAWS CodeGuru Reviewerを使ってコードの脆弱性の検知をDevOpsサイクルに組み込んでみたいと思います。

AWS CodeGuru Reviewer

AWS CodeGuru ReviewerはMLを活用したコードレビューサービスです。
コード品質やコストのかかるコードに関する推奨事項だけでなくハードコーディングされたシークレットログインジェクション脆弱性などの脆弱性についてもチェックしてくれます。

リポジトリに紐づけるだけでプルリクエスト作成時にレビューが走って推奨事項がある場合はコメントをつけてくれます

私のチームでは推奨事項コメントに対して修正をするかは人間の判断に委ねる対応をしていました。
さらに今回はセキュリティ関連の推奨事項に関しては原則必須で対応する形でやってみることにしました。
CodeGuru Reviewerをパイプラインに組み込み、コード脆弱を検知した場合は実行を中断し、リリースを失敗させる仕組みを作成します。

セキュリティを担保しつつサイクルを回すスピードを落とさないためプロセスをできるだけ自動化していきます。

対象システム

今回は実際のシステムに組み込んで運用しています。
担当は2人いて、コードは主担当が書いて、副担当(わたし)がレビューしてマージすることが多いです。
その背景を含めて記載すると以下のようになります。

構成

今回はCodeGuru ReviewerによるレビューをCodePipeline + CodeBuildで構築しているパイプラインに組み込んでみました。
CodeGuru Reviewerは「Github Actions」との統合はサポートしていますが、今回対象としたプロジェクトでは「CodePipeline」を利用しているので自前スクリプトを噛ませています。

CodeGuru Reviewerのaws-cliコマンドを呼び出すスクリプトをCodeBuildのbuildspecファイルから呼び出すことで実現しています。シェルスクリプトからaws-cliを叩いているだけなので、条件を揃えれば他のパイプラインサービスからも使えると思います。

既存のパイプラインにCodeGuru Reviewerによるレビューを追加しました。赤色が今回追加したところ。

今回はフルスキャンをかけて推奨事項が1つでも残っていたらパイプラインを失敗させています。CodeGuru Reviewerでのフルスキャンは少し時間かかるので(試しに組み込んだプロジェクトで5分くらい)、場合によってはステージ別にON/OFFしやすい作りにしておくといいかもしれません。ちなみにもともとユニットテストのタイミングで「PEP8」のチェックはかけています。

CodeGuru Reviewer 呼び出しスクリプト

やっていること

  1. レビューの生成(aws codeguru-reviewer create-code-review) # create review
  2. レビュー完了待ち(aws codeguru-reviewer wait code-review-completed ) # wait
  3. レビュー結果取得(aws codeguru-reviewer describe-code-review) # count recommendations
  4. 推奨事項が0なら成功(exit 0)、そうでなければ失敗(exit 1)

レビュー対象のブランチ名は、パイプラインから渡しています。

準備

環境変数

以下の環境変数を設定します。
(引数にして呼び出し側で環境変数指定するようにしてもいいかも)

  • REPOSITORY_BRANCH = 対象ブランチ名
  • CODEBUILD_BUILD_ID = ビルドID。今回はCodeBuildが持っている環境変数から良さげなのをそのまま使いました。CodeBuild以外から使う場合はユニークなIDを設定してください。レビューにアカウント内で一意の名前をつけるのに使ってます。
  • CODEGURU_REVIEWER_ASSOCIATION_ARN = CodeGuruReviewerの関連付けARN(※1)

(※1) CodeGuru Reviewerの関連付けARN
CodeGuruのリポジトリ設定は済んでいる前提でマネコンには「関連付けID」だけ表示されるのでARNフォーマットでは表示されないので、自分でARNフォーマットにする必要あり
arn:aws:codeguru-reviewer:{リージョン}:{AWSアカウントID}:association:{関連付けID}

IAM権限

  • codeguru-reviewer:CreateCodeReview(※2)
  • codeguru-reviewer:DescribeCodeReview(※2)

(※2) 対象resource
resourceを絞ると正常に動かないようなのでresourceにはワイルドカード(*)を指定しています。
resourceを絞るとポリシー画面で警告が出て、実際に使ってみるとその旨のエラー(not authorized to perform: codeguru-reviewer:CreateCodeReview on resource: *)が出力されました。

An error occurred (AccessDeniedException) when calling the CreateCodeReview operation: User: arn:aws:sts::000000000000:assumed-role/xxxx/yyyy is not authorized to perform: codeguru-reviewer:CreateCodeReview on resource: *

実際のスクリプト

codeguru_review.sh

echo "Start CodeGuru review"

# create review
type_param=$(cat << EOS
{
  "RepositoryAnalysis": {
    "RepositoryHead": {"BranchName": "${REPOSITORY_BRANCH}"}
  },
  "AnalysisTypes": ["Security", "CodeQuality"]
}
EOS
)
review_arn=`aws codeguru-reviewer create-code-review \
  --name ${CODEBUILD_BUILD_ID//:/_} \
  --repository-association-arn $CODEGURU_REVIEWER_ASSOCIATION_ARN \
  --type "${type_param}" \
  --query "CodeReview.CodeReviewArn" --output text`
echo "Create review_arn=" $review_arn

# wait
echo "Wait..."
aws codeguru-reviewer wait code-review-completed --code-review-arn $review_arn

# count recommendations
recommendations_count=$(aws codeguru-reviewer describe-code-review --code-review-arn $review_arn \
    --query "CodeReview.Metrics.FindingsCount" --output text)
echo "recommendations count=" $recommendations_count
echo "Show Details https://${AWS_REGION}.console.aws.amazon.com/codeguru/reviewer/codereviews/details/${review_arn}" 

# assert
if [[ "$recommendations_count" = "0" ]]; then
    echo "OK: Has no recommendations"
    exit 0
else
    echo "NG: Has ${recommendations_count} recommendations"
    exit 1
fi

補足

解析対象

今回は「セキュリティ」と「コード品質」の両方の解析を有効にしています。

"AnalysisTypes": ["Security", "CodeQuality"]

CodeGuru Reviewerの除外設定は今のところファイル単位のみで、細かいチューニングがはできなさそうです。そのため場合によっては「セキュリティ」だけ有効にしてもいいかもしれません。

レビュー名

レビュー名はAWSアカウント内で一意の名前をつける必要があります。
今回はCodeBuildのビルドIDを使いました。コロン(:)が使えないのでアンダーバー(_)に変換しています。

--name ${CODEBUILD_BUILD_ID//:/_}

CodeGuruの詳細ページURL

結果取得時に、ついでにレビューの詳細ページURLを出力するようにしています。

echo "Show Details https://${AWS_REGION}.console.aws.amazon.com/codeguru/reviewer/codereviews/details/${review_arn}" 

パイプラインに組み込む

私が今回対象としたプロジェクトでの例です。
CodePipelineやCodeBuildの設定はCloudFormationで書いているので、そのままCloudFormationの対応差分で記載します。mm

必要な情報を環境変数として渡す

CodeDeployの環境変数に、CloudFormationパラメータでとっている「ブランチ名」「CodeGuruReviewerの関連付けARN」を渡す

EnvironmentVariables:
  - Name: CODEGURU_REVIEWER_ASSOCIATION_ARN
    Value: !Sub '${CodeGuruReviewerAssociationArn}'
  - Name: REPOSITORY_BRANCH
    Value: !Sub '${RepositoryBranch}'

IAM権限

CodeBuildにAssumeさせているRoleに以下のポリシーを追加

- PolicyName: CodeGuruReviewerPolicy
  PolicyDocument:
    Version: "2012-10-17"
    Statement:
      - Action:
        - codeguru-reviewer:CreateCodeReview
        - codeguru-reviewer:DescribeCodeReview
        Effect: Allow
        Resource: '*'

buildspecファイル

CodeBuildのbuildspecファイルのコマンドに今回用意したシェルスクリプトの呼び出しを追加します。

  build:
    commands:
      - sh codeguru_review.sh

実行

推奨事項あり

推奨事項が出る変更をリポジトリにpush。パイプラインが失敗します。

パイプラインから実行されたCodeGuruのレビュー結果画面

パイプライン結果

CodeBuildの該当箇所ログ

[Container] 2022/05/05 11:38:37 Running command sh codeguru_review.sh
Start CodeGuru review
Create review_arn= arn:aws:codeguru-reviewer:us-west-2:000000000000:association:ff000000-000000000-0000-000000000000:code-review:RepositoryAnalysis-xxxxxxx
Wait...
recommendations count= 1
Show Details https://us-west-2.console.aws.amazon.com/codeguru/reviewer/codereviews/details/arn:aws:codeguru-reviewer:us-west-2:000000000000:association:ff000000-000000000-0000-000000000000:code-review:RepositoryAnalysis-xxxxxxx
NG: Has 1 recommendations

[Container] 2022/05/05 11:43:15 Command did not exit successfully sh codeguru_review.sh exit status 1
[Container] 2022/05/05 11:43:15 Phase complete: BUILD State: FAILED
[Container] 2022/05/05 11:43:15 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: sh codeguru_review.sh. Reason: exit status 1

推奨事項なし

推奨事項に従ってコードを修正しpush。推奨事項が0になりパイプラインが進み、最後まで成功しました。

パイプラインから実行されたCodeGuruのレビュー結果画面

パイプライン結果

CodeBuildの該当箇所ログ

[Container] 2022/05/05 12:11:14 Running command sh core/codeguru_review.sh
Start CodeGuru review
Create review_arn= arn:aws:codeguru-reviewer:us-west-2:000000000000:association:ff000000-000000000-0000-000000000000:code-review:RepositoryAnalysis-xxxxxxx
Wait...
recommendations count= 0
Show Details https://us-west-2.console.aws.amazon.com/codeguru/reviewer/codereviews/details/arn:aws:codeguru-reviewer:us-west-2:000000000000:association:ff000000-000000000-0000-000000000000:code-review:RepositoryAnalysis-xxxxxxx
OK: Has no recommendations

終わりに

今回のCodeGuruによるコードレビューは実際のシステムのパイプラインに組み込み1ヶ月ほど運用しています。
しばらくパイプラインに組み込んで運用していますが、パイプラインの実行時間についてはそれほど伸びた感覚はありません。(また計測してみようと思います)

CodeGuru Reviwerはリポジトリと紐づけるだけで利用でき、課金も従量制であるため手軽です。
AWSのサービスはほとんどの操作をAPIやCLIからも行うことができ、今回のような組み込みもしやすい。
効率的にアプリケーションのライフサイクルを循環させて価値を届けるなかで、サービスやツールも活用し自動化できるものはそうしていきたいところです。

セキュリティは大事だと認識していますが、どうもスピードは落ちるようなイメージをもってしまうところもあります。
ただ本当に安定して素早く機能を届けるためにも重要であることを認識し、早い段階で検知できるよう費用対応固化を考えながらサイクルに取り込んでいこうと思います。

以上です、ではまた!

元記事はこちら

CodeGuruによるレビューをCI/CDパイプラインに組み込む CodePipeline+CodeBuild編
著者:@shu85t


アイレットなら、AWS で稼働するサーバーを対象とした監視・運用・保守における煩わしい作業をすべて一括して対応し、経験豊富なプロフェッショナルが最適なシステム環境を実現いたします。AWS プレミアコンサルティングパートナーであるアイレットに、ぜひお任せください。

AWS 運用・保守サービスページ

その他のサービスについてのお問合せ、お見積り依頼は下記フォームよりお気軽にご相談ください。
https://cloudpack.jp/contact/form/