Skip to content

feat: Add host-capabilities whitelist#1693

Merged
flavio merged 56 commits intomainfrom
feat/allowed-host-cap
Apr 27, 2026
Merged

feat: Add host-capabilities whitelist#1693
flavio merged 56 commits intomainfrom
feat/allowed-host-cap

Conversation

@viccuad
Copy link
Copy Markdown
Member

@viccuad viccuad commented Apr 27, 2026

Description

This PR:

  • policy-evaluator, kwctl, policy-server: accept the host-capabilities allow list.
    • Add new crates/policy-evaluator/src/host_capabilities_allow_list.rs with new HostCapabilitiesAllowList{}
    • HostCapabilitiesAllowList::default() provides an empty list, no capabilities allowed. We provide a new HostCapabilitiesAllowList::allow_all() to use as "default" constructor.
  • controller:
    • implement PolicyServer.spec.namespacedPoliciesCapabilities
    • Support hostCapabilities field in policies.yml
  • kubewarden-defaults Helm chart:
    • Provide new .Values.policyServer.namespacedPoliciesCapabilities
    • Extend json schema.
  • kwctl: Enhance kwctl annotate and kwctl run to understand the new policy metadata hostCapabilities list. kwctl annotate now has a new heuristics on the Wasm module to discover declared host capabilities.

All changes are backwards-compatible with the default settings.

Test

CI. Added unit tests and integration tests where needed.
Pre-validated while under embargo.

Additional Information

For manual testing, use tilt up to create the environment, then define the following policies:

apiVersion: policies.kubewarden.io/v1
kind: AdmissionPolicy
metadata:
  name: privileged-pods
  namespace: default
spec:
  module: registry://ghcr.io/kubewarden/policies/pod-privileged:v0.2.2
  rules:
    - apiGroups: [""]
      apiVersions: ["v1"]
      resources: ["pods"]
      operations:
        - CREATE
        - UPDATE
  mutating: false
---
apiVersion: policies.kubewarden.io/v1
kind: ClusterAdmissionPolicy
metadata:
  name: psp-capabilities
spec:
  policyServer: default
  module: registry://ghcr.io/kubewarden/policies/capabilities-psp:v1.0.10
  rules:
    - apiGroups: [""]
      apiVersions: ["v1"]
      resources: ["pods"]
      operations:
        - CREATE
        - UPDATE
  mutating: true
  settings:
    allowed_capabilities:
      - CHOWN
    required_drop_capabilities:
      - NET_ADMIN
---
apiVersion: policies.kubewarden.io/v1
kind: AdmissionPolicyGroup
metadata:
  name: privileged-pods
  namespace: default
spec:
  rules:
    - apiGroups: [""]
      apiVersions: ["v1"]
      resources: ["pods"]
      operations:
        - CREATE
        - UPDATE
  policies:
    policy:
      module: registry://ghcr.io/kubewarden/policies/pod-privileged:v0.2.2
  expression: "policy() && true"
  message: "privileged-pods policy group rejected the request"
---
apiVersion: policies.kubewarden.io/v1
kind: ClusterAdmissionPolicyGroup
metadata:
  name: psp-capabilities
spec:
  policyServer: default
  rules:
    - apiGroups: [""]
      apiVersions: ["v1"]
      resources: ["pods"]
      operations:
        - CREATE
        - UPDATE
  policies:
    policy:
      module: registry://ghcr.io/kubewarden/policies/capabilities-psp:v1.0.10
      settings:
        allowed_capabilities:
          - CHOWN
        required_drop_capabilities:
          - NET_ADMIN
  expression: "policy() && true"
  message: "psp-capabilities policy group rejected the request"

Edit the default PolicyServer and change this attribute from:

  namespacedPoliciesCapabilities:
  - "*"

To something different like:

  namespacedPoliciesCapabilities:
  - net/*
  - oci/*

Changing this attribute will lead to a restart of the PolicyServer
Then look at the contents of the ConfigMap of the policy server, more specifically to the policies.yml atribute:

kubectl get configmaps -n kubewarden policy-server-default -o json | yq '.data["policies.yml"]' | yq -P

The output will be quite long (4 policies!), what you have to look for:

  • cluster wide policies must have hostCapabilities set to ['*']
  • namespaced policies must have hostCapabilities set according to the value you put inside of the default PolicyServer definition

By default, our helm chart will configure the default PolicyServer to give * to all the namespaced policies. That's fine and intended to avoid breaking users

Test

To test this pull request, you can run the following commands:

Additional Information

Tradeoff

Potential improvement

Checklist

viccuad and others added 30 commits April 27, 2026 11:05
Add new HostCapabilitiesAllowList{} with separate fields allow_all,
prefixes, exact. This helps for matching and serialization/deserialization.

Add new HostCapabilitiesPatternError.

Add unit tests.

Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
…tionContext{}

Consume `new HostCapabilitiesAllowList{}` in `EvaluationContext{}`.

Add `HostCapabilitiesAllowList::allow_all()` as an explicit constructor.
`default()` will reject all, kept as a sane fails-closed constructor.

Consume new HostCapabilitiesAllowList::allow_all() in existing tests
to have them pass.

Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
…tion context

Also add new can_access_host_capability() helper function.

Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
New test adapted from existing test_runtime_context_aware test.

Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
Correctly implementing this feature for PolicyGroups mean expanding
`PolicyGroupMemberWithContext{}` with `pub host_capabilities_allow_list:
HostCapabilitiesAllowList`, that will be passed from each of the policy
members.

Problem is, `PolicyGroupMemberWithContext{}` is in policy-sdk-rust.
Postponing.

Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
For each host capability namespace and operation, add a denial test.

For each host capability namespace, add an allowed test.

Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
…arning

Document Evaluator enum.

Remove unneeded mut on validate_settings() of policy-evaluator
runtimes.

Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
… for policies

Add `hostCapabilities` field to policies.yml for normal policies (not
PolicyGroups).

Add `policy_id_to_host_capabilities` to EvaluationEnvironment. Read its
value from the policies.yml structs.

We do an unwrap_or_default(), which means that if it isn't present, we
default to empty list, not allowing any host capability at this level
(as specified in RFC). We will configure the PolicyServer default via
the controller later on.h

Per policy, save it and rehydrate it later on.
Per policy, pass it on the `EvaluationContext`.

Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
…oups

Allow all capabilities, maintaining compat for now.

Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
This simplifies the testcase types.

Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
…pec{}

Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
Validate new PolicyServer.spec.namespacedPoliciesCapabilities.
Use a tree that contains all possible options for a capability string.

Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
…ry{}

Add HostCapabilities field to the policies.yml struct, for policies
(PolicyGroups will come later).

namespaced policies get their HostCapabilities value from the
PolicyServer.spec.NamespacedPoliciesCapabilities.

Clusterwide policies get their HostCapabilities value hardcoded to "*".

Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
This will be implemented later on.

Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
…abilities

Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
…abilities

Add new key to kubewarden-defaults.
Check obvious errors of items with values.schema.json.

Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
We only have 1 binding, `kubewarden`. Simplify nesting.

Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
Wire `hostCapabilities` through `buildPolicyGroupMembersWithContext` so
each group member carries its own allow-list in the policy-server
ConfigMap.

Update controller tests.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Add `host_capabilities_allow_list` field to `PolicyGroupMemberSettings`
and supply it to the evaluation context when rehydrating each member,
replacing the previous `allow_all()` placeholder.

Introduce `HostCapabilitiesAllowList::deny_all()` as an explicit constructor.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
…cies

Add `host_capabilities` field to `PolicyGroupMember` config (deserialised
from the ConfigMap). Build a `HostCapabilitiesAllowList` from it during
environment bootstrap and propagate it into each group member's
`PolicyGroupMemberSettings`.

Add integration tests covering both the denied and allowed cases for group policies.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Add `--allowed-host-capabilities` flag to run and bench sub-commands.
This is a multi-value flag, with default value to `["*"]`.

Update e2e tests.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
…sAllowList

Force users to either use the `allow_all` or the `deny_all`
constructors.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
flavio and others added 16 commits April 27, 2026 11:05
Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Cover another scenario related with cluster-wide group policies.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
- stdlib goes first
- merge import paths

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Fix schema validation for `PolicyServer.namespacedPoliciesCapabilities`,
also add a helm test covering that.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Validate the host capabilities provided by the user.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Rely on the new validation to reduce the code.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
kwctl annotate now scans the Wasm module's data segments at annotation time to detect which
host capabilities the policy actually uses.
It does this by searching for known Kubewarden host capability namespace/operation strings
embedded in the binary.

It compares the detected capabilities against the declared ones in the policy's metadata and emits
warnings for any mismatches:
- capabilities used by the policy but not declared in the metadata
- capabilities declared in the metadata but not detected in the policy binary

The embedded metadata now contains the list of host capabilities being
used. This list always comes from the value provided by the user inside
of the metadata.yml, even when the data is not matching with the
heuristics.

Signed-off-by: Flavio Castelli <fcastelli@suse.com>
Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
…s is omitted

This way we are backwards compatible with custom PolicyServers.
Cluster Operators must opt-in into the feature.

Amend testcase.

Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
Further clarify expectations.

Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
@viccuad viccuad added this to the 1.35 milestone Apr 27, 2026
Copilot AI review requested due to automatic review settings April 27, 2026 09:13
@viccuad viccuad requested a review from a team as a code owner April 27, 2026 09:13
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 27, 2026

Codecov Report

❌ Patch coverage is 95.63164% with 37 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.04%. Comparing base (5eacbd8) to head (92f2322).
⚠️ Report is 57 commits behind head on main.

Files with missing lines Patch % Lines
crates/policy-evaluator/src/runtimes/callback.rs 91.96% 20 Missing ⚠️
crates/policy-evaluator/src/host_capabilities.rs 96.34% 9 Missing ⚠️
...cy-server/src/evaluation/evaluation_environment.rs 80.48% 8 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1693      +/-   ##
==========================================
- Coverage   80.44%   77.04%   -3.41%     
==========================================
  Files         127      172      +45     
  Lines       16413    21499    +5086     
==========================================
+ Hits        13204    16564    +3360     
- Misses       3209     4722    +1513     
- Partials        0      213     +213     
Flag Coverage Δ
go-tests 57.97% <100.00%> (?)
rust-tests 82.14% <95.28%> (+1.69%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds end-to-end support for host-capabilities allow-listing (policy metadata, controller → policy-server config propagation, kwctl UX, and validation), enabling policies to be restricted to specific host capability API calls.

Changes:

  • Controller: propagates PolicyServer.spec.namespacedPoliciesCapabilities into generated policies.yml as per-policy/per-member hostCapabilities.
  • Policy execution: introduces host-capability pattern parsing/enforcement in policy-evaluator and wires it through policy-server evaluation contexts.
  • kwctl: adds --allowed-host-capabilities, adds Wasm scanning heuristics for declared capabilities, and extends tests/docs.

Reviewed changes

Copilot reviewed 53 out of 54 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
internal/controller/utils_test.go Adds shared Gomega matchers to assert hostCapabilities in generated configmap policies.yml.
internal/controller/policyserver_controller_test.go Extends controller tests to validate hostCapabilities propagation for policies and group members.
internal/controller/policyserver_controller_configmap.go Adds hostCapabilities to configmap policy entries and derives namespaced vs cluster-wide capability defaults.
docs/crds/CRD-docs-for-docs-repo.md Documents namespacedPoliciesCapabilities in PolicyServer CRD docs (markdown).
docs/crds/CRD-docs-for-docs-repo.adoc Documents namespacedPoliciesCapabilities in PolicyServer CRD docs (asciidoc).
crates/policy-server/tests/sigstore.rs Updates test config to include host_capabilities field.
crates/policy-server/tests/integration_test.rs Adds integration tests covering allow/deny behavior for host capability gating.
crates/policy-server/tests/data/deployment_admission_review.json Adds an admission review fixture for host-capabilities/context-aware tests.
crates/policy-server/tests/common/mod.rs Extends test config builders to support host capabilities and context-aware test policies/groups.
crates/policy-server/src/evaluation/evaluation_environment.rs Threads HostCapabilities into EvaluationContext creation and rehydration paths.
crates/policy-server/src/config.rs Adds host_capabilities fields to policy and group-member config structures (+ example docs).
crates/policy-evaluator/tests/k8s_mock/mod.rs Adds a no-op k8s mock scenario for cases where callbacks must not occur (capability denied).
crates/policy-evaluator/tests/integration_test.rs Adds integration coverage for host capability allow/deny behavior.
crates/policy-evaluator/src/runtimes/wapc/runtime.rs Makes validate_settings take &self and updates tests for new eval context fields.
crates/policy-evaluator/src/runtimes/rego/runtime.rs Makes validate_settings take &self (parity with wapc runtime).
crates/policy-evaluator/src/runtimes/callback.rs Enforces host-capability checks before dispatching callbacks (except tracing).
crates/policy-evaluator/src/policy_metadata.rs Adds optional host_capabilities to policy metadata.
crates/policy-evaluator/src/policy_group_evaluator/evaluator.rs Propagates host capabilities into group member evaluation contexts.
crates/policy-evaluator/src/policy_group_evaluator.rs Extends group member settings to carry host capabilities.
crates/policy-evaluator/src/policy_artifacthub.rs Updates tests/fixtures for new metadata field.
crates/policy-evaluator/src/lib.rs Exposes new host_capabilities module.
crates/policy-evaluator/src/host_capabilities.rs Introduces capability pattern parsing/validation and operation enumeration.
crates/policy-evaluator/src/evaluation_context.rs Adds host capabilities to evaluation context + access-check helper.
crates/policy-evaluator/src/errors.rs Adds HostCapabilitiesPatternError for pattern validation failures.
crates/policy-evaluator/Cargo.toml Bumps policy-evaluator crate version.
crates/kwctl/tests/e2e.rs Adds e2e coverage for host-capability gating and annotate warnings.
crates/kwctl/tests/data/context-aware-annotate/metadata-wrong.yml Adds metadata fixture with intentionally wrong host capabilities list.
crates/kwctl/tests/data/context-aware-annotate/metadata-correct.yml Adds metadata fixture with correct host capabilities list.
crates/kwctl/tests/common/mod.rs Minor import consolidation for test helpers.
crates/kwctl/src/wasm_scanner.rs Adds Wasm data-segment scanner to detect used host capabilities.
crates/kwctl/src/scaffold/manifest.rs Updates scaffold manifest tests for new metadata field.
crates/kwctl/src/main.rs Registers new wasm_scanner module.
crates/kwctl/src/config/pull_and_run.rs Passes CLI --allowed-host-capabilities into YAML policy parsing.
crates/kwctl/src/config/policy_definition.rs Stores allowed host caps on policy definitions and applies CLI allow list for YAML-defined policies/groups.
crates/kwctl/src/command/run/evaluator.rs Wires allowed host caps into EvaluationContext; boxes evaluator for enum size.
crates/kwctl/src/cli.rs Adds --allowed-host-capabilities option (default *) to run-related commands.
crates/kwctl/src/annotate.rs Scans Wasm for used capabilities and warns on mismatch vs metadata hostCapabilities.
crates/kwctl/cli-docs.md Documents new CLI flag in generated CLI docs.
crates/kwctl/Cargo.toml Adds memchr (scanner) and wasm-encoder (scanner tests) deps.
crates/context-aware-test-policy/metadata.yml Declares hostCapabilities for the context-aware test policy.
crates/burrego/src/evaluator.rs Import reformatting/organization.
config/crd/bases/policies.kubewarden.io_policyservers.yaml Adds CRD schema for namespacedPoliciesCapabilities.
charts/kubewarden-defaults/values.yaml Adds .Values.policyServer.namespacedPoliciesCapabilities defaulting to ["*"].
charts/kubewarden-defaults/values.schema.json Adds JSON schema validation for namespacedPoliciesCapabilities.
charts/kubewarden-defaults/tests/namespacedPoliciesCapabilities_test.yaml Adds Helm schema validation tests for the new value.
charts/kubewarden-defaults/templates/policyserver-default.yaml Renders namespacedPoliciesCapabilities into the default PolicyServer manifest.
charts/kubewarden-crds/templates/policies.kubewarden.io_policyservers.yaml Updates CRD chart template with namespacedPoliciesCapabilities schema/docs.
api/policies/v1/zz_generated.deepcopy.go Updates deepcopy generation for new slice field(s).
api/policies/v1/policyserver_webhook_test.go Adds webhook validation tests for namespacedPoliciesCapabilities patterns.
api/policies/v1/policyserver_webhook.go Adds capability-tree-based validation for PolicyServer.spec.namespacedPoliciesCapabilities.
api/policies/v1/policyserver_types.go Adds NamespacedPoliciesCapabilities to the PolicyServerSpec API type.
api/policies/v1/factories.go Extends PolicyServer test factory/builder to set capabilities list.
Cargo.lock Updates lockfile for new deps and version bumps.
.github/workflows/build-kwctl.yml Adds contents: read permissions to workflow jobs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread config/crd/bases/policies.kubewarden.io_policyservers.yaml
Comment thread charts/kubewarden-defaults/templates/policyserver-default.yaml
Comment thread crates/policy-server/tests/integration_test.rs
Comment thread crates/kwctl/Cargo.toml
Signed-off-by: Víctor Cuadrado Juan <vcuadradojuan@suse.de>
@flavio flavio merged commit 2745017 into main Apr 27, 2026
72 of 73 checks passed
@github-project-automation github-project-automation Bot moved this from Pending review to Done in Kubewarden Admission Controller Apr 27, 2026
@flavio flavio deleted the feat/allowed-host-cap branch April 27, 2026 12:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Development

Successfully merging this pull request may close these issues.

3 participants