# 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 }