Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
29 changes: 29 additions & 0 deletions pkg/securitypolicy/fragment_test_policies/platform_rules.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package fragment

svn := "1"
framework_version := "0.5.0"

platform_rules := [
{
"env_rules": [
{
"name": "(?i)(FABRIC)_.+",
"name_strategy": "re2",
"value": ".+",
"value_strategy": "re2"
}
],
"mounts": [
{
"destination": "/var/run/secrets/kubernetes.io/serviceaccount",
"options": [
"rbind",
"rshared",
"ro"
],
"source": "sandbox:///tmp/atlas/emptydir/.+",
"type": "bind"
}
]
}
]
144 changes: 120 additions & 24 deletions pkg/securitypolicy/framework.rego
Original file line number Diff line number Diff line change
Expand Up @@ -146,32 +146,40 @@ overlay_mounted(target) {
data.metadata.overlayTargets[target]
}

default candidate_containers := []
# Note that a valid policy might not even define (data.policy.)containers, if
# all its containers are coming from fragments. This default rule prevents
# breaking other rules in this case.
default policy_containers := []

candidate_containers := containers {
policy_containers := pc {
semver.compare(policy_framework_version, version) == 0

policy_containers := [c | c := data.policy.containers[_]]
fragment_containers := [c |
feed := data.metadata.issuers[_].feeds[_]
fragment := feed[_]
c := fragment.containers[_]
]

containers := array.concat(policy_containers, fragment_containers)
pc := data.policy.containers
}

candidate_containers := containers {
policy_containers := pc {
semver.compare(policy_framework_version, version) < 0
pc := apply_defaults("container", data.policy.containers, policy_framework_version)
}

policy_containers := apply_defaults("container", data.policy.containers, policy_framework_version)
candidate_containers := containers {
fragment_containers := [c |
feed := data.metadata.issuers[_].feeds[_]
fragment := feed[_]
c := fragment.containers[_]
]

containers := array.concat(policy_containers, fragment_containers)
containers_raw := array.concat(policy_containers, fragment_containers)

# Each container definition applied with platform_rules might turn into
# multiple containers if multiple platforms are allowed. We flatten the
# result here.
after_platform_rules := [c2 |
c1 := containers_raw[_]
applied := apply_platform_rules("container", c1)
c2 := applied[_]
]

containers := after_platform_rules
}

default mount_cims := {"allowed": false}
Expand Down Expand Up @@ -297,21 +305,26 @@ env_rule_ok(rule, env) {
env_pattern_ok(rule_value, value_strategy, env_value)
}

rule_ok(rule, env) {
not rule.required
}

rule_ok(rule, env) {
# For a required env rule, check that envList contains a matching env var for
# it.
env_required_rule_ok(rule, envList) {
rule.required
some env in envList
env_rule_ok(rule, env)
}

# If it's not required, skip the check
env_required_rule_ok(rule, envList) {
not rule.required
}

envList_ok(env_rules, envList) {
# Check that all required rules are satisfied
every rule in env_rules {
some env in envList
rule_ok(rule, env)
env_required_rule_ok(rule, envList)
}

# Check that any env provided is allowed
every env in envList {
some rule in env_rules
env_rule_ok(rule, env)
Expand Down Expand Up @@ -1194,21 +1207,28 @@ runtime_logging := {"allowed": true} {
allow_runtime_logging
}

default fragment_containers := []
# Helpers to get data from the fragment that is currently being loaded. Since
# input.namespace is the package name the fragment loaded as,
# data[input.namespace] can be used to access the fragment. This is only valid
# during a load_fragment call - the content exported by the fragment needs to be
# persisted into the metadata for later enforcement use. (c.f.
# extract_fragment_includes)

default fragment_containers := []
fragment_containers := data[input.namespace].containers

default fragment_fragments := []

fragment_fragments := data[input.namespace].fragments

default fragment_external_processes := []

fragment_external_processes := data[input.namespace].external_processes

default fragment_transparency_trust_lists := []
fragment_transparency_trust_lists := data[input.namespace].transparency_trust_lists

default fragment_platform_rules := []
fragment_platform_rules := data[input.namespace].platform_rules

apply_defaults(name, raw_values, framework_version) := values {
semver.compare(framework_version, version) == 0
values := raw_values
Expand Down Expand Up @@ -1253,6 +1273,22 @@ apply_defaults("transparency_trust_lists", raw_values, framework_version) := val
values := []
}

# platform_rules is introduced in framework version 0.5.0. If an old policy has it,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to describe it as rego code like api.rego?
It would be useful if we can describe that platform_rules, platform_rules.env_rules and platform_rules.mounts are introducein 0.5.0.

# silently ignore as it might be using the name for something else.

apply_defaults("platform_rules", raw_values, framework_version) := values {
semver.compare(framework_version, version) < 0
semver.compare(framework_version, "0.5.0") >= 0
# This is currently unreachable, otherwise we would call something like
# check_platform_rule here, like above (not defined yet).
values := raw_values
}

apply_defaults("platform_rules", raw_values, framework_version) := values {
semver.compare(framework_version, "0.5.0") < 0
values := []
}

default fragment_framework_version := null
fragment_framework_version := data[input.namespace].framework_version

Expand All @@ -1263,6 +1299,7 @@ extract_fragment_includes(includes) := fragment {
"fragments": apply_defaults("fragment", fragment_fragments, framework_version),
"external_processes": apply_defaults("external_process", fragment_external_processes, framework_version),
"transparency_trust_lists": apply_defaults("transparency_trust_lists", fragment_transparency_trust_lists, framework_version),
"platform_rules": apply_defaults("platform_rules", fragment_platform_rules, framework_version),
}

fragment := {
Expand Down Expand Up @@ -1768,6 +1805,65 @@ extract_parameter(name, fragment_parameters_obj, parameters_metadata) := fragmen
"default" in object.keys(parameters_metadata[name])
}

default policy_platform_rules := []

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be useful to document the format of platform rules here.
Also maybe put an example like you did for env_pattern_ok.


policy_platform_rules := platform_rules {
semver.compare(policy_framework_version, version) == 0
platform_rules := data.policy.platform_rules
}

# For policy with framework_version < 0.5.0, apply_defaults will ignore
# platform_rules and return [].

policy_platform_rules := platform_rules {
semver.compare(policy_framework_version, version) < 0
platform_rules := apply_defaults("platform_rules", data.policy.platform_rules, policy_framework_version)
}

default candidate_platform_rules := []

candidate_platform_rules := platform_rules {
fragment_platform_rules := [r |
feed := data.metadata.issuers[_].feeds[_]
fragment := feed[_]
r := fragment.platform_rules[_]
]

platform_rules := array.concat(policy_platform_rules, fragment_platform_rules)
}

# apply_platform_rules("container", c) applies "platform_rules" to the container
# object c, and returns a list of containers which are the input container with
# platform rules applied. The return value may contain more than one container
# if multiple platform rules are defined.

# No platform rules - return as-is.
apply_platform_rules("container", container) := updated_containers {
count(candidate_platform_rules) == 0
updated_containers := [container]
}

apply_platform_rules("container", container) := updated_containers {
count(candidate_platform_rules) > 0
updated_containers := [updated_container |
platform_rule := candidate_platform_rules[_]
updated_container := apply_single_platform_rule("container", container, platform_rule)
]
}

apply_single_platform_rule("container", container, platform_rule) := updated_container {
container_env_rules := object.get(container, "env_rules", [])
updated_env_rules := array.concat(container_env_rules, object.get(platform_rule, "env_rules", []))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it correct that we are ignoring duplication here because it doesn't harm?


container_mounts := object.get(container, "mounts", [])
updated_mounts := array.concat(container_mounts, object.get(platform_rule, "mounts", []))

updated_container := object.union(container, {
"env_rules": updated_env_rules,
"mounts": updated_mounts,
})
}

reason := {
"errors": errors,
"error_objects": error_objects
Expand Down
108 changes: 108 additions & 0 deletions pkg/securitypolicy/policy_with_platform_rules.rego

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it should be in fragment_test_policies or in a similar direcotry so that people can tell it's for test?

Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package policy

api_version := "@@API_VERSION@@"
framework_version := "@@FRAMEWORK_VERSION@@"

fragments := [
{
"feed": "@@FRAGMENT_FEED@@",
"includes": [
"containers",
"fragments"
],
"issuer": "@@FRAGMENT_ISSUER@@",
"minimum_svn": "0"
}
]

platform_rules := [
{
"env_rules": [
{
"name": "(?i)(FABRIC)_.+",
"name_strategy": "re2",
"value": ".+",
"value_strategy": "re2"
}
],
"mounts": [
{
"destination": "/var/run/secrets/kubernetes.io/serviceaccount",
"options": [
"rbind",
"rshared",
"ro"
],
"source": "sandbox:///tmp/atlas/emptydir/.+",
"type": "bind"
}
]
}
]

containers := [
{
"allow_elevated": false,
"allow_stdio_access": true,
"capabilities": {
"ambient": [],
"bounding": [],
"effective": [],
"inheritable": [],
"permitted": []
},
"command": [ "bash" ],
"env_rules": [],
"exec_processes": [],
"layers": [
"0000000000000000000000000000000000000000000000000000000000000000",
],
"mounts": [],
"no_new_privileges": false,
"seccomp_profile_sha256": "",
"signals": [],
"user": {
"group_idnames": [
{
"pattern": "",
"strategy": "any"
}
],
"umask": "0022",
"user_idname": {
"pattern": "",
"strategy": "any"
}
},
"working_dir": "/"
}
]

allow_properties_access := true
allow_dump_stacks := false
allow_runtime_logging := false
allow_environment_variable_dropping := true
allow_unencrypted_scratch := false
allow_capability_dropping := true

mount_device := data.framework.mount_device
rw_mount_device := data.framework.rw_mount_device
unmount_device := data.framework.unmount_device
rw_unmount_device := data.framework.rw_unmount_device
mount_overlay := data.framework.mount_overlay
unmount_overlay := data.framework.unmount_overlay
mount_cims := data.framework.mount_cims
create_container := data.framework.create_container
exec_in_container := data.framework.exec_in_container
exec_external := data.framework.exec_external
shutdown_container := data.framework.shutdown_container
signal_container_process := data.framework.signal_container_process
plan9_mount := data.framework.plan9_mount
plan9_unmount := data.framework.plan9_unmount
get_properties := data.framework.get_properties
dump_stacks := data.framework.dump_stacks
runtime_logging := data.framework.runtime_logging
load_fragment := data.framework.load_fragment
scratch_mount := data.framework.scratch_mount
scratch_unmount := data.framework.scratch_unmount
reason := data.framework.reason
Loading
Loading