Kubewarden

Admission Controller 1.35 Release

Author: Víctor Cuadrado Juan

Published:

Updated:

This Admission Controller 1.35 release is one that builds the nest properly: load-bearing branches first, then careful weaving. A moderate security vulnerability has been fixed, and rather than a quick twig stuffed in a gap, the team reinforced the whole structure. This release brings also a new policy, an expansion on our threat model, and a JavaScrypt/TypeScrypt SDK relocation.

Security fix: RBAC reconnaissance and host capability calls

Kubewarden makes the following security promise:

“If you can deploy namespaced policies, you can do so without obtaining raised privileges.”

Cluster Operators can rely on this promise to provide self-servicing of policies in Namespaces. Useful, for example, when servicing several teams.

While this still applies, Ville Vesilehto (thevilledev on GitHub) found the following security vulnerability. An attacker with elevated permissions to create namespaced policies (which isn’t the default), such as AdmissionPolicies or AdmissionPolicyGroups, can craft a policy that makes use of the kubernetes/can_i host capability API call. This capability queries the cluster using a SubjectAccessReview to determine if a specific user or group has the permission to perform an action on a Kubernetes resource.

Kubewarden already has a context-aware allow-list via the policy spec.contextAwareResources for capability calls that obtain information from the cluster such as kubernetes/get_resource or kubernetes_list_resources_by_namespace.

However, can_i does not perform that check and forwards the request directly to the callback handler of the PolicyServer, which executes a real SubjectAccessReview with the PolicyServer privileges. This constitutes an information disclosure / reconnaissance issue and not direct workload data exfiltration. The attacker learns permission information, such as whether specific service accounts can “get secrets”, “create pods”, or “bind clusterroles” in chosen namespaces.

This security vulnerability has receveid GHSA-wqcw-g35j-j578 (plus an upcoming CVE identifier), with score 4.3 (moderate).

We thank Ville Vesilehto for finding this vulnerability and the responsible disclosure!

Rather than only adding a gating check for this capability and possibly for future ones, we want to uphold and double down on our promise. For that, we are introducing a new feature. Users must evaluate and configure this feature as needed in their production deployments.

New security feature: gating host capability calls for namespaced policies

PolicyServers now have a new spec.namespacedPoliciesCapabilities, which is a list of host capability calls that namespaced policies (AdmissionPolicies and AdmissionPolicyGroups) are allowed to call in that PolicyServer. This new field accepts wildcard patterns following the same conventions as the matchRules for Kubernetes Dynamic Admission Controllers. For example, ["oci/v2/*"] allows all OCI v2 capabilities only.

An example of a PolicyServer that only allows the can_i capability call is as follows:

apiVersion: policies.kubewarden.io/v1
kind: PolicyServer
metadata:
  name: for-namespaced-policies
spec:
  image: ghcr.io/kubewarden/policy-server:latest
  replicas: 1
  namespacedPoliciesCapabilities:
    - kubernetes/can_i

See the reference list of all host capability identifiers.

Default settings and upgrade configuration

All PolicyServers, including the default installed via our kubewarden-defaults Helm chart and custom deployed ones, default to having their spec.namespacedPoliciesCapabilities to ['*']. This means that by default they allows all host capability calls for namespaced policies, which is the status quo prior to this version and backwards-compatible.

Cluster Operators that make use of namespaced AdmissionPolicies and AdmissionPolicyGroups for their users (not the default) must configure the PolicyServer default from the kubewarden-defaults Helm chart value of .Values.policyServer.namespacedPoliciesCapabilities.

For example, to allow no host capabilities set an empty list: --set 'policyServer.namespacedPoliciesCapabilities={}'. To allow only OCI and DNS capabilities: --set 'policyServer.namespacedPoliciesCapabilities={oci/*,net/v1/dns_lookup_host}'.

If using custom PolicyServers deployed by themselves, Cluster Operators must:

  • Configure their PolicyServers’s Spec.namespacedPoliciesCapabilities as desired, with the list of allowed capabilities.
  • Ensure that the namespaced policies are scheduled on those configured PolicyServers. This can be done for example with the new ns-policyserver-mapper policy, as follows.

See more information on this feature in our dedicated how-to page.

New policy: ns-policyserver-mapper

This policy mutates AdmissionPolicies and AdmissionPolicyGroups to schedule them in specific PolicyServers. It does this based on a label placed on the Namespace where each policy is deployed.

Label each namespace with admission.kubewarden.io/policy-server: <desired-PolicyServer>. Then:

  • Namespaces with the label will get any AdmissionPolicies and AdmissionPolicyGroups on them mutated, so those policies are scheduled in the desired PolicyServer.

  • Namespaces without the label will have any AdmissionPolicies and AdmissionPolicyGroups creation or update rejected until the label is added. This prevents policies from being silently scheduled on an unintended PolicyServer.

You can find the policy as usual in artifacthub.io.

This policy provide one approach on scheduding namespaced policies to specific PolicyServers, but others are of course possible.

Self-servicing namespaces

With this new PolicyServer spec.namespacedPoliciesCapabilities feature plus the new ns-policyserver-mapper policy, Cluster Operators can configure their PolicyServers and provide self-service per Namespace, for example to dedicated teams, without elevated privileges.

For a concrete step-by-step example, have a look at our how-to page.

Threat model expansion

We base our threat model on the Kubernetes SIG Security assessment for Dynamic Admission Controllers.

We believe that a solid and up-to-date threat model is paramount for our users to reason about our stack, understand the usability trade-offs of production, and simplify their deployment.

Therefore, we have updated our Threat model with 3 new cases relevant to Kubewarden. You can read them in our Threat model reference documentation page.

Authoring and auditing a policy with host capability calls

With 1.35, Policy Authors can declare the host capability calls a policy makes use of under a new metadata field, hostCapabilities:

# policy metadata.yml, which gets annotated on the Wasm module
---
hostCapabilities:
  - kubernetes/can_i
  - kubernetes/get_resource

Also, kwctl annotate now performs an heuristic scan of the compiled Wasm module of a policy. Policy Authors will notice that when annotating a policy for release, kwctl emits warnings to stderr if there’s any mismatch with the declared metadata:

  • Used but undeclared: host capabilities found in the binary but absent from the metadata declaration.
  • Declared but not detected: host capabilities listed in the metadata but not found in the binary.

For example:

$ kwctl annotate -m metadata.yml -o annotated-policy.wasm policy.wasm

WARN host capabilities used by the policy but not declared in metadata
     capabilities={"kubernetes/get_resource"}
WARN host capabilities declared in metadata but not detected in the policy
     capabilities={"oci/v1/verify"}

NOTE This self-reporting from the authors and the kwctl annotate heuristic scan cannot be used as a security boundary, as an untrusted policy publisher could embed an arbitrary list.

Use this information as one signal alongside other trust indicators (image signing, source code review, policy provenance, etc) not as an authoritative proof of what capabilities a policy exercises.

We have annotated the policies maintained by the Kubewarden team and we will release new versions shortly.

Running a policy with host capability calls

With 1.35 kwctl run and kwctl bench now have a new flag --allowed-host-capabilities. This flag sets the host capabilities the policy is allowed to use and can be repeated many times. For example:

$ kwctl run \
  --allowed-host-capabilities 'oci/*' \
  --allowed-host-capabilities 'kubernetes/get_resource'

kwctl will emit errors if the policy has not been granted enough permissions for the needed capabilities.

This allows Policy Users to assess their policies out-of-cluster.

Maintenance bumps

This release also comes with complimentary maintenance bumps. We have updated the codebase to the 1.95 Rust toolchain and Go 1.26 version. For us, this meant some small adjustments to allow the usage Go 1.26 for WASI policies, as well as a new kubewarden/github-actions v5.1.0 release that makes use of the newly released TinyGo 0.41, which also brings support for Go 1.26.

Renaming the NPM package of our JavaScript/TypeScript SDK

One of the SDKs for policies that we provide is the JavaScript/TypeScript SDK. This SDK is published in npmjs.com. With its new v0.2.0 release, the NPM package has been renamed from kubewarden-policy-sdk and republished under @kubewarden/policy-sdk.

Give it a try with our policy template at github.com/kubewarden/js-policy-template

Getting in touch

Join the conversation on Slack or GitHub discussions and let us know how you’re finding Kubewarden 1.35!