forked from nyalavarthi/opa-policies-s3-rds
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy paths3-validate.rego
167 lines (133 loc) · 4.86 KB
/
s3-validate.rego
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
package terraform.analysis
import input as tfplan
########################
# Parameters for Policy
########################
# acceptable score for automated authorization
blast_radius = 5
# weights assigned for each operation on each resource-type
weights = {
"aws_autoscaling_group": {"delete": 100, "create": 10, "modify": 1},
"aws_s3_bucket": {"acl": 10, "ssl": 10, "logs": 5, "sse": 10, "tags": 10, "region":10, "logging":10}
}
# Consider exactly these resource types in calculations
resource_types = {"aws_s3_bucket"}
minimum_tags = {"Name", "app:name"}
violations = data.terraform.analysis.violation
authorized = data.terraform.analysis.authz
#########
# Policy
#########
# Authorization holds if score for the plan is acceptable and no changes are made to IAM
default authz = false
authz {
score < blast_radius
not touches_iam
}
# Compute the score for a Terraform plan as the weighted sum of deletions, creations, modifications
score = s {
all := [ x |
some resource_type
crud := weights[resource_type];
acl_chg := crud["acl"] * s3_acl_change[resource_type];
region_chg := crud["region"] * s3_region_change[resource_type];
sse_chg := crud["sse"] * s3_encryption_change[resource_type];
tags_chg := crud["tags"] * s3_tags_change[resource_type];
logging_chg := crud["logging"] * s3_logging_change[resource_type];
x := acl_chg + region_chg + sse_chg + tags_chg + logging_chg
]
s := sum(all)
}
# Whether there is any change to IAM
touches_iam {
all := resources["aws_iam"]
count(all) > 0
}
# Whether there is any change to IAM
touches_sg {
all := resources["aws_security_group"]
count(all) > 0
}
####################
# Terraform Library
####################
# list of all resources of a given type
resources[resource_type] = all {
some resource_type
resource_types[resource_type]
all := [name |
name:= tfplan.resource_changes[_]
name.type == resource_type
]
}
#print validation errors to console
violation["missing required tags"] {
s3_tags_change[resource_types[_]] > 0
}
violation["bucket region shoule be in eu-central-1 "] {
s3_region_change[resource_types[_]] > 0
}
violation["bucket acl property should be private unless it is a website bucket "] {
s3_acl_change[resource_types[_]] > 0
}
violation["bucket should be encrypted with AES256/KMS "] {
s3_encryption_change[resource_types[_]] > 0
}
violation["bucket logging should be enabled "] {
s3_logging_change[resource_types[_]] > 0
}
# Validte each compliance rule.
# Enforce S3 bucket region to eu-central-1
s3_region_change[resource_type] = num {
some resource_type
resource_types[resource_type]
all := resources[resource_type]
creates := [res | res:= all[_]; res.change.after.region != "eu-central-1"]
num := count(creates)
}
# S3 acl property , bucket ACL can't be public unless its is a website hosting bucket.
s3_acl_change[resource_type] = num {
some resource_type
resource_types[resource_type]
all := resources[resource_type]
modifies := [res | res:= all[_]; res.change.after.acl == "public"; res.change.after.website != null]
num := count(modifies)
}
# S3 Excryption , should be either AES256 or KMS
s3_encryption_change[resource_type] = num {
some resource_type
resource_types[resource_type]
all := resources[resource_type]
#check if encryption is used.
modifies := [res | res:= all[_]; not res.change.after.server_side_encryption_configuration[0].rule]
# check for specific type of encryption
#modifies := [res | res:= all[_]; res.change.after.server_side_encryption_configuration[_].rule[_].apply_server_side_encryption_by_default[_].sse_algorithm != "AES256";
# res.change.after.server_side_encryption_configuration[_].rule[_].apply_server_side_encryption_by_default[_].sse_algorithm != "aws:kms"]
num := count(modifies)
trace("Hello There!")
}
# S3 missing tags - refer to variable minimum_tags
s3_tags_change[resource_type] = num {
some resource_type
resource_types[resource_type]
all := resources[resource_type]
modifies := [res | res:= all[_]; not tags_contain_proper_keys(res.change.after.tags)]
num := count(modifies)
}
# S3 logging should be enabled.
s3_logging_change[resource_type] = num {
some resource_type
resource_types[resource_type]
all := resources[resource_type]
modifies := [res | res:= all[_]; not res.change.after.logging[0].target_bucket]
num := count(modifies)
}
#helper functions
tags_contain_proper_keys(tags) {
keys := {key | tags[key]}
leftover := minimum_tags - keys
leftover == set()
}
#contains(arr, elem) {
# arr[_] = elem
#}