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()
を指定します。
./bin/cdk.ts
#!/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ロールを定義します。
./lib/cdk-stack.ts
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:*
に変更します。
./lib/cdk-stack.ts
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つ作成します。
./lib/cdk-stack.ts
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のルールを無効化します。
./bin/cdk.ts
#!/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
を指定します。
./bin/cdk.ts
#!/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を追加してみます。
まず、ルールを定義したファイルを作成します。
./lib/rule/ct-iam-pr-3.guard
# ###################################
## 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
を設定します。
./bin/cdk.ts
#!/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)でした!