built-in-references/Kubernetes/allowed-users-groups/template.yaml (151 lines of code) (raw):

apiVersion: templates.gatekeeper.sh/v1beta1 kind: ConstraintTemplate metadata: name: k8sazureallowedusersgroups spec: crd: spec: names: kind: K8sAzureAllowedUsersGroups validation: openAPIV3Schema: properties: runAsUser: type: object properties: rule: type: string ranges: type: array items: type: object properties: min: type: integer max: type: integer runAsGroup: type: object properties: rule: type: string ranges: type: array items: type: object properties: min: type: integer max: type: integer supplementalGroups: type: object properties: rule: type: string ranges: type: array items: type: object properties: min: type: integer max: type: integer fsGroup: type: object properties: rule: type: string ranges: type: array items: type: object properties: min: type: integer max: type: integer targets: - target: admission.k8s.gatekeeper.sh rego: | package k8sazureallowedusersgroups violation[{"msg": msg}] { fields := ["runAsUser", "runAsGroup", "supplementalGroups", "fsGroup"] field := fields[_] container := input_containers[_] msg := get_type_violation(field, container) } get_type_violation(field, container) = msg { field == "runAsUser" params := input.parameters[field] msg := get_user_violation(params, container) } get_type_violation(field, container) = msg { field != "runAsUser" params := input.parameters[field] msg := get_violation(field, params, container) } # RunAsUser (separate due to "MustRunAsNonRoot") get_user_violation(params, container) = msg { rule := params.rule provided_user := get_field_value("runAsUser", container, input.review) not accept_users(rule, provided_user) msg := sprintf("Container %v is attempting to run as disallowed user %v. Allowed runAsUser: %v", [container.name, provided_user, params]) } get_user_violation(params, container) = msg { not get_field_value("runAsUser", container, input.review) params.rule != "RunAsAny" msg := sprintf("Container %v is attempting to run without a required securityContext/runAsUser. Allowed runAsUser: %v", [container.name, params]) } accept_users("RunAsAny", provided_user) {true} accept_users("MustRunAsNonRoot", provided_user) = res {res := provided_user != 0} accept_users("MustRunAs", provided_user) = res { ranges := input.parameters.runAsUser.ranges res := is_in_range(provided_user, ranges) } # Group Options get_violation(field, params, container) = msg { rule := params.rule provided_value := get_field_value(field, container, input.review) not is_array(provided_value) not accept_value(rule, provided_value, params.ranges) msg := sprintf("Container %v is attempting to run as disallowed group %v. Allowed %v: %v", [container.name, provided_value, field, params]) } # SupplementalGroups is array value get_violation(field, params, container) = msg { rule := params.rule array_value := get_field_value(field, container, input.review) is_array(array_value) provided_value := array_value[_] not accept_value(rule, provided_value, params.ranges) msg := sprintf("Container %v is attempting to run with disallowed supplementalGroups %v. Allowed %v: %v", [container.name, array_value, field, params]) } get_violation(field, params, container) = msg { not get_field_value(field, container, input.review) params.rule == "MustRunAs" msg := sprintf("Container %v is attempting to run without a required securityContext/%v. Allowed %v: %v", [container.name, field, field, params]) } accept_value("RunAsAny", provided_value, ranges) {true} accept_value("MayRunAs", provided_value, ranges) = res { res := is_in_range(provided_value, ranges)} accept_value("MustRunAs", provided_value, ranges) = res { res := is_in_range(provided_value, ranges)} # If container level is provided, that takes precedence get_field_value(field, container, review) = out { container_value := get_seccontext_field(field, container) out := container_value } # If no container level exists, use pod level get_field_value(field, container, review) = out { not get_seccontext_field(field, container) review.kind.kind == "Pod" pod_value := get_seccontext_field(field, review.object.spec) out := pod_value } # Helper Functions is_in_range(val, ranges) = res { matching := {1 | val >= ranges[j].min; val <= ranges[j].max} res := count(matching) > 0 } get_seccontext_field(field, obj) = out { out = obj.securityContext[field] } input_containers[c] { c := input.review.object.spec.containers[_] } input_containers[c] { c := input.review.object.spec.initContainers[_] }