はじめに
CloudFormation(以下CFn)使用時にCloudFormation Guardを使用していますか?
CFnのPolicy as Codeといえばこのツールと認知されている、
CFnにおけるガードレールの役割を担うツールがCloudFormation Guardです。
本記事は、CloudFormation Guardを利用したPJにおいて、
繰り返し利用したルールセットと一部ハマりポイントを記載した記事となります。
※CloudFormation Guardに関する説明・使い方他のブログで紹介されているので、
本記事の記載範囲外となります。
CloudFormation Guardとは・使い方
以下ブログにわかりやすくまとめられているため割愛します。
紹介評価ルールの構造
基本的に上記まとめブログに記載されていますが、
本記事におけるルールの構造を記載します。
例)KMSの暗号化確認
let aws_kms_key_resources = Resources.*[ Type == 'AWS::KMS::Key' ]
rule aws_kms_key when %aws_kms_key_resources !empty {
%aws_kms_key_resources.Properties.EnableKeyRotation == true
}
let aws_kms_key_resources
:
対象となるリソース情報を格納するための変数作成
Resources.*[ Type == 'AWS::KMS::Key' ]
:
Cfnテンプレート内記載のType「AWS::KMS::Key」を持つ全てのResource句を範囲指定し、本範囲を変数「aws_kms_key_resources」に代入
※テンプレート内一意である論理IDを対象にすることも可能
rule aws_kms_key
:
ルールを格納するためのルールセット変数作成
when %aws_kms_key_resources !empty
:
これから記載するルール適用条件を記載。本記載は変数「aws_kms_key_resources」がテンプレートに存在する場合ルールが適用される
%aws_kms_key_resources.Properties.EnableKeyRotation == true
:
ルール内容。変数「aws_kms_key_resources」内のProperies句内の「EnableKeyRotation」がtrueであることの確認
よく使う評価ルール
ここから複数システムにて共通的に使用したルールを記載します。
ProjectタグとNameタグが付いているのか
この評価ルールではProject/Nameタグの付与確認とProjectタグ内容の確認になります。
Nameタグは各論理IDごとに設定しているため、汎用性を優先してタグ付与のみの確認としています。
※網羅的に確認する場合は論理ID単位でルールを作成する必要有
■タグ評価ルールファイル
let aws_s3_resources = Resources.*[ Type == 'AWS::S3::Bucket' ]
rule aws_s3 when %aws_s3_resources !empty {
%aws_s3_resources.Properties {
# CostTag check
some Tags[*] {
Key == "Project"
Value == "test"
}
some Tags[*] {
Key == "Name"
}
}
}
ハマりポイント
Key/Valueの組み合わせがバラバラでもルールに準拠していればOKとなってしまう。
Cfnテンプレート内におけるKey/Value形式のようにセットで評価するようにしたい。
対応方法
some
をProperties要素の前に付与することで、
Key/Valueの組み合わせをブロックとして評価ルールになる。
※以下ルールはこのKey/Valueの組み合わせでないとエラーにできる。
some Tags[*] {
Key == "Project"
Value == "test"
}
Projectタグが付いているのか(Prameter句参照形式)
タグのValueをPrameter句に定義している場合以下記載のような記載となる。
※Prameter句の内容までGuardツールで検出できないため、
Prameter句の正当性はレビューやデプロイ後のAWSConfig等で検知する必要あり。
■タグ評価ルールファイル(Prameter句参照形式)
let aws_s3_resources = Resources.*[ Type == 'AWS::S3::Bucket' ]
rule aws_s3 when %aws_s3_resources !empty {
%aws_s3_resources.Properties {
# CostTag check
some Tags[*] {
Key == "Project"
Value == {"Fn::Sub":"${SystemName}-${Enviroment}"}
}
}
}
ハマりポイント
評価ルール内では短縮形の組み込み関数(!Ref)が利用できない。
また、組み込み関数を利用して設定される値を評価してくれないため、
組み込み関数の記述が行えているかの確認しかできない。
対応方法
CFnテンプレートファイル内で短縮形の組み込み関数を利用している場合は、
評価ルールの中で、完全関数名に戻してあげる必要がある。
以下のようなテンプレートファイルに対しては、
Value == {"Fn::Sub":"${SystemName}-${Enviroment}"}
のような形のルールを作成
some Tags[*] {
Key == "Project"
Value == {"Fn::Sub":"${SystemName}-${Enviroment}"}
}
■評価対象テンプレートファイル
S3:
Type: AWS::S3::Bucket
Properties:
Tags:
- Key: Project
Value: !Sub "${SystemName}-${Enviroment}"
S3バケットの暗号化が有効になっているのか
S3バケットの暗号化が有効になっていることを確認する評価ルール
■S3バケットの暗号化確認評価ルールファイル
let aws_s3_resources = Resources.*[ Type == 'AWS::S3::Bucket' ]
rule aws_s3 when %aws_s3_resources !empty {
%aws_s3_resources.Properties {
BucketEncryption.ServerSideEncryptionConfiguration[*] {
BucketKeyEnabled == true
}
}
}
CFnデフォルト値になっているのか
CFnデフォルト値を正として評価したい場合は「要素に値が設定されていないこと」を評価する!exist
を使用する
■CFnデフォルト値を正とする確認評価ルールファイル
let aws_s3_resources = Resources.*[ Type == 'AWS::S3::Bucket' ]
rule aws_s3 when %aws_s3_resources !empty {
%aws_s3_resources.Properties {
BucketEncryption.ServerSideEncryptionConfiguration[*] {
BucketKeyEnabled !exists
}
}
}
配列内の値がCFnデフォルト値になっているのか
CFnデフォルト値を正として評価したい場合は「要素に値が設定されていないこと」と「その配列を定義する要素」が存在しないことを確認する必要がある。
例えば、SecurityHub 基礎セキュリティ[ECS.8]を題材として、
'AWS::ECS::TaskDefinition'に環境変数「AWS_ACCESS_KEY_ID」を渡していないことを確認します。
※ECSにおけるAWS_ACCESS_KEY_IDの設定は、
以下テンプレート内のEnvironmentにて設定します。
■評価対象テンプレートファイル
AWSTemplateFormatVersion: 2010-09-09
Description: Create a task definition for a web server.
Resources:
ECSTaskDefinition:
Type: 'AWS::ECS::TaskDefinition'
Properties:
ContainerDefinitions:
- Name: first-run-task
Image: 'httpd:2.4'
Essential: true
PortMappings:
- ContainerPort: 80
Protocol: tcp
Environment:
- Name: AWS_ACCESS_KEY_ID
Value: 'XXXXXXXXXXXXXXXX'
■配列内の値をCFnデフォルト値を正とする評価ルールファイル
let aws_ecs_taskdefinition_resources = Resources.*[ Type == 'AWS::ECS::TaskDefinition' ]
rule aws_ecs_taskdefinition when %aws_ecs_taskdefinition_resources !empty {
%aws_ecs_taskdefinition_resources.Properties {
# [ECS-2-8] Cloud inspection check sheet
some ContainerDefinitions[*] {
Environment[*].Name != "AWS_ACCESS_KEY_ID" or
Environment[*].Name !exists
}
}
}
終わりに
テンプレートを用いてデプロイ後のリソースではなく、
テンプレートファイルの記述を評価するため組み込み関数が使いにくいなど、
惜しいところがあるツールだと感じました。
また、評価ルールの作成も独自の記述方法があるため、慣れるまで時間がかかりました。
組織やPJの規約に当てはまるルールを作成することができれば、
規約違反による手戻りを防げる協力なツールであると思います。
本記事がCloudForamtionGuard作成に悩まれている方の助けになれば幸いです。
参考元