[アップデート] AWS CDKで合成時にAWS CloudFormation Guardによるコンプライアンスチェックができるようになりました (ベータ版)
AWS CDKでもAWS CloudFormation Guardを使いたいな
こんにちは、のんピ(@non____97)です。
皆さんはAWS CDKでもAWS CloudFormation Guard(以降CFn Guard)を使いたいなと思ったことはありますか? 私はあります。
AWS CDKで作成されるリソースのコンプライアンスチェックと言えばcdk-nagもありますが、既にCloudFormationで構築している場合は、CFn Guardのルールを使いまわしたい場面もあると思います。
CFn Guardの説明は以下記事をご覧ください。
今回、ベータ版ですがAWS CDKで合成時にCFn Guardによるコンプライアンスチェックができるようになりました。
GitHubを確認するとCDK CFN Guard Validator Pluginをインストールして使うようですね。
試したみたので紹介します。
いきなりまとめ
- AWS CDKで合成時にコンプライアンスチェックができる
- 2023/4/28時点ではベータ版
- AWS CDKのApps単位で設定
- デフォルトではControl Towerのプロアクティブコントロールの一部がバンドルされている
- ルール単位で無効化することが可能
- 個別にリソース単位で抑制することはできなさそう
- 独自ルールを定義することも可能
やってみた
インストール
CDK CFN Guard Validator PluginのREADMEを確認しながら動作確認してみます。
まず、CDK CFN Guard Validator Pluginをインストールします。簡単ですね。
$ npm i -D @cdklabs/cdk-validator-cfnguard up to date, audited 346 packages in 471ms 33 packages are looking for funding run `npm fund` for details found 0 vulnerabilities
package.json
は以下の通りです。
{ "name": "cdk", "version": "0.1.0", "bin": { "cdk": "bin/cdk.js" }, "scripts": { "build": "tsc", "watch": "tsc -w", "test": "jest", "cdk": "cdk" }, "devDependencies": { "@cdklabs/cdk-validator-cfnguard": "^0.0.24", "@types/jest": "^29.4.0", "@types/node": "18.14.6", "aws-cdk": "2.76.0", "jest": "^29.5.0", "ts-jest": "^29.0.5", "ts-node": "^10.9.1", "typescript": "~4.9.5" }, "dependencies": { "aws-cdk-lib": "2.76.0", "constructs": "^10.0.0", "source-map-support": "^0.5.21" } }
ルールにマッチした場合の挙動の確認
インストール後、CDK CFN Guard Validator Pluginを使いたいAppsのプロパティpolicyValidationBeta1
にnew CfnGuardValidator()
を指定します。
#!/usr/bin/env node import "source-map-support/register"; import * as cdk from "aws-cdk-lib"; import { CdkStack } from "../lib/cdk-stack"; import { CfnGuardValidator } from "@cdklabs/cdk-validator-cfnguard"; const app = new cdk.App({ policyValidationBeta1: [new CfnGuardValidator()], }); new CdkStack(app, "CdkStack");
AWS CDKのAPI Referenceを確認するとIPolicyValidationPluginBeta1
というインターフェイスが追加されていました。併せてチェックしておくと良いでしょう。
デフォルトでバンドルされているルールはControl Towerのプロアクティブコントロールの一部です。どんなルールがあるかはnode_modules
配下から確認できます。
$ ls -R node_modules/@cdklabs/cdk-validator-cfnguard/rules/control-tower/cfn-guard/ ec2/ identityandaccessmanagement/ rds/ s3/ node_modules/@cdklabs/cdk-validator-cfnguard/rules/control-tower/cfn-guard//ec2: ct-ec2-pr-1.guard ct-ec2-pr-11.guard ct-ec2-pr-2.guard ct-ec2-pr-4.guard ct-ec2-pr-6.guard ct-ec2-pr-8.guard ct-ec2-pr-10.guard ct-ec2-pr-12.guard ct-ec2-pr-3.guard ct-ec2-pr-5.guard ct-ec2-pr-7.guard ct-ec2-pr-9.guard node_modules/@cdklabs/cdk-validator-cfnguard/rules/control-tower/cfn-guard//identityandaccessmanagement: ct-iam-pr-2.guard node_modules/@cdklabs/cdk-validator-cfnguard/rules/control-tower/cfn-guard//rds: ct-rds-pr-1.guard ct-rds-pr-12.guard ct-rds-pr-15.guard ct-rds-pr-18.guard ct-rds-pr-20.guard ct-rds-pr-23.guard ct-rds-pr-4.guard ct-rds-pr-7.guard ct-rds-pr-10.guard ct-rds-pr-13.guard ct-rds-pr-16.guard ct-rds-pr-19.guard ct-rds-pr-21.guard ct-rds-pr-24.guard ct-rds-pr-5.guard ct-rds-pr-8.guard ct-rds-pr-11.guard ct-rds-pr-14.guard ct-rds-pr-17.guard ct-rds-pr-2.guard ct-rds-pr-22.guard ct-rds-pr-3.guard ct-rds-pr-6.guard ct-rds-pr-9.guard node_modules/@cdklabs/cdk-validator-cfnguard/rules/control-tower/cfn-guard//s3: ct-s3-pr-1.guard ct-s3-pr-2.guard ct-s3-pr-3.guard ct-s3-pr-4.guard ct-s3-pr-5.guard ct-s3-pr-6.guard ct-s3-pr-7.guard ct-s3-pr-8.guard
今回はCT.IAM.PR.2
にマッチするようなIAMポリシーを持つIAMロールを定義します。
import * as cdk from "aws-cdk-lib"; import { Construct } from "constructs"; export class CdkStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const role = new cdk.aws_iam.Role(this, "Iam Role", { assumedBy: new cdk.aws_iam.ServicePrincipal("ec2.amazonaws.com"), managedPolicies: [ new cdk.aws_iam.ManagedPolicy(this, "Iam Policy", { statements: [ new cdk.aws_iam.PolicyStatement({ effect: cdk.aws_iam.Effect.ALLOW, actions: ["*"], resources: ["*"], }), ], }), ], }); } }
この状態でcdk synth
をすると以下のように怒られました。
$ npx cdk synth --no-version-reporting --no-path-metadata Performing Policy Validations Validation Report ----------------- ╔════════════════════════════════════╗ ║ Plugin Report ║ ║ Plugin: cdk-validator-cfnguard ║ ║ Version: N/A ║ ║ Status: failure ║ ╚════════════════════════════════════╝ (Violations) iam_managed_policy_no_statements_with_admin_access_check (1 occurrences) Occurrences: - Construct Path: CdkStack/Iam Policy/Resource - Template Path: cdk.out/CdkStack.template.json - Creation Stack: └── CdkStack (CdkStack) │ Construct: aws-cdk-lib.Stack │ Library Version: 2.76.0 │ Location: Run with '--debug' to include location info └── Iam Policy (CdkStack/Iam Policy) │ Construct: aws-cdk-lib.aws_iam.ManagedPolicy │ Library Version: 2.76.0 │ Location: Run with '--debug' to include location info └── ImportedIam Policy (CdkStack/Iam Policy/ImportedIam Policy) │ Construct: aws-cdk-lib.Resource │ Library Version: 2.76.0 │ Location: Run with '--debug' to include location info - Resource ID: IamPolicy22E02181 - Template Locations: > /Resources/IamPolicy22E02181/Properties/PolicyDocument/Statement/0/Resource Description: [CT.IAM.PR.2]: Require that AWS Identity and Access Management (IAM) customer-managed policies do not contain a statement that includes "*" in the Action and Resource elements How to fix: [FIX]: Remove AWS IAM inline policy statements with "Effect": "Allow" that permit "Action": "*" over "Resource": "*". Rule Metadata: DocumentationUrl: https://docs.aws.amazon.com/controltower/latest/userguide/identityandaccessmanagement-rules.html#ct-iam-pr-2-description Policy Validation Report Summary ╔════════════════════════╤═════════╗ ║ Plugin │ Status ║ ╟────────────────────────┼─────────╢ ║ cdk-validator-cfnguard │ failure ║ ╚════════════════════════╧═════════╝ Validation failed. See the validation report above for details Subprocess exited with error 1
どのリソースが、どのルールにマッチしてステータスがfailure
になったことが分かりますね。併せてドキュメントへのリンクや修正方法も紹介してくれて良きです。
ルールにマッチしなかった場合の挙動の確認
ルールにマッチしなかった場合も確認します。
Actionsを*
からec2:*
に変更します。
import * as cdk from "aws-cdk-lib"; import { Construct } from "constructs"; export class CdkStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const role = new cdk.aws_iam.Role(this, "Iam Role", { assumedBy: new cdk.aws_iam.ServicePrincipal("ec2.amazonaws.com"), managedPolicies: [ new cdk.aws_iam.ManagedPolicy(this, "Iam Policy", { statements: [ new cdk.aws_iam.PolicyStatement({ effect: cdk.aws_iam.Effect.ALLOW, actions: ["ec2:*"], resources: ["*"], }), ], }), ], }); } }
この状態でcdk synth
をすると以下のようにcdk-validator-cfnguard
がsuccess
となりました。
$ npx cdk synth --no-version-reporting --no-path-metadata Performing Policy Validations Validation Report ----------------- Policy Validation Report Summary ╔════════════════════════╤═════════╗ ║ Plugin │ Status ║ ╟────────────────────────┼─────────╢ ║ cdk-validator-cfnguard │ success ║ ╚════════════════════════╧═════════╝ Policy Validation Successful! Resources: IamPolicy22E02181: Type: AWS::IAM::ManagedPolicy Properties: PolicyDocument: Statement: - Action: ec2:* Effect: Allow Resource: "*" Version: "2012-10-17" Description: "" Path: / IamRoleB5F0CB96: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: ec2.amazonaws.com Version: "2012-10-17" ManagedPolicyArns: - Ref: IamPolicy22E02181 Parameters: BootstrapVersion: Type: AWS::SSM::Parameter::Value<String> Default: /cdk-bootstrap/hnb659fds/version Description: Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip] Rules: CheckBootstrapVersion: Assertions: - Assert: Fn::Not: - Fn::Contains: - - "1" - "2" - "3" - "4" - "5" - Ref: BootstrapVersion AssertDescription: CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI.
複数マッチするルールがあった場合
複数マッチするルールがあった場合にどんな見え方をするのかも確認します。
CT.EC2.PR.3とCT.EC2.PR.4にマッチするようなセキュリティグループを2つ作成します。
import * as cdk from "aws-cdk-lib"; import { Construct } from "constructs"; export class CdkStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const role = new cdk.aws_iam.Role(this, "Iam Role", { assumedBy: new cdk.aws_iam.ServicePrincipal("ec2.amazonaws.com"), managedPolicies: [ new cdk.aws_iam.ManagedPolicy(this, "Iam Policy", { statements: [ new cdk.aws_iam.PolicyStatement({ effect: cdk.aws_iam.Effect.ALLOW, actions: ["*"], resources: ["*"], }), ], }), ], }); const vpc = new cdk.aws_ec2.Vpc(this, "Vpc", { ipAddresses: cdk.aws_ec2.IpAddresses.cidr("10.1.1.0/24"), natGateways: 0, maxAzs: 1, subnetConfiguration: [ { name: "Public", subnetType: cdk.aws_ec2.SubnetType.PUBLIC, cidrMask: 27, mapPublicIpOnLaunch: false, }, ], }); const sg1 = new cdk.aws_ec2.SecurityGroup(this, "Sg1", { vpc, }); sg1.addIngressRule( cdk.aws_ec2.Peer.anyIpv4(), cdk.aws_ec2.Port.allTraffic() ); const sg2 = new cdk.aws_ec2.SecurityGroup(this, "Sg2", { vpc, }); sg2.addIngressRule( cdk.aws_ec2.Peer.anyIpv4(), cdk.aws_ec2.Port.allTraffic() ); } }
この状態でcdk synth
をすると以下のように怒られました。
$ npx cdk synth --no-version-reporting --no-path-metadata Performing Policy Validations Validation Report ----------------- ╔════════════════════════════════════╗ ║ Plugin Report ║ ║ Plugin: cdk-validator-cfnguard ║ ║ Version: N/A ║ ║ Status: failure ║ ╚════════════════════════════════════╝ (Violations) vpc_sg_open_only_to_authorized_ports_check (2 occurrences) Occurrences: - Construct Path: CdkStack/Sg1/Resource - Template Path: cdk.out/CdkStack.template.json - Creation Stack: └── CdkStack (CdkStack) │ Construct: aws-cdk-lib.Stack │ Library Version: 2.76.0 │ Location: Object.<anonymous> (/<ディレクトリパス>/cdk/bin/cdk.ts:10:1) └── Sg1 (CdkStack/Sg1) │ Construct: aws-cdk-lib.aws_ec2.SecurityGroup │ Library Version: 2.76.0 │ Location: new CdkStack (/<ディレクトリパス>/cdk/lib/cdk-stack.ts:37:17) └── Resource (CdkStack/Sg1/Resource) │ Construct: aws-cdk-lib.aws_ec2.CfnSecurityGroup │ Library Version: 2.76.0 │ Location: new SecurityGroup (/<ディレクトリパス>/cdk/node_modules/aws-cdk-lib/aws-ec2/lib/security-group.ts:515:26) - Resource ID: Sg1CAF6452B - Template Locations: > /Resources/Sg1CAF6452B/Properties/SecurityGroupIngress/0/IpProtocol - Construct Path: CdkStack/Sg2/Resource - Template Path: cdk.out/CdkStack.template.json - Creation Stack: └── CdkStack (CdkStack) │ Construct: aws-cdk-lib.Stack │ Library Version: 2.76.0 │ Location: Object.<anonymous> (/<ディレクトリパス>/cdk/bin/cdk.ts:10:1) └── Sg1 (CdkStack/Sg1) │ Construct: aws-cdk-lib.aws_ec2.SecurityGroup │ Library Version: 2.76.0 │ Location: new CdkStack (/<ディレクトリパス>/cdk/lib/cdk-stack.ts:37:17) └── Resource (CdkStack/Sg1/Resource) │ Construct: aws-cdk-lib.aws_ec2.CfnSecurityGroup │ Library Version: 2.76.0 │ Location: new SecurityGroup (/<ディレクトリパス>/cdk/node_modules/aws-cdk-lib/aws-ec2/lib/security-group.ts:515:26) - Resource ID: Sg25F6699F6 - Template Locations: > /Resources/Sg25F6699F6/Properties/SecurityGroupIngress/0/IpProtocol Description: [CT.EC2.PR.3]: Require an Amazon EC2 security group to allow incoming traffic on authorized ports only How to fix: [FIX]: Ensure that security groups with ingress rules that allow TCP or UDP traffic from '0.0.0.0/0' or ' only allow traffic from ports 80 or 443. Rule Metadata: DocumentationUrl: https://docs.aws.amazon.com/controltower/latest/userguide/ec2-rules.html#ct-ec2-pr-3-description vpc_sg_restricted_common_ports_check (2 occurrences) Occurrences: - Construct Path: CdkStack/Sg1/Resource - Template Path: cdk.out/CdkStack.template.json - Creation Stack: └── CdkStack (CdkStack) │ Construct: aws-cdk-lib.Stack │ Library Version: 2.76.0 │ Location: Object.<anonymous> (/<ディレクトリパス>/cdk/bin/cdk.ts:10:1) └── Sg1 (CdkStack/Sg1) │ Construct: aws-cdk-lib.aws_ec2.SecurityGroup │ Library Version: 2.76.0 │ Location: new CdkStack (/<ディレクトリパス>/cdk/lib/cdk-stack.ts:37:17) └── Resource (CdkStack/Sg1/Resource) │ Construct: aws-cdk-lib.aws_ec2.CfnSecurityGroup │ Library Version: 2.76.0 │ Location: new SecurityGroup (/<ディレクトリパス>/cdk/node_modules/aws-cdk-lib/aws-ec2/lib/security-group.ts:515:26) - Resource ID: Sg1CAF6452B - Template Locations: > /Resources/Sg1CAF6452B/Properties/SecurityGroupIngress/0/IpProtocol - Construct Path: CdkStack/Sg2/Resource - Template Path: cdk.out/CdkStack.template.json - Creation Stack: └── CdkStack (CdkStack) │ Construct: aws-cdk-lib.Stack │ Library Version: 2.76.0 │ Location: Object.<anonymous> (/<ディレクトリパス>/cdk/bin/cdk.ts:10:1) └── Sg1 (CdkStack/Sg1) │ Construct: aws-cdk-lib.aws_ec2.SecurityGroup │ Library Version: 2.76.0 │ Location: new CdkStack (/<ディレクトリパス>/cdk/lib/cdk-stack.ts:37:17) └── Resource (CdkStack/Sg1/Resource) │ Construct: aws-cdk-lib.aws_ec2.CfnSecurityGroup │ Library Version: 2.76.0 │ Location: new SecurityGroup (/<ディレクトリパス>/cdk/node_modules/aws-cdk-lib/aws-ec2/lib/security-group.ts:515:26) - Resource ID: Sg25F6699F6 - Template Locations: > /Resources/Sg25F6699F6/Properties/SecurityGroupIngress/0/IpProtocol Description: [CT.EC2.PR.4]: Require that an Amazon EC2 security group does not allow incoming traffic for high-risk ports How to fix: [FIX]: Remove Amazon EC2 security group ingress rules that allow traffic from '0.0.0.0/0' or '::/0' to high risk ports: '3389', '20', '23', '110', '143', '3306', '8080', '1433', '9200', '9300', '25', '445', '135', '21', '1434', '4333', '5432', '5500', '5601', '22', '3000', '5000', '8088', '8888'. Rule Metadata: DocumentationUrl: https://docs.aws.amazon.com/controltower/latest/userguide/ec2-rules.html#ct-ec2-pr-4-description iam_managed_policy_no_statements_with_admin_access_check (1 occurrences) Occurrences: - Construct Path: CdkStack/Iam Policy/Resource - Template Path: cdk.out/CdkStack.template.json - Creation Stack: └── CdkStack (CdkStack) │ Construct: aws-cdk-lib.Stack │ Library Version: 2.76.0 │ Location: Object.<anonymous> (/<ディレクトリパス>/cdk/bin/cdk.ts:10:1) └── Sg1 (CdkStack/Sg1) │ Construct: aws-cdk-lib.aws_ec2.SecurityGroup │ Library Version: 2.76.0 │ Location: new CdkStack (/<ディレクトリパス>/cdk/lib/cdk-stack.ts:37:17) └── Resource (CdkStack/Sg1/Resource) │ Construct: aws-cdk-lib.aws_ec2.CfnSecurityGroup │ Library Version: 2.76.0 │ Location: new SecurityGroup (/<ディレクトリパス>/cdk/node_modules/aws-cdk-lib/aws-ec2/lib/security-group.ts:515:26) - Resource ID: IamPolicy22E02181 - Template Locations: > /Resources/IamPolicy22E02181/Properties/PolicyDocument/Statement/0/Resource Description: [CT.IAM.PR.2]: Require that AWS Identity and Access Management (IAM) customer-managed policies do not contain a statement that includes "*" in the Action and Resource elements How to fix: [FIX]: Remove AWS IAM inline policy statements with "Effect": "Allow" that permit "Action": "*" over "Resource": "*". Rule Metadata: DocumentationUrl: https://docs.aws.amazon.com/controltower/latest/userguide/identityandaccessmanagement-rules.html#ct-iam-pr-2-description Policy Validation Report Summary ╔════════════════════════╤═════════╗ ║ Plugin │ Status ║ ╟────────────────────────┼─────────╢ ║ cdk-validator-cfnguard │ failure ║ ╚════════════════════════╧═════════╝ Validation failed. See the validation report above for details Subprocess exited with error 1
全リソースそれぞれに検出結果が出力されていますね。
ただしiam_managed_policy_no_statements_with_admin_access_check
の結果にも関わらず、Creation Stack
のツリーで表示されているリソースがセキュリティグループであったりとちょっと怪しさもあります。ベータ版なので割り切ります。
一部のルールを無効化する
一部のルールを無効化することができます。
その場合はdisabledRules
でルール名を指定します。なお、スタック単位やリソース単位での抑制は出来なさそうなので注意が必要です。
今回はCT.EC2.PR.3とCT.EC2.PR.4のルールを無効化します。
#!/usr/bin/env node import "source-map-support/register"; import * as cdk from "aws-cdk-lib"; import { CdkStack } from "../lib/cdk-stack"; import { CfnGuardValidator } from "@cdklabs/cdk-validator-cfnguard"; const app = new cdk.App({ policyValidationBeta1: [ new CfnGuardValidator({ disabledRules: ["ct-ec2-pr-3", "ct-ec2-pr-4"] }), ], }); new CdkStack(app, "CdkStack");
この状態でcdk synth
をすると以下のように1件だけ出力されるようになりました。
$ npx cdk synth --no-version-reporting --no-path-metadata Performing Policy Validations Validation Report ----------------- ╔════════════════════════════════════╗ ║ Plugin Report ║ ║ Plugin: cdk-validator-cfnguard ║ ║ Version: N/A ║ ║ Status: failure ║ ╚════════════════════════════════════╝ (Violations) iam_managed_policy_no_statements_with_admin_access_check (1 occurrences) Occurrences: - Construct Path: CdkStack/Iam Policy/Resource - Template Path: cdk.out/CdkStack.template.json - Creation Stack: └── CdkStack (CdkStack) │ Construct: aws-cdk-lib.Stack │ Library Version: 2.76.0 │ Location: Run with '--debug' to include location info └── Iam Policy (CdkStack/Iam Policy) │ Construct: aws-cdk-lib.aws_iam.ManagedPolicy │ Library Version: 2.76.0 │ Location: Run with '--debug' to include location info └── ImportedIam Policy (CdkStack/Iam Policy/ImportedIam Policy) │ Construct: aws-cdk-lib.Resource │ Library Version: 2.76.0 │ Location: Run with '--debug' to include location info - Resource ID: IamPolicy22E02181 - Template Locations: > /Resources/IamPolicy22E02181/Properties/PolicyDocument/Statement/0/Resource Description: [CT.IAM.PR.2]: Require that AWS Identity and Access Management (IAM) customer-managed policies do not contain a statement that includes "*" in the Action and Resource elements How to fix: [FIX]: Remove AWS IAM inline policy statements with "Effect": "Allow" that permit "Action": "*" over "Resource": "*". Rule Metadata: DocumentationUrl: https://docs.aws.amazon.com/controltower/latest/userguide/identityandaccessmanagement-rules.html#ct-iam-pr-2-description Policy Validation Report Summary ╔════════════════════════╤═════════╗ ║ Plugin │ Status ║ ╟────────────────────────┼─────────╢ ║ cdk-validator-cfnguard │ failure ║ ╚════════════════════════╧═════════╝ Validation failed. See the validation report above for details Subprocess exited with error 1
Control Towerのルールを全て無効化する
Control Towerのルールを全て無効化したい場合もあると思います。その場合は、controlTowerRulesEnabled: false
を指定します。
#!/usr/bin/env node import "source-map-support/register"; import * as cdk from "aws-cdk-lib"; import { CdkStack } from "../lib/cdk-stack"; import { CfnGuardValidator } from "@cdklabs/cdk-validator-cfnguard"; const app = new cdk.App({ policyValidationBeta1: [ new CfnGuardValidator({ disabledRules: ["ct-ec2-pr-3", "ct-ec2-pr-4"], controlTowerRulesEnabled: false, }), ], }); new CdkStack(app, "CdkStack");
この状態でcdk synth
をすると以下のようにcdk-validator-cfnguard
がsuccess
となりました。
$ npx cdk synth --no-version-reporting --no-path-metadata Performing Policy Validations Validation Report ----------------- Policy Validation Report Summary ╔════════════════════════╤═════════╗ ║ Plugin │ Status ║ ╟────────────────────────┼─────────╢ ║ cdk-validator-cfnguard │ success ║ ╚════════════════════════╧═════════╝ Policy Validation Successful! Resources: IamPolicy22E02181: Type: AWS::IAM::ManagedPolicy Properties: PolicyDocument: Statement: - Action: "*" Effect: Allow Resource: "*" Version: "2012-10-17" Description: "" Path: / IamRoleB5F0CB96: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: ec2.amazonaws.com Version: "2012-10-17" ManagedPolicyArns: - Ref: IamPolicy22E02181 Vpc8378EB38: Type: AWS::EC2::VPC Properties: CidrBlock: 10.1.1.0/24 EnableDnsHostnames: true EnableDnsSupport: true InstanceTenancy: default Tags: - Key: Name Value: CdkStack/Vpc VpcPublicSubnet1Subnet5C2D37C4: Type: AWS::EC2::Subnet Properties: VpcId: Ref: Vpc8378EB38 AvailabilityZone: Fn::Select: - 0 - Fn::GetAZs: "" CidrBlock: 10.1.1.0/27 MapPublicIpOnLaunch: false Tags: - Key: aws-cdk:subnet-name Value: Public - Key: aws-cdk:subnet-type Value: Public - Key: Name Value: CdkStack/Vpc/PublicSubnet1 VpcPublicSubnet1RouteTable6C95E38E: Type: AWS::EC2::RouteTable Properties: VpcId: Ref: Vpc8378EB38 Tags: - Key: Name Value: CdkStack/Vpc/PublicSubnet1 VpcPublicSubnet1RouteTableAssociation97140677: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: Ref: VpcPublicSubnet1RouteTable6C95E38E SubnetId: Ref: VpcPublicSubnet1Subnet5C2D37C4 VpcPublicSubnet1DefaultRoute3DA9E72A: Type: AWS::EC2::Route Properties: RouteTableId: Ref: VpcPublicSubnet1RouteTable6C95E38E DestinationCidrBlock: 0.0.0.0/0 GatewayId: Ref: VpcIGWD7BA715C DependsOn: - VpcVPCGWBF912B6E VpcIGWD7BA715C: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: CdkStack/Vpc VpcVPCGWBF912B6E: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: Ref: Vpc8378EB38 InternetGatewayId: Ref: VpcIGWD7BA715C Sg1CAF6452B: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: CdkStack/Sg1 SecurityGroupEgress: - CidrIp: 0.0.0.0/0 Description: Allow all outbound traffic by default IpProtocol: "-1" SecurityGroupIngress: - CidrIp: 0.0.0.0/0 Description: from 0.0.0.0/0:ALL TRAFFIC IpProtocol: "-1" VpcId: Ref: Vpc8378EB38 Sg25F6699F6: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: CdkStack/Sg2 SecurityGroupEgress: - CidrIp: 0.0.0.0/0 Description: Allow all outbound traffic by default IpProtocol: "-1" SecurityGroupIngress: - CidrIp: 0.0.0.0/0 Description: from 0.0.0.0/0:ALL TRAFFIC IpProtocol: "-1" VpcId: Ref: Vpc8378EB38 Parameters: BootstrapVersion: Type: AWS::SSM::Parameter::Value<String> Default: /cdk-bootstrap/hnb659fds/version Description: Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip] Rules: CheckBootstrapVersion: Assertions: - Assert: Fn::Not: - Fn::Contains: - - "1" - "2" - "3" - "4" - "5" - Ref: BootstrapVersion AssertDescription: CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI.
独自ルールの追加
独自ルールを追加することも可能です。
今回はデフォルトでバンドルされていないCT.IAM.PR.3を追加してみます。
まず、ルールを定義したファイルを作成します。
# ################################### ## Rule Specification ## ##################################### # # Rule Name: # iam_managed_policy_no_statements_with_full_access_check # # Description: # This control checks that AWS Identity and Access Management (IAM) customer-managed policies do not contain statements of "Effect": "Allow" with "Action": "Service:*" (for example, s3:*) for individual AWS services, and that the policies do not use the combination of "NotAction" with an "Effect" of "Allow". # # Reports on: # AWS::IAM::ManagedPolicy # # Rule Parameters: # None # # Scenarios: # Scenario: 1 # Given: The input document is an AWS CloudFormation or AWS CloudFormation hook document # And: The input document does not contain any IAM Managed Policy resources # Then: SKIP # Scenario: 2 # Given: The input document is an AWS CloudFormation or AWS CloudFormation hook document # And: The input document contains an IAM Managed Policy resource # And: The policy has no statements with 'Effect' set to 'Allow' # Then: SKIP # Scenario: 3 # Given: The input document is an AWS CloudFormation or AWS CloudFormation hook document # And: The input document contains an IAM Managed Policy resource # And: The policy has a statement with 'Effect' set to 'Allow' # And: The policy has one or more 'Action' statements # And: At least one 'Action' statement allows full access to a service ('Action' has a value 'service:*') # Then: FAIL # Scenario: 4 # Given: The input document is an AWS CloudFormation or AWS CloudFormation hook document # And: The input document contains an IAM Managed Policy resource # And: The policy has a statement with 'Effect' set to 'Allow' # And: The policy has one or more 'NotAction' statements # Then: FAIL # Scenario: 5 # Given: The input document is an AWS CloudFormation or AWS CloudFormation hook document # And: The input document contains an IAM Managed Policy resource # And: The policy has a statement with 'Effect' set to 'Allow' # And: The policy has one or more 'Action' statements # And: No 'Action' statements allow full access to a service ('Action' does not have a value 'service:*') # Then: PASS # # Constants # let AWS_IAM_MANAGED_POLICY_TYPE = "AWS::IAM::ManagedPolicy" let WILDCARD_ACTION_PATTERN = /^[\w]*[:]*\*$/ let INPUT_DOCUMENT = this # # Assignments # let iam_managed_policies = Resources.*[ Type == %AWS_IAM_MANAGED_POLICY_TYPE ] # # Primary Rules # rule iam_managed_policy_no_statements_with_full_access_check when is_cfn_template(%INPUT_DOCUMENT) %iam_managed_policies not empty { check(%iam_managed_policies.Properties) << [CT.IAM.PR.3]: Require that AWS Identity and Access Management (IAM) customer-managed policies do not have wildcard service actions [FIX]: Remove statements from IAM customer-managed policies with "Effect": "Allow" and "Action": "service:*" or "Effect": "Allow" and "NotAction". >> } rule iam_managed_policy_no_statements_with_full_access_check when is_cfn_hook(%INPUT_DOCUMENT, %AWS_IAM_MANAGED_POLICY_TYPE) { check(%INPUT_DOCUMENT.%AWS_IAM_MANAGED_POLICY_TYPE.resourceProperties) << [CT.IAM.PR.3]: Require that AWS Identity and Access Management (IAM) customer-managed policies do not have wildcard service actions [FIX]: Remove statements from IAM customer-managed policies with "Effect": "Allow" and "Action": "service:*" or "Effect": "Allow" and "NotAction". >> } # # Parameterized Rules # rule check(policy) { %policy [ filter_policy_document_with_statement_provided(this) ] { PolicyDocument { check_statement_no_wildcard_actions(Statement) check_statement_no_not_action(Statement) } } } rule check_statement_no_wildcard_actions(statement) { %statement [ filter_allow_on_action(this) ] { Action exists check_no_wildcard_action(Action) } } rule check_statement_no_not_action(statement) { %statement [ filter_allow(this) ] { NotAction not exists } } rule filter_allow_on_action(statement) { %statement { Effect == "Allow" Action exists } } rule filter_allow(statement) { %statement { Effect == "Allow" } } rule filter_policy_document_with_statement_provided(policy) { %policy { PolicyDocument exists PolicyDocument is_struct PolicyDocument { Statement exists filter_statement_non_empty_list(Statement) or Statement is_struct } } } rule filter_statement_non_empty_list(statement) { %statement { this is_list this not empty } } rule check_no_wildcard_action(actions) { %actions[*] { this != %WILDCARD_ACTION_PATTERN } } # # Utility Rules # rule is_cfn_template(doc) { %doc { AWSTemplateFormatVersion exists or Resources exists } } rule is_cfn_hook(doc, RESOURCE_TYPE) { %doc.%RESOURCE_TYPE.resourceProperties exists }
作成したファイルを読み込むようにCfnGuardValidator
を設定します。
#!/usr/bin/env node import "source-map-support/register"; import * as cdk from "aws-cdk-lib"; import { CdkStack } from "../lib/cdk-stack"; import { CfnGuardValidator } from "@cdklabs/cdk-validator-cfnguard"; import * as path from "path"; const app = new cdk.App({ policyValidationBeta1: [ new CfnGuardValidator({ disabledRules: ["ct-ec2-pr-3", "ct-ec2-pr-4"], controlTowerRulesEnabled: false, rules: [path.join(__dirname, "../lib/rule/ct-iam-pr-3.guard")], }), ], }); new CdkStack(app, "CdkStack");
この状態でcdk synth
をすると以下のように怒られました。
$ npx cdk synth --no-version-reporting --no-path-metadata Performing Policy Validations Validation Report ----------------- ╔════════════════════════════════════╗ ║ Plugin Report ║ ║ Plugin: cdk-validator-cfnguard ║ ║ Version: N/A ║ ║ Status: failure ║ ╚════════════════════════════════════╝ (Violations) iam_managed_policy_no_statements_with_full_access_check (1 occurrences) Occurrences: - Construct Path: CdkStack/Iam Policy/Resource - Template Path: cdk.out/CdkStack.template.json - Creation Stack: └── CdkStack (CdkStack) │ Construct: aws-cdk-lib.Stack │ Library Version: 2.76.0 │ Location: Run with '--debug' to include location info └── Iam Policy (CdkStack/Iam Policy) │ Construct: aws-cdk-lib.aws_iam.ManagedPolicy │ Library Version: 2.76.0 │ Location: Run with '--debug' to include location info └── ImportedIam Policy (CdkStack/Iam Policy/ImportedIam Policy) │ Construct: aws-cdk-lib.Resource │ Library Version: 2.76.0 │ Location: Run with '--debug' to include location info - Resource ID: IamPolicy22E02181 - Template Locations: > /Resources/IamPolicy22E02181/Properties/PolicyDocument/Statement/0/Action Description: [CT.IAM.PR.3]: Require that AWS Identity and Access Management (IAM) customer-managed policies do not have wildcard service actions How to fix: [FIX]: Remove statements from IAM customer-managed policies with "Effect": "Allow" and "Action": "service:*" or "Effect": "Allow" and "NotAction". Rule Metadata: DocumentationUrl: https://docs.aws.amazon.com/controltower/latest/userguide/rule-rules.html#ct-iam-pr-3-description Policy Validation Report Summary ╔════════════════════════╤═════════╗ ║ Plugin │ Status ║ ╟────────────────────────┼─────────╢ ║ cdk-validator-cfnguard │ failure ║ ╚════════════════════════╧═════════╝ Validation failed. See the validation report above for details Subprocess exited with error 1
良い感じで検出されましたね。
CloudFormation Guardを既に使っていた時に役立ちそう
2023/4/28時点ではベータ版ですが、AWS CDKで合成時にAWS CloudFormation GuardによるコンプライアンスチェックができるようになるCDK CFN Guard Validator Pluginを紹介しました。
CFn Guardを既に使われている方は非常に役立ちそうですね。
ルールファイルの作成方法など詳細なお作法はAWS公式ドキュメントをウォッチしておくと良いでしょう。
この記事が誰かの助けになれば幸いです。
以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!