LoginSignup
2
0

More than 1 year has passed since last update.

CloudFormation Hookを試してみる

Last updated at Posted at 2022-03-10

CloudFormation Hookが利用可能になったので、試してみる。

やりたいこと

IaCに予防的統制を組み込みたい。
これまで、例えばS3で暗号化を強制したい場合、以下のような手段はあった。

  • SCPでS3暗号化設定の変更を禁止する。
  • Config RulesでS3暗号化が設定されていないバケットを検知する。
  • CFn GuardでS3暗号化設定が入っているかどうかを事前にチェックする。

いずれも有益な統制ではあるが、より直截的に「S3暗号化設定が入ってなければスタックの作成を禁止」したい場合、これらの方法では難しかった。
フックならそれが実現できそうだ。

準備

CFnテンプレートを二つ用意する。

一つ目は何も設定されていないテンプレート。

hook-test-ng.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: "Hook Test. Encryption settings is not present."
Resources:
  S3HookTestBucket:
    Type: "AWS::S3::Bucket"
    Properties:
      BucketName: !Sub "hooktest-ng-${AWS::AccountId}-${AWS::Region}-bucket"

二つ目は、暗号化が設定されているテンプレート。

hook-test-ok.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: "Hook Test. Encrypytion is enabled."
Resources:
  S3HookTestBucket:
    Type: "AWS::S3::Bucket"
    Properties:
      BucketName: !Sub "hooktest-ok-${AWS::AccountId}-${AWS::Region}-bucket"
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: 'aws:kms'
              KMSMasterKeyID: '12345678-abcd-efgh-1234-1234567890ab'
            BucketKeyEnabled: true

ステップバイステップ

1. サンプルHookを検索する

フックはCloudFormation→レジストリ→パブリック拡張機能から検索できる。
AWSSamplesという名前でいくつか登録されているので、ここから検索する。以下二点に注意。

  • パブリッシャーはAWSではなくサードパーティー。
  • 前方一致。AWSSamples::S3はマッチするが、S3はマッチしない。
    スクリーンショット 2022-03-10 午前11.54.51.png
    今回は上から二つ目のAWSSamples::S3BucketEncrypt::Hookを有効化する。

2. サンプルHookを有効化する

フックを選択すると詳細が表示されるので、右上の「アクティブ化」ボタンをクリックする。
スクリーンショット 2022-03-10 午後8.18.02.png
アクティブ化の詳細設定画面に遷移するが、どうやらフックを登録するためのIAMロールのARNが必要らしい。
スクリーンショット 2022-03-10 午後2.19.10.png

3. IAMロールを準備する

マニュアルを参考に、信頼関係を以下の通り設定したロールを作成する(ちなみにresources.cloudformation.amazonaws.comはフックではなく拡張リソースタイプを登録するための設定)。
ロール名はCFnHookTestRoleとし、権限そのものはいったん'AdministratorAccess'とした。

CFnHookTestRole(信頼関係)
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": [
                    "hooks.cloudformation.amazonaws.com",
                    "resources.cloudformation.amazonaws.com"
                ]
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "StringEquals": {
                    "aws:SourceAccount": "123456789012"
                }
            }
        }
    ]
}

4. アクティベート

IAMロールとログ設定を入れ、他の設定はブランクのまま、「拡張機能のアクティブ化」をクリックする。
スクリーンショット 2022-03-10 午後8.20.14.png

エイリアスと設定JSONの設定入力画面が出るので、それぞれ入力する。
エイリアスはリージョンごと・アカウントごとに一意なものを設定する必要がある模様。
別リージョン・別アカウントならかぶってもよいということになるが、マニュアルの記載を見る限り、なるべく一意にしておくことが望ましいようだ。ただ、その割にハイフンやアンスコが使えないのは不便としか言えない。
...と、色々調べてはみたものの、試した限りでは、このフックはdefaultというエイリアスしか受け付けないらしい。何なんだ。。。

Invalid configuration alias S3BucketEncrypt12345678901tokyo for HOOK type AWSSamples::S3BucketEncrypt::Hook. Only 'default' alias is supported for HOOK types

仕方ないのでエイリアスをdefaultとして作成する。
スクリーンショット 2022-03-10 午後8.35.24.png

設定JSON(HookConfiguration)は以下のような感じになる。詳細はこちらに記載がある。

設定JSON
{
  "CloudFormationConfiguration": {
    "HookConfiguration": {
      "TargetStacks": "ALL",
      "FailureMode": "FAIL",
      "Properties": {
        "minBuckets":"1",
        "encryptionAlgorithm": "aws:kms"
      }
    }
  }
}
項目 内容 取りうる値
TargetStacks 対象スタックぽく見えるが、実質、有効/無効の設定。 ALL, NONE
FailureMode フックのルールに違反した際の挙動。FAILにするとスタック作成が中止される。WARNだと警告止まり。 FAIL, WARN
Properties 各フックの設定スキーマごとに定義された独自設定。 フックによる

S3暗号化のフックの場合、PropertiesはminBuckets(準拠している最小バケット数。これは意図がよくわからない)とencryptionAlgorithm(暗号化アルゴリズム)の二つ。
これはフックの設定スキーマに定義されているので、少し長いが引用しておく。

S3暗号化フックの設定スキーマ
{
    "typeName": "AWSSamples::S3BucketEncrypt::Hook",
    "description": "Example resource hook that check Amazon S3 bucket encryption properties",
    "sourceUrl": "https://github.com/aws-cloudformation/aws-cloudformation-samples/tree/main/hooks/python-hooks/s3-bucket-encryption",
    "documentationUrl": "https://github.com/aws-cloudformation/aws-cloudformation-samples/blob/main/hooks/python-hooks/s3-bucket-encryption/README.md",
    "typeConfiguration": {
        "properties": {
            "minBuckets": {
                "description": "Minimum number of compliant buckets",
                "type": "string"
                
            },
            "encryptionAlgorithm": {
                "description": "Encryption algorithm for SSE",
                "default": "AES256",
                "type": "string"
            }
        },
        "required": [],
        "additionalProperties": false
    },
    "required": [],
    "handlers": {
        "preCreate": {
            "targetNames": [
                "AWS::S3::Bucket"
            ],
            "permissions": []
        },
        "preUpdate": {
            "targetNames": [
                "AWS::S3::Bucket"
            ],
            "permissions": []
        }
    },
    "additionalProperties": false
}

5. 設定確認

アクティブ化が完了したら、レジストリ→アクティブ化済みの拡張機能から設定を確認してみる。
デフォルトでは一つも出てこないのでアレ?となるが、これはフィルターが「非公開登録」になっているためで、「アクティブ化済みのサードパーティー」に変更すると表示される。
スクリーンショット 2022-03-10 午後8.36.45.png

アクティブ化され、失敗モード(FailureMode)がちゃんと「失敗(Fail)」になっていることがわかる。

6. 挙動確認

用意したCFnテンプレートを使ってテストする。

まずは、暗号化設定のないhooktest-ng.ymlでスタックを作成してみる。
スクリーンショット 2022-03-10 午後8.38.41.png
S3暗号化のフックで評価に失敗した旨を表示して、CREATE_FAILEDとなった。
CloudWatch Logs(log-group-hook-test)を見ると、以下のように詳細が記録されている。
スクリーンショット 2022-03-10 午後8.46.38.png

次に暗号化を有効化したhook-test-ok.ymlでテスト。
スクリーンショット 2022-03-10 午後9.23.27.png
無事成功した。
CloudWatch Logsの記録は以下の通り。
スクリーンショット 2022-03-10 午後9.29.26.png
余談だが、AES256ではBucket key not enabled for bucketとのエラーが出て上手くいかなかった。
サンプルフックのコードに何か問題があるのかも知れないが、本題から逸れるのでここではスルー。

まとめ

フックによって、従来より強い水準での予防的統制が実現出来るようになった。
ただ、他の大多数のセキュリティ施策と同じく、やり過ぎれば開発自由度を阻害し、運用を煩雑化することに繋がりかねない点は注意が必要かも知れない。
諸刃の剣ではあるが、うまく使えばIaCにもいい感じのガードレール(自動ブレーキと言った方が近いかも)を敷くことができそうな気はする。

おまけ

フックの開発について詳細はこちら
こっちもいずれトライしてみたい。

2
0
1

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