Feat: add ValidatingAdmissionPolicy to protect kagenti.io/type label#418
Conversation
b31dd19 to
3d61116
Compare
d9dc082 to
3ed85cc
Compare
d1d3459 to
27a43ca
Compare
Prevents manual labeling of Deployments and StatefulSets with kagenti.io/type in both metadata and pod template spec. Only the operator SA may set this label via an AgentRuntime CR. Includes both kustomize (config/vap/) and Helm chart templates. Signed-off-by: Daniels Nagornuks <dnagornu@redhat.com> Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Removes manual kagenti.io/type labels from all E2E test fixtures (echoAgent, noProtoAgent, auditAgent, signedAgent, authBridgeAgent, authBridgeDisabledAgent) and adds AgentRuntime CRs to drive the label via the operator. Each test now deploys the workload without the protected label, creates an AgentRuntime, waits for the operator to apply kagenti.io/type, then proceeds with existing assertions. This aligns E2E tests with the new ValidatingAdmissionPolicy that prevents manual label application. Signed-off-by: Daniels Nagornuks <dnagornu@redhat.com> Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
The DefaultsConfigReconciler maintained config-hash annotations on workloads with kagenti.io/type that were not managed by an AgentRuntime CR. With the new ValidatingAdmissionPolicy enforcing that the label can only be set via an AgentRuntime, no unmanaged workloads can exist. The reconciler was effectively a no-op and is now removed along with its tests and the ComputeDefaultsOnlyHash helper. Signed-off-by: Daniels Nagornuks <dnagornu@redhat.com> Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Remove manual kagenti.io/type labeling from docs, samples, and
comments. All examples now use AgentRuntime CRs, matching the
ValidatingAdmissionPolicy that prevents direct label application.
Fix auto-created AgentCard naming to use the {name}-{kind}-card
pattern produced by AgentCardSync.
Signed-off-by: Daniels Nagornuks <dnagornu@redhat.com>
27a43ca to
4230ba6
Compare
pdettori
left a comment
There was a problem hiding this comment.
Clean, well-scoped security hardening. The VAP correctly enforces operator-only application of kagenti.io/type, the Helm/kustomize SA parity is handled (Helm templates the SA + exemptServiceAccounts; kustomize hardcodes both default identities with an explanatory comment), and tests/docs are updated to match the AgentRuntime-driven flow. The DefaultsConfigReconciler removal compiles cleanly (Build/Unit/Integration green = no dangling references), and the E2E refactor uses Gomega Eventually polling for the operator-applied label rather than manual labeling — no skips, no naked sleeps.
One minor CEL-robustness suggestion inline; no blocking issues.
Non-blocking note: the policy is enabled by default and is a breaking change — already flagged by the author with dependent PRs (kagenti#1907, kagenti-extensions#494) as merge prerequisites.
Areas reviewed: Helm/K8s (CEL/VAP), Go (controller, main.go), E2E tests, Docs, Kustomize. Commits: 4, all signed-off. CI: all 16 checks passing.
Assisted-By: Claude Code
| variables: | ||
| - name: metaLabelUnchanged | ||
| expression: >- | ||
| !('kagenti.io/type' in object.metadata.labels) |
There was a problem hiding this comment.
object.metadata.labels is accessed here without a has() guard, unlike the has-agent-type-label matchCondition and the parallel podTemplateLabelUnchanged variable (both of which guard with has(...)). If a non-operator submits a workload carrying kagenti.io/type only in the pod template with no top-level metadata.labels, this expression accesses an absent map; under failurePolicy: Fail that risks an eval-error denial instead of the intended messageExpression. The outcome (deny) is still correct, so this is low severity, but suggest guarding for symmetry:
!has(object.metadata.labels)
|| !('kagenti.io/type' in object.metadata.labels)
|| (request.operation == 'UPDATE' && ...)
The same applies to the identical Helm template at charts/kagenti-operator/templates/vap/validating-admission-policy.yaml.
Prerequisites
This PR can be reviewed in parallel but should not be merged until the following PRs are merged due to breaking changes:
Summary
The
kagenti.io/typelabel on Deployments and StatefulSets can currently be applied by any user, bypassing the AgentRuntime workflow. This PR adds a ValidatingAdmissionPolicy (VAP) that prevents direct application of the label in both the workload metadata and pod template spec — only the operator's service account (via the AgentRuntime controller) can set or change it. This enforces the intended enrollment flow: users create an AgentRuntime CR, and the controller applies the label. TheDefaultsConfigReconciler(which handled unmanaged labeled workloads) is removed since the VAP makes that path impossible. Documentation, samples, and E2E tests are updated to reflect the new enforcement.Changes
kagenti.io/typein eithermetadata.labelsorspec.template.metadata.labels, unless the request is from the operator's service account or the label value is unchanged on an UPDATEconfig/vap/and Helm viacharts/kagenti-operator/templates/vap/) apply the policy on installkagenti.io/type; tests create an AgentRuntime CR and wait for the operator to apply the label before proceedingComputeDefaultsOnlyHashfunction (no longer reachable with VAP in place)kagenti.io/typefrom skill-discovery sample Deployment metadata{name}-{kind}-cardnaming patterndefaults_config_reconciler.goin controller and fixture commentsTesting
Related Issues
Resolves: RHAIENG-4934
Resolves: RHAIENG-4937
Made with Cursor