はじめに
先日、AWS Dev Day 2023 Tokyoのセッション動画が公開されました。
8月末までの公開のため、少しずつ見ていっています。
その中の以下のセッションのスライドで、AWS SAMがテストできるとあり、興味がでたため少し触ってみました。
概要
-
Conftest
というツールを試用-
Open Policy Agent
のRego
というポリシー専用の言語で記述 - Yamlをチェックできる
- それを用いて、CFnをチェックする
-
参考
やってみた
作業用に、Cloud9(t2.micro)をつかっています。
インストール
以下のページのコマンドをそのまま行います。
LATEST_VERSION=$(wget -O - "https://api.github.com/repos/open-policy-agent/conftest/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/' | cut -c 2-)
wget "https://github.com/open-policy-agent/conftest/releases/download/v${LATEST_VERSION}/conftest_${LATEST_VERSION}_Linux_x86_64.tar.gz"
tar xzf conftest_${LATEST_VERSION}_Linux_x86_64.tar.gz
sudo mv conftest /usr/local/bin
conftest -v
サンプル実行
公式のリポジトリを丸ごと持ってきて、AWS SAMの例をやってみます。
git clone https://github.com/open-policy-agent/conftest.git
cd conftest/examples/awssam
ディレクトリ構成は以下になっています。policyディレクトリ以下の.rego
ファイルが定義ファイルの模様です。
$ pwd;find . | sort | sed '1d;s/^\.//;s/\/\([^/]*\)$/|--\1/;s/\/[^/|]*/| /g'
/home/ec2-user/environment/conftest/examples/awssam
|--lambda.yaml
|--policy
| |--policy.rego
それぞれの中身は以下のようになっています。
サンプルをそのまま実行すると、4件失敗しました。
$ conftest test lambda.yaml
FAIL - lambda.yaml - main - Sensitive data not allowed in environment variables
FAIL - lambda.yaml - main - excessive Action permissions not allowed
FAIL - lambda.yaml - main - excessive Resource permissions not allowed
FAIL - lambda.yaml - main - python2.7 runtime not allowed
7 tests, 3 passed, 0 warnings, 4 failures, 0 exceptions
ソースを修正して再実行してみます。
- Runtime: python2.7
+ Runtime: python3.7
失敗が3件に減りました。
$ conftest test lambda.yaml
FAIL - lambda.yaml - main - Sensitive data not allowed in environment variables
FAIL - lambda.yaml - main - excessive Action permissions not allowed
FAIL - lambda.yaml - main - excessive Resource permissions not allowed
7 tests, 4 passed, 0 warnings, 3 failures, 0 exceptions
セッションの例
最初に紹介したセッションの記述もやってみます。
新しくディレクトリとファイルを作成します。
# 一旦上まで戻る
cd ~/environment
mkdir testConftest && cd testConftest
mkdir policy
touch sampleCreateLambda.yaml
touch policy/lambdaPolicy.rego
sampleCreateLambda.yaml
に以下のサンプル(Yaml)を転記します。
.rego
ファイルは以下をそのまま転記します。
package main
import future.keywords
legacy_runtime = [
"python2.7",
"nodejs12.x",
"dotnetcore3.1"
]
deny[msg] {
some id, res in input.Resources
res.Type == "AWS::Lambda::Function"
runtime = res.Properties.Runtime
contains(runtime, legacy_runtime[_])
msg = sprintf("%s: %s runtime not allowed",[id , runtime])
}
実行して、失敗が出ませんでした。
$ conftest test sampleCreateLambda.yaml
1 test, 1 passed, 0 warnings, 0 failures, 0 exceptions
失敗になるように、Yamlを修正して実行します。
- Runtime: nodejs18.x
+ Runtime: nodejs12.x
$ conftest test sampleCreateLambda.yaml
FAIL - sampleCreateLambda.yaml - main - primer: nodejs12.x runtime not allowed
1 test, 0 passed, 0 warnings, 1 failure, 0 exceptions
IAMロールをテスト
IAMロールのテストも作ってみます。
touch sampleCreateRole.yaml
touch policy/rolePolicy.rego
sampleCreateRole.yaml
は以下を利用します。
アクションに*が使われているかのチェックを作ってみました。
package main
import future.keywords
# Actionに * が使われているか
deny[msg] {
# idに論理IDが格納される
some id, res in input.Resources
res.Type == "AWS::IAM::Role"
action = res.Properties.Policies[_].PolicyDocument.Statement[_].Action
is_string(action)
endswith(action, "*")
msg = sprintf("%s 内でアクションに * が使われています。",[id])
}
実行してちゃんとチェックできていることが確認できました。
ここでテストが2件実行されているのは、先に作成したpolicy/lambdaPolicy.rego
も使われているためです。
$ conftest test sampleCreateRole.yaml
FAIL - sampleCreateRole.yaml - main - RootRole 内でアクションに * が使われています。
2 tests, 1 passed, 0 warnings, 1 failure, 0 exceptions
リソースに * が使われているかのチェックも追加してみます。
package main
import future.keywords
# Actionに * が使われているか
deny[msg] {
# idに論理IDが格納される
some id, res in input.Resources
res.Type == "AWS::IAM::Role"
action = res.Properties.Policies[_].PolicyDocument.Statement[_].Action
is_string(action)
endswith(action, "*")
msg = sprintf("%s 内でアクションに * が使われています。",[id])
}
# Resourceに * が使われているか
deny[msg] {
some id, res in input.Resources
res.Type == "AWS::IAM::Role"
resource = res.Properties.Policies[_].PolicyDocument.Statement[_].Resource
is_string(resource)
endswith(resource, "*")
msg = sprintf("%s 内でリソースに * が使われています。",[id])
}
ちゃんとチェックできるようになりました。
$ conftest test sampleCreateRole.yaml
FAIL - sampleCreateRole.yaml - main - RootRole 内でアクションに * が使われています。
FAIL - sampleCreateRole.yaml - main - RootRole 内でリソースに * が使われています。
3 tests, 1 passed, 0 warnings, 2 failures, 0 exceptions
決められたタグの有無をチェック
domain
というタグがあるかのチェックを書いてみました。
package main
import future.keywords
# タグセクションが存在するか
deny[msg] {
some id, res in input.Resources
not res.Properties.Tags
msg = sprintf("%s 内には、Tagsが定義されていません。",[id])
}
# 特定のタグが存在するか
deny[msg] {
targetKey = "domain"
some id, res in input.Resources
hasTarget = [ target | target = res.Properties.Tags[_].Key ]
1 != count( [x | hasTarget[x] == targetKey ])
msg = sprintf("%s 内で、'%s'をキーとしたタグが定義されていません。",[id,targetKey])
}
先のLambdaのCFnテンプレートに実行すると、以下のようにエラーになりました。
$ conftest test sampleCreateLambda.yaml
FAIL - sampleCreateLambda.yaml - main - primer 内で、'domain'をキーとしたタグが定義されていません。
FAIL - sampleCreateLambda.yaml - main - primer 内には、Tagsが定義されていません。
5 tests, 3 passed, 0 warnings, 2 failures, 0 exceptions
CFnテンプレートに以下を加えてみます。
Tags:
- Key: domain
Value: train
- Key: customer
Value: jr
再度conftestを実行すると、うまくいきました。
$ conftest test sampleCreateLambda.yaml
5 tests, 5 passed, 0 warnings, 0 failures, 0 exceptions
別のリソースでタグが定義されていない場合も、チェックされます。
$ conftest test sampleCreateLambda.yaml
FAIL - sampleCreateLambda.yaml - main - primer2 内で、'domain'をキーとしたタグが定義されていません。
5 tests, 4 passed, 0 warnings, 1 failure, 0 exceptions
おわりに
今回はConftest
を使って、CFnテンプレートのテストを作ってみました。
うまく使うことで、過剰な権限の付与を防止したりできそうでした。
この記事がどなたかのお役に立てれば幸いです。