LoginSignup
1
0

ConftestでCFnをテストする

Posted at

はじめに

先日、AWS Dev Day 2023 Tokyoのセッション動画が公開されました。

8月末までの公開のため、少しずつ見ていっています。
その中の以下のセッションのスライドで、AWS SAMがテストできるとあり、興味がでたため少し触ってみました。

概要

  • Conftestというツールを試用
    • Open Policy AgentRegoというポリシー専用の言語で記述
    • 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

ソースを修正して再実行してみます。

lambda.yaml
-      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ファイルは以下をそのまま転記します。

policy/lambdaPolicy.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を修正して実行します。

sampleCreateLambda.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は以下を利用します。

アクションに*が使われているかのチェックを作ってみました。

policy/rolePolicy.rego
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

リソースに * が使われているかのチェックも追加してみます。

policy/rolePolicy.rego
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というタグがあるかのチェックを書いてみました。

policy/tagPolicy.rego
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テンプレートのテストを作ってみました。
うまく使うことで、過剰な権限の付与を防止したりできそうでした。
この記事がどなたかのお役に立てれば幸いです。

1
0
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
1
0