Skip to content

Commit b2bc315

Browse files
committed
feat: cis_monitoring rules
1 parent c773d31 commit b2bc315

File tree

3 files changed

+323
-0
lines changed

3 files changed

+323
-0
lines changed

cis-oci-benchmark/cis_mon_rules.rego

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package cis.monitoring
2+
3+
import rego.v1
4+
5+
import input as tfplan
6+
7+
########################
8+
# Parameters for Policy
9+
########################
10+
11+
# acceptable score for automated authorization
12+
blast_radius := 7000
13+
14+
# weights assigned for each operation on each resource-type
15+
weights := {
16+
17+
# IAM Resources
18+
"oci_identity_policy": {"delete": 80, "create": 50, "modify" : 30},
19+
"oci_identity_user_group_membership": {"delete": 80, "create": 50, "modify" : 30},
20+
"oci_identity_group": {"delete": 80, "create": 50, "modify" : 30},
21+
"oci_identity_dynamic_group": {"delete": 80, "create": 50, "modify" : 30},
22+
"oci_identity_user": {"delete": 80, "create": 50, "modify" : 30},
23+
"oci_identity_authentication_policy": {"delete": 80, "create": 50, "modify" : 30},
24+
"oci_identity_domains_password_policy": {"delete": 80, "create": 50, "modify" : 30},
25+
# Network resources
26+
"oci_core_vcn": {"delete": 80, "create": 50, "modify" : 30},
27+
"oci_core_route_table": {"delete": 80, "create": 50, "modify" : 30},
28+
"oci_core_security_list": {"delete": 80, "create": 50, "modify" : 30},
29+
"oci_core_network_security_group": {"delete": 80, "create": 50, "modify" : 30},
30+
"oci_core_network_security_group_security_rule": {"delete": 80, "create": 50, "modify" : 30},
31+
"oci_core_drg": {"delete": 80, "create": 50, "modify" : 30},
32+
"oci_core_drg_attachment": {"delete": 80, "create": 50, "modify" : 30},
33+
"oci_core_internet_gateway": {"delete": 80, "create": 50, "modify" : 30},
34+
"oci_core_local_peering_gateway": {"delete": 80, "create": 50, "modify" : 30},
35+
"oci_core_nat_gateway": {"delete": 80, "create": 50, "modify" : 30},
36+
"oci_core_service_gateway": {"delete": 80, "create": 50, "modify" : 30}
37+
38+
}
39+
40+
# Consider exactly these resource types in calculations
41+
cis_mon_types := {
42+
# IAM Resources
43+
"oci_identity_policy",
44+
"oci_identity_user_group_membership",
45+
"oci_identity_group",
46+
"oci_identity_dynamic_group",
47+
"oci_identity_user",
48+
"oci_identity_authentication_policy",
49+
"oci_identity_domains_password_policy",
50+
# Network resources
51+
"oci_core_vcn",
52+
"oci_core_route_table",
53+
"oci_core_security_list",
54+
"oci_core_network_security_group",
55+
"oci_core_network_security_group_security_rule",
56+
"oci_core_drg",
57+
"oci_core_drg_attachment",
58+
"oci_core_internet_gateway",
59+
"oci_core_local_peering_gateway",
60+
"oci_core_nat_gateway",
61+
"oci_core_service_gateway"
62+
}
63+
64+
#########
65+
# Policy
66+
#########
67+
68+
# Authorization holds if score for the plan is acceptable and no changes are made to IAM
69+
default authz := false
70+
71+
authz if {
72+
score < blast_radius
73+
}
74+
75+
# Compute the score for a Terraform plan as the weighted sum of deletions, creations, modifications
76+
score := s if {
77+
all := [x |
78+
some resource_type
79+
crud := weights[resource_type]
80+
del := crud["delete"] * num_deletes[resource_type]
81+
new := crud["create"] * num_creates[resource_type]
82+
mod := crud["modify"] * num_modifies[resource_type]
83+
x := (del + new) + mod
84+
]
85+
s := sum(all)
86+
}
87+
88+
89+
90+
####################
91+
# Terraform Library
92+
####################
93+
94+
# list of all resources of a given type
95+
resources[resource_type] := all if {
96+
some resource_type
97+
cis_mon_types[resource_type]
98+
all := [name |
99+
name := tfplan.resource_changes[_]
100+
name.type == resource_type
101+
]
102+
}
103+
104+
# number of creations of resources of a given type
105+
num_creates[resource_type] := num if {
106+
some resource_type
107+
cis_mon_types[resource_type]
108+
all := resources[resource_type]
109+
creates := [res | res := all[_]; res.change.actions[_] == "create"]
110+
num := count(creates)
111+
}
112+
113+
# number of deletions of resources of a given type
114+
num_deletes[resource_type] := num if {
115+
some resource_type
116+
cis_mon_types[resource_type]
117+
all := resources[resource_type]
118+
deletions := [res | res := all[_]; res.change.actions[_] == "delete"]
119+
num := count(deletions)
120+
}
121+
122+
# number of modifications to resources of a given type
123+
num_modifies[resource_type] := num if {
124+
some resource_type
125+
cis_mon_types[resource_type]
126+
all := resources[resource_type]
127+
modifies := [res | res := all[_]; res.change.actions[_] == "update"]
128+
num := count(modifies)
129+
}

core-lz-tf-plan.json

+1
Large diffs are not rendered by default.

notes.md

+193
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
# Current rules
2+
3+
Run cis_mon_rules.regio
4+
`opa exec --decision cis/monitoring/score --bundle cis-oci-benchmark/ core-lz-tf-plan.json`
5+
6+
# Basic Policy
7+
## Package Name
8+
```
9+
package <name>
10+
```
11+
12+
## Basic policy, default is to return `true`
13+
```
14+
default <policy-name> <result>
15+
<policy-name> = <result> {
16+
<condition>
17+
}
18+
```
19+
## Sample policy no INPUT
20+
```
21+
package policy
22+
default testing = false
23+
testing = true {
24+
1 == 1
25+
}
26+
27+
```
28+
## Sample policy with input
29+
30+
```
31+
package policy
32+
default testing2 = false
33+
testing2 = true {
34+
input.user.roles[_] == "admin" # iterating through multiple roles in an input of roles
35+
input.user.roles[i] == # if we cared about the index
36+
37+
}
38+
39+
40+
```
41+
42+
43+
## Basic Package example
44+
### Two Globals
45+
`--data` - contains all polices fed to policy analyzer
46+
`--input` - input
47+
48+
`opa eval --data <rego file name> data.<package name>.rule>`
49+
### Example All data
50+
`opa eval --data policy.rego data.policy.testing`
51+
52+
## Example outputs just result
53+
`opa eval --format raw --data policy.rego 'data.policy.testing'`
54+
55+
## With a file as input
56+
`opa eval --format raw --data policy.rego --input user.json 'data.policy.testing2'`
57+
58+
59+
# Basic Policy Testing Package
60+
## Notes
61+
- Test policies should start with `test_`
62+
- best practice to put them in a seperate package
63+
- import the policy you want to test
64+
65+
## Example Test Package
66+
- Without any input provide testing2 should be false
67+
68+
```
69+
package policy_test
70+
71+
import data.policy.testing2
72+
73+
test_testing2_is_false_by_default {
74+
testing2 == false
75+
}
76+
77+
```
78+
## Cleaner
79+
```
80+
package policy_test
81+
82+
import data.policy.testing2
83+
84+
test_testing2_is_false_by_default {
85+
not testing2
86+
}
87+
88+
```
89+
90+
91+
## Execute test
92+
`opa test *.rego`
93+
94+
95+
96+
## Examples
97+
98+
```resource_types := {"aws_autoscaling_group", "aws_instance", "aws_iam", "aws_launch_configuration"}
99+
100+
resources[resource_type] := all {
101+
some resource_type
102+
resource_types[resource_type]
103+
all := [name |
104+
name:= tfplan.resource_changes[_]
105+
name.type == resource_type
106+
]
107+
}
108+
```
109+
110+
111+
```
112+
Here's a pseudo-code explanation of what this code does:
113+
114+
Initialize a set called resource_types with the names of resource types that you want to manage, such as "aws_autoscaling_group," "aws_instance," "aws_iam," and "aws_launch_configuration."
115+
116+
Initialize an empty dictionary called resources.
117+
118+
Iterate over each resource_type in the resource_types set:
119+
120+
a. For each resource_type, create a sub-set of resources called all.
121+
122+
b. In this sub-set, filter out resource names (name) from a source called tfplan.resource_changes.
123+
124+
c. Only include name if it has a type matching the current resource_type.
125+
126+
d. Store this filtered list of resource names as the value for the current resource_type in the resources dictionary.
127+
```
128+
129+
## Sample 1
130+
### Code
131+
```
132+
# Define the rule to find the type of bucket in the Terraform plan
133+
find_bucket_type[resource_type] {
134+
# Iterate over each change in the Terraform plan
135+
change := input.resource_changes[_]
136+
137+
# Check if the change is related to an AWS S3 bucket
138+
change.type == "aws_s3_bucket"
139+
140+
# Extract the resource type (in this case, it's "aws_s3_bucket")
141+
resource_type := change.type
142+
}
143+
```
144+
### Meaing
145+
```In this Rego policy:
146+
147+
The package is named find_bucket_type, and it contains a rule named find_bucket_type.
148+
149+
The rule iterates through each change in the Terraform plan using _ as the iterator variable.
150+
151+
It checks if the change.type is equal to "aws_s3_bucket". This condition identifies changes related to AWS S3 buckets.
152+
153+
If the condition is met, it sets resource_type to "aws_s3_bucket".
154+
155+
Now, you can use this Rego policy to find AWS S3 bucket changes in a Terraform JSON plan by passing the plan as input to the Rego policy evaluator. The find_bucket_type rule will return the resource type when it encounters an AWS S3 bucket resource in the plan.
156+
157+
Please note that this Rego policy assumes that the Terraform JSON plan follows the typical structure where each resource change has a type field indicating the resource type. You may need to adapt the policy to your specific Terraform plan format if it differs.```
158+
159+
## Sample 2
160+
### Code
161+
```
162+
package find_public_s3_buckets
163+
164+
# Define the rule to find AWS S3 buckets with PublicAccess
165+
public_s3_bucket[resource_name] {
166+
# Iterate through each change in the Terraform plan
167+
change := input.resource_changes[_]
168+
169+
# Check if the change is related to an AWS S3 bucket
170+
change.type == "aws_s3_bucket"
171+
172+
# Extract the resource name (the AWS S3 bucket name)
173+
resource_name := change.name
174+
175+
# Check if the access_type is PublicAccess
176+
change.change.after.access_type == "PublicAccess"
177+
}
178+
```
179+
180+
### Explain
181+
1. The package is named find_public_s3_buckets, and it contains a rule named public_s3_bucket.
182+
183+
2. The rule iterates through each change in the Terraform plan using _ as the iterator variable.
184+
185+
3. It checks if the change.type is equal to "aws_s3_bucket". This condition identifies changes related to AWS S3 buckets.
186+
187+
4. If the condition is met, it extracts the AWS S3 bucket's name as resource_name.
188+
189+
5. It further checks if the access_type in the change.change.after field is equal to "PublicAccess". This condition identifies AWS S3 buckets with public access.
190+
191+
Now, you can use this modified Rego policy to find AWS S3 buckets with public access in a Terraform JSON plan by passing the plan as input to the Rego policy evaluator. The public_s3_bucket rule will return the names of the AWS S3 buckets with public access when it encounters such resources in the plan.
192+
193+
Please make sure that the structure of your Terraform JSON plan includes the access_type field as indicated in the policy for this to work correctly.

0 commit comments

Comments
 (0)