Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CIS 4.07: Shielded VMs #237

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions policies/templates/gcp_compute_shielded_v1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
apiVersion: templates.gatekeeper.sh/v1alpha1
kind: ConstraintTemplate
metadata:
name: gcp-compute-shielded-v1
spec:
crd:
spec:
names:
kind: GCPComputeShieldedConstraintV1
plural: gcpcomputeshieldedconstraintsv1
validation:
openAPIV3Schema:
properties:
mode:
charliewolf marked this conversation as resolved.
Show resolved Hide resolved
type: string
enum: [blacklist, whitelist]
match_mode:
type: string
enum: [exact, regex]
instances:
type: array
items: string
targets:
validation.gcp.forsetisecurity.org:
rego: | #INLINE("validator/compute_shielded.rego")
#
# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

package templates.gcp.GCPComputeShieldedConstraintV1

import data.validator.gcp.lib as lib

###########################
# Find Whitelist/Blacklist Violations
###########################
deny[{
"msg": message,
"details": metadata,
}] {
constraint := input.constraint
lib.get_constraint_params(constraint, params)
asset := input.asset
asset.asset_type == "compute.googleapis.com/Instance"

instance := asset.resource.data
shielded_config := lib.get_default(instance, "shieldedInstanceConfig", {})
lib.get_default(shielded_config, "enableIntegrityMonitoring", false) != true
lib.get_default(shielded_config, "enableVtpm", false) != true

# Check if instance is in blacklist/whitelist
match_mode := lib.get_default(params, "match_mode", "exact")
mode := lib.get_default(params, "mode", "whitelist")
target_instances := lib.get_default(params, "instances", [])
trace(sprintf("asset name:%v, target_instances: %v, mode: %v, match_mode: %v", [asset.name, target_instances, mode, match_mode]))
instance_name_targeted(asset.name, target_instances, mode, match_mode)
message := sprintf("%v is not shielded.", [asset.name])
metadata := {"resource": asset.name}
}

###########################
# Rule Utilities
###########################
instance_name_targeted(asset_name, instance_filters, mode, match_mode) {
mode == "whitelist"
match_mode == "exact"
matches := {asset_name} & cast_set(instance_filters)
count(matches) == 0
}

instance_name_targeted(asset_name, instance_filters, mode, match_mode) {
mode == "blacklist"
match_mode == "exact"
matches := {asset_name} & cast_set(instance_filters)
count(matches) > 0
}

instance_name_targeted(asset_name, instance_filters, mode, match_mode) {
mode == "whitelist"
match_mode == "regex"
not re_match_name(asset_name, instance_filters)
}

instance_name_targeted(asset_name, instance_filters, mode, match_mode) {
mode == "blacklist"
match_mode == "regex"
re_match_name(asset_name, instance_filters)
}

re_match_name(name, filters) {
re_match(filters[_], name)
}
#ENDINLINE
24 changes: 24 additions & 0 deletions samples/compute_shielded.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
apiVersion: constraints.gatekeeper.sh/v1alpha1
kind: GCPComputeIpForwardConstraintV1
metadata:
name: forbid_ip_forward
annotations:
# This constraint is not certified by CIS.
bundles.validator.forsetisecurity.org/cis-v1.1: 4.07
spec:
severity: high
parameters: {}
79 changes: 79 additions & 0 deletions validator/compute_shielded.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#
# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

package templates.gcp.GCPComputeShieldedConstraintV1

import data.validator.gcp.lib as lib

###########################
# Find Whitelist/Blacklist Violations
###########################
deny[{
"msg": message,
"details": metadata,
}] {
constraint := input.constraint
lib.get_constraint_params(constraint, params)
asset := input.asset
asset.asset_type == "compute.googleapis.com/Instance"

instance := asset.resource.data
shielded_config := lib.get_default(instance, "shieldedInstanceConfig", {})
lib.get_default(shielded_config, "enableIntegrityMonitoring", false) != true
lib.get_default(shielded_config, "enableVtpm", false) != true

# Check if instance is in blacklist/whitelist
match_mode := lib.get_default(params, "match_mode", "exact")
mode := lib.get_default(params, "mode", "whitelist")
target_instances := lib.get_default(params, "instances", [])
trace(sprintf("asset name:%v, target_instances: %v, mode: %v, match_mode: %v", [asset.name, target_instances, mode, match_mode]))
instance_name_targeted(asset.name, target_instances, mode, match_mode)
message := sprintf("%v is not shielded.", [asset.name])
metadata := {"resource": asset.name}
}

###########################
# Rule Utilities
###########################
instance_name_targeted(asset_name, instance_filters, mode, match_mode) {
mode == "whitelist"
match_mode == "exact"
matches := {asset_name} & cast_set(instance_filters)
count(matches) == 0
}

instance_name_targeted(asset_name, instance_filters, mode, match_mode) {
mode == "blacklist"
match_mode == "exact"
matches := {asset_name} & cast_set(instance_filters)
count(matches) > 0
}

instance_name_targeted(asset_name, instance_filters, mode, match_mode) {
mode == "whitelist"
match_mode == "regex"
not re_match_name(asset_name, instance_filters)
}

instance_name_targeted(asset_name, instance_filters, mode, match_mode) {
mode == "blacklist"
match_mode == "regex"
re_match_name(asset_name, instance_filters)
}

re_match_name(name, filters) {
re_match(filters[_], name)
}
164 changes: 164 additions & 0 deletions validator/compute_shielded_test.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
#
# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

package templates.gcp.GCPComputeShieldedConstraintV1

import data.test.fixtures.compute_shielded.assets as fixture_instances
import data.test.fixtures.compute_shielded.constraints as fixture_constraints

# Find all violations on our test cases
find_violations[violation] {
instance := data.instances[_]
constraint := data.test_constraints[_]

issues := deny with input.asset as instance
with input.constraint as constraint

total_issues := count(issues)

violation := issues[_]
}

# Confim no violations with no instances
test_shielded_no_instances {
found_violations := find_violations with data.instances as []

count(found_violations) = 0
}

test_shielded_no_constraints {
found_violations := find_violations with data.instances as fixture_instances
with data.constraints as []

count(found_violations) = 0
}

violations_with_empty_parameters[violation] {
constraints := [fixture_constraints.shielded_default]

found_violations := find_violations with data.instances as fixture_instances
with data.test_constraints as constraints

violation := found_violations[_]
}

test_shielded_default {
found_violations := violations_with_empty_parameters

count(found_violations) = 2
}

whitelist_violations[violation] {
constraints := [fixture_constraints.shielded_whitelist]

found_violations := find_violations with data.instances as fixture_instances
with data.test_constraints as constraints

violation := found_violations[_]
}

# Confirm only a single violation was found (whitelist constraint)
test_shielded_whitelist_violates_one {
found_violations := whitelist_violations

count(found_violations) = 1

violation := found_violations[_]
resource_name := "//compute.googleapis.com/projects/test-project/zones/us-east1-b/instances/vm-unshielded"

is_string(violation.msg)
is_object(violation.details)
}

no_violation_due_to_whitelist[violation] {
constraints := [fixture_constraints.shielded_whitelist_all]

found_violations := find_violations with data.instances as fixture_instances
with data.test_constraints as constraints

violation := found_violations[_]
}

# Confirm no violation when both VMs with exernal IP are whitelisted.
test_shielded_whitelist_all {
found_violations := no_violation_due_to_whitelist

count(found_violations) = 0
}

blacklist_violations[violation] {
constraints := [fixture_constraints.shielded_blacklist]

found_violations := find_violations with data.instances as fixture_instances
with data.test_constraints as constraints

violation := found_violations[_]
}

test_shielded_blacklist_violates_one {
found_violations := blacklist_violations

count(found_violations) = 1
}

two_blacklist_violations[violation] {
constraints := [fixture_constraints.shielded_blacklist_all]

found_violations := find_violations with data.instances as fixture_instances
with data.test_constraints as constraints

violation := found_violations[_]
}

# Confirm we get 2 violations when both VMs with external IP are blacklisted.
test_shielded_blacklist_all {
found_violations := two_blacklist_violations

count(found_violations) = 2
}

test_blacklist_violations_regex {
constraints := [fixture_constraints.shielded_regex_blacklist_all]

found_violations := find_violations with data.instances as fixture_instances
with data.test_constraints as constraints

count(found_violations) == 2
}

test_whitelist_violations_regex {
constraints := [fixture_constraints.shielded_regex_whitelist_all]

found_violations := find_violations with data.instances as fixture_instances
with data.test_constraints as constraints

count(found_violations) == 0
}

test_instance_name_targeted_whitelist {
not instance_name_targeted("//compute/vm1", ["//compute/vm.*", "//compute/nomatch"], "whitelist", "regex")
not instance_name_targeted("//compute/vm1", ["//compute/vm1", "//compute/nomatch"], "whitelist", "exact")
}

test_instance_name_targeted_whitelist_nomatch {
instance_name_targeted("//compute/vm1", ["//compute/nomatch1", "//compute/nomatch2"], "whitelist", "regex")
instance_name_targeted("//compute/vm1", ["//compute/nomatch1", "//compute/nomatch2"], "whitelist", "exact")
}

test_instance_name_targeted_blacklist {
instance_name_targeted("//compute/vm1", ["//compute/vm.*", "//compute/nomatch"], "blacklist", "regex")
instance_name_targeted("//compute/vm1", ["//compute/vm1", "//compute/nomatch"], "blacklist", "exact")
}
Loading