CloudFormation Guardを利用してVPCに関するConfigのカスタムルールを色々試してみた
AWS CloudFormation Guard を利用して、AWS Config のカスタムルールを作成できるようになりました。今後のカスタムルール作成は CloudFormation Guard を利用してみたいと思ったので、習熟のために VPC に関するいくつかのカスタムルールを作ってみました。CloudFormation Guard でカスタムルールを作成するために学習したドキュメントも合わせて紹介します。
試してみた
今回は、VPC(AWS::EC2::VPC)に関するカスタムルールを Guard で作成してみました。
事前学習
始めに AWS のブログを確認して、とりあえず動くカスタムルールを作成してイメージを掴みました。
その後、GitHub 上のドキュメントで CloudFormation Guard の文法を学習しました。
cloudformation-guard/docs at main · aws-cloudformation/cloudformation-guard
後で気づいたのですが、AWS のユーザーガイドにも Guard の文法を記載したページがありましたので、こちらを参照でも良いかもしれません。
Getting started with AWS CloudFormation Guard - AWS CloudFormation Guard
GitHub 上にはサンプルもあります。サンプルは CloudFormation のチェック向けと思われますが、文法は同じなので Config のカスタムルールの参考になります。
cloudformation-guard/guard-examples at main · aws-cloudformation/cloudformation-guard
Config が保持するデータの構造も知っておく必要があるため、確認しました。
マネジメントコンソールから確認する場合は、Config サービスの「リソース」から VPC のリソース ID で検索することで確認できます。
「設定項目(JSON)の表示」から確認します。
AWS CLI で取得する場合は次のブログが参考になります。
VPC の設定項目例です。サブネットを作成していない VPC となります。また、後述において設定項目を記載している箇所がありますが、この例とは異なる項目例を記載している場合がありますので、ご注意願います。
VPCの設定項目例(折りたたんでいます)
{ "version": "1.3", "accountId": "111122223333", "configurationItemCaptureTime": "2022-08-03T15:25:01.025Z", "configurationItemStatus": "OK", "configurationStateId": "1659540301025", "configurationItemMD5Hash": "", "arn": "arn:aws:ec2:ap-northeast-1:111122223333:vpc/vpc-0636fc95b3cf20d33", "resourceType": "AWS::EC2::VPC", "resourceId": "vpc-0636fc95b3cf20d33", "awsRegion": "ap-northeast-1", "availabilityZone": "Multiple Availability Zones", "tags": { "Name": "test3-vpc" }, "relatedEvents": [], "relationships": [ { "resourceType": "AWS::EC2::SecurityGroup", "resourceId": "sg-06b039c10a3838d4e", "relationshipName": "Contains SecurityGroup" }, { "resourceType": "AWS::EC2::RouteTable", "resourceId": "rtb-017783b6ec2783d70", "relationshipName": "Contains RouteTable" }, { "resourceType": "AWS::EC2::NetworkAcl", "resourceId": "acl-095afdcd82caff293", "relationshipName": "Contains NetworkAcl" } ], "configuration": { "cidrBlock": "172.16.0.0/24", "dhcpOptionsId": "dopt-0024732f85497bcd2", "state": "available", "vpcId": "vpc-0636fc95b3cf20d33", "ownerId": "111122223333", "instanceTenancy": "default", "ipv6CidrBlockAssociationSet": [ { "associationId": "vpc-cidr-assoc-092cd3f6cc6250add", "ipv6CidrBlock": "2001:db8:1:1::/56", "ipv6CidrBlockState": { "state": "associated" }, "networkBorderGroup": "ap-northeast-1", "ipv6Pool": "Amazon" } ], "cidrBlockAssociationSet": [ { "associationId": "vpc-cidr-assoc-0ee4a773782a3f3fd", "cidrBlock": "172.16.0.0/24", "cidrBlockState": { "state": "associated" } }, { "associationId": "vpc-cidr-assoc-0dd35303baf668482", "cidrBlock": "172.16.1.0/24", "cidrBlockState": { "state": "associated" } } ], "isDefault": false, "tags": [ { "key": "Name", "value": "test3-vpc" } ] }, "supplementaryConfiguration": {}, "resourceTransitionStatus": "None" }
次の節以降では、需要があるかは別にして、練習として VPC のカスタムルールを作成した例を紹介していきます。
指定した DHCP オプションセットが設定されているか
let dhcp_option_set_id = "dopt-0024732f85497bcd2" rule vpc_dhcp_option_check { configuration.dhcpOptionsId == %dhcp_option_set_id }
dhcp_option_set_id
で指定した DHCP オプションセットが設定されていない場合は非準拠- トリガー設定
- 変更範囲 : リソース
- リソースカテゴリ : AWS リソース
- リソースタイプ : AWS EC2 VPC
- 変更範囲 : リソース
configuration
パラメータ内のdhcpOptionsId
と比較しています。
(略) "configuration": { "cidrBlock": "172.16.0.0/24", "dhcpOptionsId": "dopt-0024732f85497bcd2", "state": "available", "vpcId": "vpc-0636fc95b3cf20d33", "ownerId": "111122223333", "instanceTenancy": "default", (略)
DHCP オプションセットのリソース ID をパラメータとして指定することもできます。DHCPOptionSetId
をパラメータのキーとした場合の例です。
rule vpc_dhcp_option_check { configuration.dhcpOptionsId == CONFIG_RULE_PARAMETERS.DHCPOptionSetId }
パラメータ設定は次の画像のようになります。
CIDR がプライベート IPv4 アドレスの範囲であるか
let ipv4_cidr_block = configuration.cidrBlock let ipv4_cidr_block_association_set = configuration.cidrBlockAssociationSet[*] rule vpc_ipv4_cidr_block { %ipv4_cidr_block == /(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)/ } rule vpc_ipv4_cidr_block_association_set { %ipv4_cidr_block_association_set.cidrBlock == /(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)/ }
- CIDR がプライベート IPv4 アドレスの範囲でない場合は非準拠
- トリガー設定
- 変更範囲 : リソース
- リソースカテゴリ : AWS リソース
- リソースタイプ : AWS EC2 VPC
- 変更範囲 : リソース
プライベート IPv4 アドレスかどうかは CIDR の前方一致で確認しています(他にもっと良い方法があるかも)。
確認するパラメータはcidrBlock
とcidrBlockAssociationSet
内のcidrBlock
の両方にしています。VPC に追加の CIDR を設定している場合はcidrBlockAssociationSet
に追加されるため、両方を確認する内容にしました。しかしながら、VPC 作成時に指定する CIDR もcidrBlockAssociationSet
に含まれているため、cidrBlockAssociationSet
内のcidrBlock
のチェックだけでも問題ないかもしれません。
(略) "configuration": { "cidrBlock": "172.16.0.0/24", "dhcpOptionsId": "dopt-0024732f85497bcd2", "state": "available", "vpcId": "vpc-0636fc95b3cf20d33", "ownerId": "111122223333", "instanceTenancy": "default", "ipv6CidrBlockAssociationSet": [ { "associationId": "vpc-cidr-assoc-092cd3f6cc6250add", "ipv6CidrBlock": "2001:db8:1:1::/56", "ipv6CidrBlockState": { "state": "associated" }, "networkBorderGroup": "ap-northeast-1", "ipv6Pool": "Amazon" } ], "cidrBlockAssociationSet": [ { "associationId": "vpc-cidr-assoc-0ee4a773782a3f3fd", "cidrBlock": "172.16.0.0/24", "cidrBlockState": { "state": "associated" } }, { "associationId": "vpc-cidr-assoc-0dd35303baf668482", "cidrBlock": "172.16.1.0/24", "cidrBlockState": { "state": "associated" } } ], "isDefault": false, "tags": [ { "key": "Name", "value": "test3-vpc" } ] }, (略)
IPv6 アドレスを設定していないか
rule vpc_ipv6_not_enabled { configuration.ipv6CidrBlockAssociationSet empty }
- IPv6 を設定している場合は非準拠
- トリガー設定
- 変更範囲 : リソース
- リソースカテゴリ : AWS リソース
- リソースタイプ : AWS EC2 VPC
- 変更範囲 : リソース
IPv6 アドレスを設定している場合は、ipv6CidrBlockAssociationSet
に値が入るため、ipv6CidrBlockAssociationSet
が空の場合に準拠、空ではない場合に非準拠としています。次の例では、IPv6 アドレスが存在するため非準拠となります。
(略) "configuration": { "cidrBlock": "172.16.0.0/24", "dhcpOptionsId": "dopt-0024732f85497bcd2", "state": "available", "vpcId": "vpc-0636fc95b3cf20d33", "ownerId": "111122223333", "instanceTenancy": "default", "ipv6CidrBlockAssociationSet": [ { "associationId": "vpc-cidr-assoc-092cd3f6cc6250add", "ipv6CidrBlock": "2001:db8:1:1::/56", "ipv6CidrBlockState": { "state": "associated" }, "networkBorderGroup": "ap-northeast-1", "ipv6Pool": "Amazon" } ], (略)
IPv6 アドレスを設定しているか
rule vpc_ipv6_enabled { configuration.ipv6CidrBlockAssociationSet !empty }
- IPv6 を設定していない場合は非準拠
- トリガー設定
- 変更範囲 : リソース
- リソースカテゴリ : AWS リソース
- リソースタイプ : AWS EC2 VPC
- 変更範囲 : リソース
IPv6 アドレスを設定している場合は、ipv6CidrBlockAssociationSet
に値が入るため、ipv6CidrBlockAssociationSet
が空ではない場合に準拠、空の場合に非準拠としています。次の例では、IPv6 アドレスが存在していないため非準拠となります。
(略) "configuration": { "cidrBlock": "10.0.0.0/16", "dhcpOptionsId": "dopt-0ae97f3769798f9ff", "state": "available", "vpcId": "vpc-057355bebd0cc89b2", "ownerId": "111122223333", "instanceTenancy": "default", "ipv6CidrBlockAssociationSet": [], (略)
一つ前の節の「IPv6 アドレスを設定していないか」の逆パターンなのですが、作成時にはどちらか準拠判定となるのか混乱しました。慣れが必要そうです。
Internet Gateway が関連付けされていないか
let vpc_relationships = relationships[*] rule vpc_without_igw { %vpc_relationships.resourceType != "AWS::EC2::InternetGateway" }
- Internet Gateway が関連付けされている場合は非準拠
- トリガー設定
- 変更範囲 : リソース
- リソースカテゴリ : AWS リソース
- リソースタイプ : AWS EC2 VPC
- 変更範囲 : リソース
Internet Gateway が関連付けされている場合は、relationships
に Internet Gateway のリソースが存在するため、relationships
内にresourceType
がAWS::EC2::InternetGateway
であるリソースが存在していないことを確認しています。
(略) "relationships": [ { "resourceType": "AWS::EC2::InternetGateway", "resourceId": "igw-0f8ac80ef1c0f31a2", "relationshipName": "Is attached to InternetGateway" }, { "resourceType": "AWS::EC2::Subnet", "resourceId": "subnet-0e8a21edbfaed39ae", "relationshipName": "Contains Subnet" }, (略)
デフォルト VPC が存在しないか
2022.10.4 修正
デフォルト VPC の判定に vpcId の文字長を利用することが誤っていたため修正いたしました。
rule no_default_vpc { configuration.isDefault == false }
- デフォルト VPC が存在している場合は非準拠
- トリガー設定
- 変更範囲 : リソース
- リソースカテゴリ : AWS リソース
- リソースタイプ : AWS EC2 VPC
- 変更範囲 : リソース
isDefault
パラメータを利用してデフォルト VPC であるかどうかを確認しています。
(略) "configuration": { "cidrBlock": "172.16.0.0/24", "dhcpOptionsId": "dopt-0024732f85497bcd2", "state": "available", "vpcId": "vpc-0636fc95b3cf20d33", "ownerId": "111122223333", "instanceTenancy": "default", "ipv6CidrBlockAssociationSet": [ { "associationId": "vpc-cidr-assoc-092cd3f6cc6250add", "ipv6CidrBlock": "2001:db8:1:1::/56", "ipv6CidrBlockState": { "state": "associated" }, "networkBorderGroup": "ap-northeast-1", "ipv6Pool": "Amazon" } ], "cidrBlockAssociationSet": [ { "associationId": "vpc-cidr-assoc-0ee4a773782a3f3fd", "cidrBlock": "172.16.0.0/24", "cidrBlockState": { "state": "associated" } }, { "associationId": "vpc-cidr-assoc-0dd35303baf668482", "cidrBlock": "172.16.1.0/24", "cidrBlockState": { "state": "associated" } } ], "isDefault": false, (略)
さいごに
CloudFormation Guard を習熟したく、VPC に関する Config のカスタムルールを作成してみました。ルールを書いている途中で、どちらが準拠でどちらが非準拠になるか混乱することが多かったです。同じ内容のカスタムルールでも複数の書き方があるので、どのような書き方が良いのかは今後も模索していきたいと思いました。
このブログがどなたかのご参考になれば幸いです。