open-policy-agent / gatekeeper

🐊 Gatekeeper - Policy Controller for Kubernetes
https://open-policy-agent.github.io/gatekeeper/
Apache License 2.0
3.67k stars 751 forks source link

kube-mgmt/opa feature parity #3645

Open EdwardCooke opened 2 days ago

EdwardCooke commented 2 days ago

Describe the solution you'd like Right now I'm using kube-mgmt/opa to expose an endpoint for use as a Kubernetes API Authorization webhook. For example, my api authentication webhook calls https://opa-opa-kube-mgmt.opa-auth-system.svc.cluster.local:8181/v0/data/k8sallow/allow.

That policy is set using a config map that kube-mgmt injects into OPA.

Can I do the same using gatekeeper? And if so, how? I looked over the documentation and couldn't find a way of doing this.

Anything else you would like to add: I'm using the latest kube-mgmt with the latest opa image.

Here's the policies I use kube-mgmt to inject into opa:

apiVersion: v1
kind: ConfigMap
metadata:
  labels:
    openpolicyagent.org/policy: rego
  name: policy-k8sallow
  namespace: opa-auth-system
data:
  k8sallow: |
    package k8sallow

    import rego.v1

    namespace := input.spec.resourceAttributes.namespace
    x := data.kubernetes.namespaces[namespace]

    default override_allow := false
    valid_groups := split(data.kubernetes.namespaces[namespace].metadata.annotations["vecc/groups"], ",")

    override_allow if {
      input.spec.resourceAttributes.resource == "namespaces"
      input.spec.resourceAttributes.verb == "list"
    }
    else if {
      input.spec.nonResourceAttributes.path in {
        "/api",
        "/apis",
        "/openapi/v2"
      }
    }
    else if {
      some group in input.spec.groups
      group == "oidc:Admins"
    }

    groups_match if {
      not override_allow
      some group in valid_groups
      some member_of in input.spec.groups
      group == member_of
    }

    deny contains "Cluster scope not allowed" if {
      not override_allow
      not namespace
    }

    deny contains "Namespace denied, not assigned to namespace" if {
      namespace
      not override_allow
      not groups_match
    }

    allow := {
      "apiVersion": "authorization.k8s.io/v1",
      "kind": "SubjectAccessReview",
      "status": {
        "allowed": allowed,
        "denied": denied,
        "reason": reason
      }
    }

    default allowed := false
    default denied := false
    default reason = ""

    reason := concat(";", deny)

    allowed if {
      override_allow
    }

    denied if {
      reason != ""
    }

  k8sallow_authz: |
    package system.authz

    import rego.v1

    allow if { input.path == [ "v0", "data", "k8sallow", "allow" ] }

Another policy that would fit into gatekeeper I think pretty well.

apiVersion: v1
kind: ConfigMap
metadata:
  labels:
    openpolicyagent.org/policy: rego
  name: policy-k8srestartonly
  namespace: opa-auth-system
data:
  k8srestartonly: |
    package k8srestartonly

    import rego.v1

    old_object := input.request.oldObject
    new_object := input.request.object

    default uid := ""
    default allowed := false
    default is_developers := false
    default message := ""

    allow := {
      "apiVersion": "admission.k8s.io/v1",
      "kind": "AdmissionReview",
      "response": {
        "uid": uid,
        "allowed": allowed,
        "status": {
          "message": message
        }
      }
    }

    uid := input.request.uid

    is_usrestricted if {
      some group in input.request.userInfo.groups
      group == "oidc:Admins"
    }

    allowed if {
      is_usrestricted
    }
    else if {
      x := json.remove(old_object, [ "status", "metadata/generation", "metadata/managedFields", "spec/template/metadata/annotations/kubectl.kubernetes.io~1restartedAt" ])
      y := json.remove(new_object, [ "status", "metadata/generation", "metadata/managedFields", "spec/template/metadata/annotations/kubectl.kubernetes.io~1restartedAt" ])
      x == y
    }

    message := "Modifying the object is prohibited" if {
      not allowed
    }

  k8srestartonly_authz: |
    package system.authz

    import rego.v1

    allow if { input.path == [ "v0", "data", "k8srestartonly", "allow" ] }

Environment:

maxsmythe commented 21 hours ago

Unfortunately Gatekeeper does not currently provide an authorization webhook.

ritazh commented 20 hours ago

Not exactly what you asked for, but take a look at https://kubernetes.io/blog/2024/04/26/multi-webhook-and-modular-authorization-made-much-easier/

conditions for invocation with CEL rules to pre-filter requests before they are dispatched to webhooks, helping you prevent unnecessary invocations.

EdwardCooke commented 7 hours ago

That’s what I use. Along with the GitHub issue I opened due to incorrect documentation on that and all the other pages for using that method.

Right now I have it calling opa directly was hoping for something with gatekeeper.

EdwardCooke commented 7 hours ago

Thanks @maxsmythe that answers my question. I’ll continue with the setup I have then and revisit it later.