1
0

【AWS】CloudFormation Guardでよく使う評価ルールとハマりポイント

Posted at

はじめに

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作成に悩まれている方の助けになれば幸いです。

参考元

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