Make your AWS CDK app more security via cloudformation-guard
git clone https://github.com/neilkuan/cdk-cloudformation-guard-demo
cd cdk-cloudformation-guard-demo
yarn
or
npm i
--- example output ---
yarn install v1.22.10
[1/4] π Resolving packages...
success Already up-to-date...
...
...
Will create cloudformation
*.template.json
files incdk.out
directory.
cdk synth
cdk ls
--- example output ---
cdk-cloudformation-guard-demo
cdk-cloudformation-guard-demo-fail
The Pass Check Stack.
...
export class guardDemo extends Stack {
constructor(scope: Construct, id: string, props: StackProps = {}) {
super(scope, id, props);
const vpc = ec2.Vpc.fromLookup(this, 'defVpc', {
isDefault: true,
});
const sg = new ec2.SecurityGroup(this, 'checkSG', {
securityGroupName: 'checkSG',
allowAllOutbound: true,
description: 'This is for cdk cloudformation-guard demo Security Group.',
vpc,
});
const companyIp = '1.2.3.4/32';
sg.addIngressRule(ec2.Peer.ipv4(companyIp), ec2.Port.allTraffic());
sg.addIngressRule(ec2.Peer.ipv4(companyIp), ec2.Port.tcp(3306));
sg.addIngressRule(ec2.Peer.ipv4(companyIp), ec2.Port.tcp(22));
}
}
The Fail Check Stack.
...
export class guardDemoFail extends Stack {
constructor(scope: Construct, id: string, props: StackProps = {}) {
super(scope, id, props);
const vpc = ec2.Vpc.fromLookup(this, 'defVpc', {
isDefault: true,
});
const sg = new ec2.SecurityGroup(this, 'checkSG', {
securityGroupName: 'checkSG',
allowAllOutbound: true,
description: 'This is for cdk cloudformation-guard demo Security Group.',
vpc,
});
const companyIp = '1.2.3.4/32';
sg.addIngressRule(ec2.Peer.ipv4(companyIp), ec2.Port.allTraffic());
sg.addIngressRule(ec2.Peer.ipv4(companyIp), ec2.Port.tcp(3306));
sg.addIngressRule(ec2.Peer.ipv4(companyIp), ec2.Port.tcp(22));
// will check pass, only 80 and 443 can from anyivp4.
sg.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(80));
// will check fail, only 80 and 443 can from anyivp4.
sg.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(22));
// will check fail, only 80 and 443 can from anyivp4.
sg.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.allTraffic());
}
}
## Find the resource that Type is 'AWS::EC2::SecurityGroup'.
let sg_resources = Resources.*[ Type == 'AWS::EC2::SecurityGroup' ]
## Define the rule, the ingress which all traffic from cidrip '0.0.0.0/0' can not be created.
rule ingress_rule_not_in_anyipv4_to_any_traffic when %sg_resources !empty {
let ingress = %sg_resources.Properties.SecurityGroupIngress[
IpProtocol is_string
]
%ingress[*] {
when this.CidrIp == "0.0.0.0/0" {
this.IpProtocol != "-1"
}
}
}
## Define the rule, only the ingress which all traffic from cidrip '1.2.3.4/32'(ex: company ip) can be created.
rule ingress_rule_allow_anyip_protocol_to_company_ip when %sg_resources !empty {
let ingress = %sg_resources.Properties.SecurityGroupIngress[
IpProtocol is_string
]
%ingress[*] {
when this.CidrIp == "1.2.3.4/32" {
this.IpProtocol IN ["tcp", "udp", "-1"]
}
}
}
## Define the rule, only the ingress which 443 or 80 from cidrip '0.0.0.0/0' can be created.
rule ingress_rule_allow_http_https_can_from_anyip when %sg_resources !empty {
let ingress = %sg_resources.Properties.SecurityGroupIngress[
IpProtocol is_string
]
%ingress[*] {
when this.CidrIp == "0.0.0.0/0" {
this.FromPort IN [443, 80]
}
}
}
yarn guardcheck
or
npm run guardcheck
--- example output ---
> npx projen guardcheck
π€ guardcheck | env: PATH=/usr/local/lib/node_...
π€ guardcheck | for i in `ls cdk.out/*template.json`;do cfn-guard validate -r sg-rule-common-tcp.rules -o yaml --data $i;done
cdk-cloudformation-guard-demo-fail.template.json Status = FAIL
PASS rules
sg-rule-common-tcp.rules/ingress_rule_allow_anyip_protocol_to_company_ip PASS
FAILED rules
sg-rule-common-tcp.rules/ingress_rule_not_in_anyipv4_to_any_traffic FAIL
sg-rule-common-tcp.rules/ingress_rule_allow_http_https_can_from_anyip FAIL
---
---
data_from: cdk-cloudformation-guard-demo-fail.template.json
rules_from: sg-rule-common-tcp.rules
not_compliant:
ingress_rule_not_in_anyipv4_to_any_traffic:
- rule: ingress_rule_not_in_anyipv4_to_any_traffic
path: /Resources/checkSG2E84885A/Properties/SecurityGroupIngress/5/IpProtocol
provided: "-1"
expected: "-1"
comparison:
operator: Eq
not_operator_exists: true
message: ""
ingress_rule_allow_http_https_can_from_anyip:
- rule: ingress_rule_allow_http_https_can_from_anyip
path: /Resources/checkSG2E84885A/Properties/SecurityGroupIngress/4/FromPort
provided: 22
expected: 443
comparison:
operator: In
not_operator_exists: false
message: ""
- rule: ingress_rule_allow_http_https_can_from_anyip
path: /Resources/checkSG2E84885A/Properties/SecurityGroupIngress/4/FromPort
provided: 22
expected: 80
comparison:
operator: In
not_operator_exists: false
message: ""
- rule: ingress_rule_allow_http_https_can_from_anyip
path: /Resources/checkSG2E84885A/Properties/SecurityGroupIngress/5
provided: ~
expected: ~
comparison: ~
message: "Attempting to retrieve array index or key from map at path = /Resources/checkSG2E84885A/Properties/SecurityGroupIngress/5 , Type was not an array/object map, Remaining Query = FromPort"
not_applicable: []
compliant:
- ingress_rule_allow_anyip_protocol_to_company_ip
cdk-cloudformation-guard-demo.template.json Status = PASS
SKIP rules
sg-rule-common-tcp.rules/ingress_rule_not_in_anyipv4_to_any_traffic SKIP
sg-rule-common-tcp.rules/ingress_rule_allow_http_https_can_from_anyip SKIP
PASS rules
sg-rule-common-tcp.rules/ingress_rule_allow_anyip_protocol_to_company_ip PASS
---
---
data_from: cdk-cloudformation-guard-demo.template.json
rules_from: sg-rule-common-tcp.rules
not_compliant: {}
not_applicable:
- ingress_rule_not_in_anyipv4_to_any_traffic
- ingress_rule_allow_http_https_can_from_anyip
compliant:
- ingress_rule_allow_anyip_protocol_to_company_ip
cd kubernetes
cfn-guard validate -r deployment.guard -d bad-deploy.yaml -o yaml
--- example output ---
bad-deploy.yaml Status = FAIL
PASS rules
deployment.guard/version_and_kind_match PASS
FAILED rules
deployment.guard/ensure_deploy_has_owner_label FAIL
deployment.guard/ensure_container_has_memory_limits FAIL
---
---
data_from: bad-deploy.yaml
rules_from: deployment.guard
not_compliant:
ensure_container_has_memory_limits:
- rule: ensure_container_has_memory_limits
path: /spec/template/spec/containers/0
provided: ~
expected: ~
comparison: ~
message: "Attempting to retrieve array index or key from map at path = /spec/template/spec/containers/0 , Type was not an array/object map, Remaining Query = resources.limits"
ensure_deploy_has_owner_label:
- rule: ensure_deploy_has_owner_label
path: /metadata/labels
provided:
app: nginx
expected: ~
comparison:
operator: Exists
not_operator_exists: false
message: "\n Id: Cathay_K8S_001\n Description: Need Define Deployment Onwer\n "
not_applicable: []
compliant:
- version_and_kind_matchbad-deploy.yaml Status = FAIL
PASS rules
deployment.guard/version_and_kind_match PASS
FAILED rules
deployment.guard/ensure_deploy_has_owner_label FAIL
deployment.guard/ensure_container_has_memory_limits FAIL
---
---
data_from: bad-deploy.yaml
rules_from: deployment.guard
not_compliant:
ensure_container_has_memory_limits:
- rule: ensure_container_has_memory_limits
path: /spec/template/spec/containers/0
provided: ~
expected: ~
comparison: ~
message: "Attempting to retrieve array index or key from map at path = /spec/template/spec/containers/0 , Type was not an array/object map, Remaining Query = resources.limits"
ensure_deploy_has_owner_label:
- rule: ensure_deploy_has_owner_label
path: /metadata/labels
provided:
app: nginx
expected: ~
comparison:
operator: Exists
not_operator_exists: false
message: "\n Id: Cathay_K8S_001\n Description: Need Define Deployment Onwer\n "
not_applicable: []
compliant:
- version_and_kind_match
------
- need define onwer label
- need define container limits memory
cat bad-deploy.yaml
----
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
------
cfn-guard validate -r deployment.guard -d good-deploy.yaml -o yaml
--- example output ---
good-deploy.yaml Status = PASS
PASS rules
deployment.guard/version_and_kind_match PASS
deployment.guard/ensure_deploy_has_owner_label PASS
deployment.guard/ensure_container_has_memory_limits PASS
---
Evaluation of rules deployment.guard against data good-deploy.yaml
--
Rule [deployment.guard/ensure_deploy_has_owner_label] is compliant for template [good-deploy.yaml]
Rule [deployment.guard/ensure_container_has_memory_limits] is compliant for template [good-deploy.yaml]
Rule [deployment.guard/version_and_kind_match] is compliant for template [good-deploy.yaml]
--
------
cd terraform/good_instance
terraform init
terraform plan -out tfgood.bin
terraform show -json tfgood.bin > tfgood.json
cd ../bad_instance
terraform init
terraform plan -out tfbad.bin
terraform show -json tfbad.bin > tfbad.json
cd ..
- bad instance
pwd
xxx/xxx/cdk-cloudformation-guard-demo/terraform
cfn-guard validate -r terraform.guard -d bad_instance/tfbad.json -o yaml
--- example output ---
tfbad.json Status = FAIL
FAILED rules
terraform.guard/gcp_instance_need_lanuch_at_tw_az FAIL
terraform.guard/gcp_instance_network_cannot_use_default_network FAIL
---
---
data_from: tfbad.json
rules_from: terraform.guard
not_compliant:
gcp_instance_network_cannot_use_default_network:
- rule: gcp_instance_network_cannot_use_default_network
path: /planned_values/root_module/resources/0/values/network_interface/0/network
provided: default
expected: default
comparison:
operator: Eq
not_operator_exists: true
message: "\n Id: Cathay_GCP_001\n Description: GCE Need Lanuch at Taiwan Region\n "
gcp_instance_need_lanuch_at_tw_az:
- rule: gcp_instance_need_lanuch_at_tw_az
path: /planned_values/root_module/resources/0/values/zone
provided: us-central1-a
expected: asia-east1-a
comparison:
operator: In
not_operator_exists: false
message: "\n Id: Cathay_GCP_001\n Description: GCE Need Lanuch at Taiwan Region\n "
- rule: gcp_instance_need_lanuch_at_tw_az
path: /planned_values/root_module/resources/0/values/zone
provided: us-central1-a
expected: asia-east1-b
comparison:
operator: In
not_operator_exists: false
message: "\n Id: Cathay_GCP_001\n Description: GCE Need Lanuch at Taiwan Region\n "
- rule: gcp_instance_need_lanuch_at_tw_az
path: /planned_values/root_module/resources/0/values/zone
provided: us-central1-a
expected: asia-east1-c
comparison:
operator: In
not_operator_exists: false
message: "\n Id: Cathay_GCP_001\n Description: GCE Need Lanuch at Taiwan Region\n "
not_applicable: []
compliant: []
------
- good instance
pwd
xxx/xxx/cdk-cloudformation-guard-demo/terraform
cfn-guard validate -r terraform.guard -d good_instance/tfgood.json -o yaml
--- example output ---
tfgood.json Status = PASS
SKIP rules
terraform.guard/gcp_instance_network_cannot_use_default_network SKIP
PASS rules
terraform.guard/gcp_instance_need_lanuch_at_tw_az PASS
---
---
data_from: tfgood.json
rules_from: terraform.guard
not_compliant: {}
not_applicable:
- gcp_instance_network_cannot_use_default_network
compliant:
- gcp_instance_need_lanuch_at_tw_az
-----------