1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
|
# NCD policy evaluations =======================================================
# Controls : NCD-003, NCD-005, NCD-006, NCD-007, NCD-008, NCD-009
#
# Co-emitted from cmp.rego:
# NCD-001 → CMP-005 Unrestricted SSH Ingress
# NCD-002 → CMP-006 Unrestricted RDP Ingress
# NCD-004 → CMP-008 Default Security Group Rules
package controls.aws.ncd
import data.common.definitions as defs
import data.common.exemptions as exemptions
import data.common.network as network
import data.common.outputs as outputs
# Scope guard ------------------------------------------------------------------
_vpc_in_scope if {
some _, rc in input.resource_changes
rc.type == "aws_vpc"
}
# NCD-003 — Block Unrestricted Access to High-Risk Ports ----------------------
deny contains msg if {
policy_id := "ICP-TF-AWS-NCD-003"
some _, rc in input.resource_changes
rc.type in {"aws_security_group", "aws_vpc_security_group_ingress_rule"}
rc.change.actions[_] in ["create", "update"]
some rule in network.ingress_rules(rc)
network.unrestricted_cidr(rule)
some port in defs.common_use_ports
network.port_in_range(port, rule)
meta := data.policies[policy_id]
meta.status == "active"
not exemptions.policy_exemption(policy_id)
not exemptions.severity_exemption(meta.severity)
msg := outputs.violation(
policy_id, meta,
sprintf("'%s.%s' permits unrestricted ingress to high-risk port %d", [rc.type, rc.name, port]),
)
}
# NCD-005 — Restrict NACL Ingress to Remote Administration Ports --------------
deny contains msg if {
policy_id := "ICP-TF-AWS-NCD-005"
some _, rc in input.resource_changes
rc.type == "aws_network_acl_rule"
rc.change.actions[_] in ["create", "update"]
rc.change.after.egress == false
rc.change.after.protocol == "tcp"
rc.change.after.rule_action == "allow"
rc.change.after.cidr_block == "0.0.0.0/0"
some port in defs.management_ports
network.port_in_range(port, rc.change.after)
meta := data.policies[policy_id]
meta.status == "active"
not exemptions.policy_exemption(policy_id)
not exemptions.severity_exemption(meta.severity)
msg := outputs.violation(
policy_id, meta,
sprintf("NACL rule '%s' allows ingress from 0.0.0.0/0 to admin port %d", [rc.name, port]),
)
}
# NCD-006 — Require VPC Endpoints for AWS Service Traffic ---------------------
deny contains msg if {
policy_id := "ICP-TF-AWS-NCD-006"
_vpc_in_scope
some service in _required_endpoint_services
not _endpoint_exists(service)
meta := data.policies[policy_id]
meta.status == "active"
not exemptions.policy_exemption(policy_id)
not exemptions.severity_exemption(meta.severity)
msg := outputs.violation(
policy_id, meta,
sprintf("No aws_vpc_endpoint found for service '%s'", [service]),
)
}
_required_endpoint_services := {
"s3",
"dynamodb",
"kms",
"secretsmanager",
}
_endpoint_exists(service) if {
some _, rc in input.resource_changes
rc.type == "aws_vpc_endpoint"
rc.change.actions[_] in ["create", "update"]
contains(rc.change.after.service_name, service)
}
# NCD-007 — Application Load Balancer Must Redirect HTTP to HTTPS -------------
deny contains msg if {
some policy_id in {"ICP-TF-AWS-NCD-007", "ICP-TF-AWS-GAC-020"}
some _, rc in input.resource_changes
rc.type == "aws_lb_listener"
rc.change.actions[_] in ["create", "update"]
rc.change.after.port == 80
not _redirects_to_https(rc.change.after)
meta := data.policies[policy_id]
meta.status == "active"
not exemptions.policy_exemption(policy_id)
not exemptions.severity_exemption(meta.severity)
msg := outputs.violation(
policy_id, meta,
sprintf("ALB listener '%s' on port 80 does not redirect to HTTPS", [rc.name]),
)
}
_redirects_to_https(after) if {
some action in after.default_action
action.type == "redirect"
some redirect in action.redirect
redirect.protocol == "HTTPS"
redirect.port == "443"
}
# NCD-008 — CloudFront Distributions Must Enforce HTTPS to Viewers ------------
deny contains msg if {
some policy_id in {"ICP-TF-AWS-NCD-008", "ICP-TF-AWS-GAC-021"}
some _, rc in input.resource_changes
rc.type == "aws_cloudfront_distribution"
rc.change.actions[_] in ["create", "update"]
not _https_enforced(rc.change.after)
meta := data.policies[policy_id]
meta.status == "active"
not exemptions.policy_exemption(policy_id)
not exemptions.severity_exemption(meta.severity)
msg := outputs.violation(
policy_id, meta,
sprintf("CloudFront distribution '%s' does not enforce HTTPS to viewers", [rc.name]),
)
}
_https_enforced(after) if {
after.default_cache_behavior[_].viewer_protocol_policy in {"redirect-to-https", "https-only"}
after.viewer_certificate[_].minimum_protocol_version in {"TLSv1.2_2021", "TLSv1.2_2022", "TLSv1.3_2022"}
}
# NCD-009 — Enable API Gateway REST Stage Execution Logging -------------------
deny contains msg if {
policy_id := "ICP-TF-AWS-NCD-009"
some _, rc in input.resource_changes
rc.type == "aws_api_gateway_method_settings"
rc.change.actions[_] in ["create", "update"]
rc.change.after.method_path == "*/*"
not _logging_enabled(rc.change.after)
meta := data.policies[policy_id]
meta.status == "active"
not exemptions.policy_exemption(policy_id)
not exemptions.severity_exemption(meta.severity)
msg := outputs.violation(
policy_id, meta,
sprintf("API Gateway method settings '%s' does not enable execution logging", [rc.name]),
)
}
_logging_enabled(after) if {
some setting in after.settings
setting.logging_level in {"ERROR", "INFO"}
}
|