Add OPA and Openshift AI
This commit is contained in:
65
roles/opa_server/files/policies/aap_policy.rego
Normal file
65
roles/opa_server/files/policies/aap_policy.rego
Normal file
@@ -0,0 +1,65 @@
|
||||
# AAP policy-as-code baseline.
|
||||
# Set opa_query_path to aap/policy/decision on an organization, inventory, or
|
||||
# job template. Input is the serialized Job (awx/main/tasks/policy.py).
|
||||
# Tunables come from data.aap_policy_config (opa_server_policy_config).
|
||||
package aap.policy
|
||||
|
||||
import rego.v1
|
||||
|
||||
decision := {
|
||||
"allowed": count(violations) == 0,
|
||||
"violations": sort([msg | some msg in violations]),
|
||||
}
|
||||
|
||||
default_config := {
|
||||
"allowed_ee_registries": [],
|
||||
"forbidden_extra_vars": [],
|
||||
"change_freeze": false,
|
||||
"change_freeze_exempt_teams": [],
|
||||
}
|
||||
|
||||
default user_config := {}
|
||||
|
||||
user_config := data.aap_policy_config
|
||||
|
||||
config := object.union(default_config, user_config)
|
||||
|
||||
# Execution environment must come from an approved registry
|
||||
violations contains msg if {
|
||||
count(config.allowed_ee_registries) > 0
|
||||
image := input.execution_environment.image
|
||||
not approved_registry(image)
|
||||
msg := sprintf(
|
||||
"Execution environment image %q is not from an approved registry (%s)",
|
||||
[image, concat(", ", config.allowed_ee_registries)],
|
||||
)
|
||||
}
|
||||
|
||||
approved_registry(image) if {
|
||||
some prefix in config.allowed_ee_registries
|
||||
startswith(image, prefix)
|
||||
}
|
||||
|
||||
# Credentials belong in AAP credentials, not launch-time extra vars
|
||||
violations contains msg if {
|
||||
extra_vars := object.get(input, "extra_vars", {})
|
||||
is_object(extra_vars)
|
||||
some key, _ in extra_vars
|
||||
lower(key) in {lower(k) | some k in config.forbidden_extra_vars}
|
||||
msg := sprintf("Extra variable %q may not be passed at launch; use a credential", [key])
|
||||
}
|
||||
|
||||
# Change freeze: only check-mode jobs unless the launcher is exempt
|
||||
violations contains msg if {
|
||||
config.change_freeze
|
||||
input.job_type == "run"
|
||||
not freeze_exempt
|
||||
msg := "A change freeze is in effect: only check-mode jobs are allowed"
|
||||
}
|
||||
|
||||
freeze_exempt if input.created_by.is_superuser
|
||||
|
||||
freeze_exempt if {
|
||||
some team in input.created_by.teams
|
||||
team.name in config.change_freeze_exempt_teams
|
||||
}
|
||||
8
roles/opa_server/files/policies/aap_smoke.rego
Normal file
8
roles/opa_server/files/policies/aap_smoke.rego
Normal file
@@ -0,0 +1,8 @@
|
||||
# Wiring checks for AAP policy enforcement.
|
||||
# aap/smoke/allow_all - always passes (safe first attachment)
|
||||
# aap/smoke/deny_all - always blocks, proving enforcement is active
|
||||
package aap.smoke
|
||||
|
||||
allow_all := {"allowed": true, "violations": []}
|
||||
|
||||
deny_all := {"allowed": false, "violations": ["aap.smoke.deny_all: policy enforcement is active"]}
|
||||
11
roles/opa_server/files/policies/authz.rego
Normal file
11
roles/opa_server/files/policies/authz.rego
Normal file
@@ -0,0 +1,11 @@
|
||||
# Server authorization (opa run --authorization=basic).
|
||||
# Health checks are open; everything else needs the bearer token.
|
||||
package system.authz
|
||||
|
||||
import rego.v1
|
||||
|
||||
default allow := false
|
||||
|
||||
allow if input.path == ["health"]
|
||||
|
||||
allow if input.identity == data.opa_authz.token
|
||||
87
roles/opa_server/files/tests/aap_policy_test.rego
Normal file
87
roles/opa_server/files/tests/aap_policy_test.rego
Normal file
@@ -0,0 +1,87 @@
|
||||
# Unit tests (not deployed). Run with:
|
||||
# podman run --rm -v ./roles/opa_server/files:/files:Z docker.io/openpolicyagent/opa:1.21.1-static \
|
||||
# test /files/policies /files/tests -v
|
||||
package aap.policy_test
|
||||
|
||||
import rego.v1
|
||||
|
||||
import data.aap.policy
|
||||
|
||||
cfg := {
|
||||
"allowed_ee_registries": ["aap.toal.ca/", "registry.redhat.io/"],
|
||||
"forbidden_extra_vars": ["ansible_password"],
|
||||
"change_freeze": false,
|
||||
"change_freeze_exempt_teams": ["Platform"],
|
||||
}
|
||||
|
||||
job := {
|
||||
"job_type": "run",
|
||||
"execution_environment": {"image": "aap.toal.ca/toallab/ee-demo:latest"},
|
||||
"extra_vars": {"target": "web"},
|
||||
"created_by": {"username": "dev", "is_superuser": false, "teams": [{"id": 1, "name": "Dev"}]},
|
||||
}
|
||||
|
||||
test_compliant_job_allowed if {
|
||||
d := policy.decision with input as job with data.aap_policy_config as cfg
|
||||
d.allowed
|
||||
d.violations == []
|
||||
}
|
||||
|
||||
test_unapproved_registry_denied if {
|
||||
j := object.union(job, {"execution_environment": {"image": "quay.io/scottharwell/cloud-ee:latest"}})
|
||||
d := policy.decision with input as j with data.aap_policy_config as cfg
|
||||
not d.allowed
|
||||
count(d.violations) == 1
|
||||
}
|
||||
|
||||
test_forbidden_extra_var_denied_case_insensitive if {
|
||||
j := object.union(job, {"extra_vars": {"Ansible_Password": "x"}})
|
||||
d := policy.decision with input as j with data.aap_policy_config as cfg
|
||||
not d.allowed
|
||||
}
|
||||
|
||||
test_null_extra_vars_and_ee_allowed if {
|
||||
j := object.union(job, {"extra_vars": null, "execution_environment": null})
|
||||
d := policy.decision with input as j with data.aap_policy_config as cfg
|
||||
d.allowed
|
||||
}
|
||||
|
||||
test_freeze_blocks_run if {
|
||||
d := policy.decision with input as job with data.aap_policy_config as object.union(cfg, {"change_freeze": true})
|
||||
not d.allowed
|
||||
}
|
||||
|
||||
test_freeze_allows_check if {
|
||||
j := object.union(job, {"job_type": "check"})
|
||||
d := policy.decision with input as j with data.aap_policy_config as object.union(cfg, {"change_freeze": true})
|
||||
d.allowed
|
||||
}
|
||||
|
||||
test_freeze_exempt_team if {
|
||||
j := object.union(job, {"created_by": {"is_superuser": false, "teams": [{"name": "Platform"}]}})
|
||||
d := policy.decision with input as j with data.aap_policy_config as object.union(cfg, {"change_freeze": true})
|
||||
d.allowed
|
||||
}
|
||||
|
||||
test_freeze_scheduled_job_without_creator if {
|
||||
j := object.union(job, {"created_by": null})
|
||||
d := policy.decision with input as j with data.aap_policy_config as object.union(cfg, {"change_freeze": true})
|
||||
not d.allowed
|
||||
}
|
||||
|
||||
test_no_config_allows_everything if {
|
||||
j := object.union(job, {"execution_environment": {"image": "quay.io/anything:latest"}})
|
||||
d := policy.decision with input as j
|
||||
d.allowed
|
||||
}
|
||||
|
||||
test_authz_health_open if {
|
||||
data.system.authz.allow with input as {"path": ["health"]}
|
||||
}
|
||||
|
||||
test_authz_requires_token if {
|
||||
not data.system.authz.allow with input as {"path": ["v1", "data"], "identity": "bad"}
|
||||
with data.opa_authz.token as "good"
|
||||
data.system.authz.allow with input as {"path": ["v1", "data"], "identity": "good"}
|
||||
with data.opa_authz.token as "good"
|
||||
}
|
||||
Reference in New Issue
Block a user