Skip to content

Admission merge#3917

Closed
tombentley wants to merge 20 commits into
kroxylicious:mainfrom
tombentley:admission-merge
Closed

Admission merge#3917
tombentley wants to merge 20 commits into
kroxylicious:mainfrom
tombentley:admission-merge

Conversation

@tombentley
Copy link
Copy Markdown
Member

Type of change

  • Enhancement / new feature

Description

Add the admission webhook, as described in proposal kroxylicious/design#100

tombentley and others added 20 commits May 8, 2026 13:38
)

* Add sidecar injection webhook module (Phase 1 or several)

Implements a Kubernetes mutating admission webhook that injects
Kroxylicious proxy sidecars into pods in labelled namespaces. Includes
KroxyliciousSidecarConfig CRD, admission handler with fail-open
semantics, JSON Patch generation, proxy config generation, and
deployment artifacts with cert-manager TLS integration.

Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>
kroxylicious#3820)

* Webhook Phase 2: filters, upstream TLS, delegated annotations, native sidecar

Extend the sidecar injection webhook with:
- Filter definitions in KroxyliciousSidecarConfig CRD and proxy config generation
- Upstream TLS support with Secret volume mounts for CA trust anchors
- Delegated annotation overrides (bootstrap port, node ID range) with validation
- Native sidecar container injection (initContainers with restartPolicy: Always) for K8s 1.28+
- Kubernetes version detection in WebhookMain to auto-select sidecar strategy

Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>
kroxylicious#3830)

* Webhook Phase 3: container image build, integration tests, module documentation

- Assembly descriptor, start script, Dockerfile for webhook container image
- Maven dist profile: image build, manifest interpolation, failsafe KT tests
- KindWebhookInstallKT: installs webhook on Kind, verifies sidecar injection
  and opt-out, using self-signed TLS (no cert-manager dependency)
- Add MinikubeWebhookInstallKT for running the
  webhook integration test on Minikube clusters too.
- Fix InjectionDecision to check initContainers for native sidecar idempotency
- Add README.md and CLAUDE.md for the webhook module

Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>
kroxylicious#3847)

* Webhook Phase 4: 3rd party plugin support via OCI image volumes; init-container fallback

Extends the CRD with plugins and allowedPluginRegistries fields. PodMutator
generates OCI image volumes on K8s 1.31+ clusters or falls back to
init-container + emptyDir for older clusters. Delegated plugin images are
validated against a registry allow-list and require @sha256: digest pinning.

Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>
kroxylicious#3849)

* Webhook Phase 5: End-to-end OCI image volume plugin loading integration test

Adds KindPluginEndToEndKT which creates a Kind cluster with the
ImageVolume feature gate, deploys Strimzi + Kafka, installs the
webhook with a plugin filter config, and verifies that messages
produced through the sidecar are transformed by the plugin filter
loaded from an OCI image volume.

Supporting changes:
- Add test-plugin scratch OCI image (simple-transform JARs)
- Extend WebhookInfo to carry proxy and test-plugin image metadata
- Add maven-dependency-plugin and docker-maven-plugin executions
  for building the test-plugin image in the dist profile
- Remove duplicate jackson-dataformat-yaml dependency

Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>
…ok (kroxylicious#3863)

The example certificate was not consumable, complaining about "Empty
issuer DN not allowed in X509Certificates" in the Webhook. This is
resolved by specifying a common name in the Certificate

Then, the default tls.key failed to parse. By default it is emitted as
PKCS#1 (starts with -----BEGIN RSA PRIVATE KEY-----). We attempt to
parse it as a PKCS8 spec and fail. Java does not have native support for
PKCS#1 so we change the Certificate to emit PCKS8

Signed-off-by: Robert Young <robertyoungnz@gmail.com>
Various tidyings-up:

* Upstream -> Target
* Some review fixes
* Use the constants for the default ports
* Let the CRD be the ultimate SoT about defaults
* feat(webhook): set Ready status condition on KroxyliciousSidecarConfig
* test(webhook): verify SidecarConfigResolver calls status updater
* Remove TODOs
* refactor(webhook): use Fabric8 POJOs instead of Jackson ObjectNode in PodMutator
* refactor(webhook): use Fabric8 client instead of kubectl in install test
* refactor(webhook): use Fabric8 client in plugin end-to-end test
* change webhook parent module to kroxylicious-kubernetes-parent
* toJson tweaks

Signed-off-by: Tom Bentley <tbentley@redhat.com>
Signed-off-by: Robert Young <robertyoungnz@gmail.com>
Co-authored-by: Robert Young <robertyoungnz@gmail.com>
Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
* rename kroxylicious-kubernetes-web-hook to kroxylicious-admission, and factor out a separate api module.

Signed-off-by: Robert Young <robertyoungnz@gmail.com>
…icy (kroxylicious#3868)

The webhook now denies admission requests on internal errors by default
(fail-closed), matching the convention used by Istio and Linkerd.
Administrators can opt back into fail-open by setting FAILURE_POLICY=Ignore
on the Deployment and failurePolicy: Ignore on the
MutatingWebhookConfiguration.

The Deployment now runs 2 replicas with pod anti-affinity and a
PodDisruptionBudget (maxUnavailable: 1) to maintain availability
during voluntary disruptions.

Includes a KT integration test that verifies pod creation is rejected
when the webhook is unreachable.

Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>

Signed-off-by: Tom Bentley <tbentley@redhat.com>
Java's KeyFactory cannot parse PKCS1-encoded private keys (those with
BEGIN RSA PRIVATE KEY or BEGIN EC PRIVATE KEY headers). Previously,
the webhook would strip these headers and attempt PKCS8 parsing,
leading to cryptic errors at runtime.

Now the webhook detects PKCS1 format during startup and fails with a
clear error message.

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>

Signed-off-by: Robert Young <robertyoungnz@gmail.com>
The target topology is a concern of the admission webhook admin, not the
application owner. Unclear why they would want to override it at the pod
level.

Signed-off-by: Robert Young <robertyoungnz@gmail.com>
…roxylicious#3870)

* docs(admission): add admission webhook guide and fix opt-out to use labels

Add initial user documentation for the admission webhook as a new guide
in kroxylicious-docs, covering overview, installation, and basic usage.
The guide marks the feature as early access with the v1alpha1 API subject
to change. TODOs are included for content that depends on incomplete
implementation (filters, TLS, plugins, delegated annotations).

Fix pod-level opt-out to consistently use labels rather than annotations,
matching the objectSelector in the MutatingWebhookConfiguration and
aligning with Istio's approach. Both namespace-level opt-in and pod-level
opt-out now use labels.

Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>
* Adopt sidecar.kroxylicious.io/ annotation prefix

Use Istio-style subdomain prefix (sidecar.kroxylicious.io/) instead of
kroxylicious.io/sidecar-* for all sidecar webhook annotations and labels.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>

* Replace sidecar-status with config-generation annotation

Use sidecar.kroxylicious.io/config-generation (recording the
KroxyliciousSidecarConfig's metadata.generation) instead of a
simple status flag. Serves as both idempotency guard and drift
detection mechanism.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>

* Support feature gate configuration via FEATURE_GATES env var

Add FEATURE_GATES environment variable (e.g. ImageVolume=true,
SidecarContainers=true) to override version-based feature detection.
Feature gate values take precedence over version heuristics. When no
gates are specified, defaults to beta thresholds (1.29 for native
sidecars, 1.33 for OCI image volumes).

Also update user documentation to use sidecar.kroxylicious.io/ prefix
and document feature gate configuration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>

* Remove pod-level securityContext, fix null generation handling

Stop setting pod-level securityContext (runAsNonRoot, seccompProfile)
from the webhook. Admins should use PodSecurity admission policies
instead, avoiding ordering conflicts with other mutating webhooks.

Also fix NPE when KroxyliciousSidecarConfig metadata.generation is
null (e.g. before first server-side update), and fix PodMutatorTest
calls missing the configGeneration parameter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>

* Document pod security context recommendation in user docs

The webhook does not set pod-level securityContext. Document that
admins should use PodSecurity admission (Pod Security Standards)
at the namespace level instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>

* Fix parameter alignment in WebhookMain.detectVersion

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>

* Remove app-owner plugin image delegation

Remove DELEGATED_PLUGIN_IMAGES annotation, allowedPluginRegistries
CRD field, parseDelegatedPlugins/applyDelegatedPluginImages methods,
and all associated tests. Admin-specified plugins (spec.plugins)
are unaffected.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>

* Fix webhook label selectors to use sidecar.kroxylicious.io/ prefix

Missed from the annotation prefix convention change in a9ac240.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>

* Remove delegation and restructure CRD for virtualClusters list

Remove all delegation machinery (bootstrap port override, undelegated
annotation warnings, delegatedAnnotations CRD field). Delegation can
be re-added later if needed.

Restructure the CRD so that targetBootstrapServers, bootstrapPort,
nodeIdRange, and targetClusterTls live under a virtualClusters list
rather than as flat spec-level fields. The alpha enforces exactly one
entry (minItems/maxItems: 1). This enables future multi-cluster
support (e.g. MirrorMaker2) without a schema-breaking change.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>

* Update user docs for virtualClusters schema and remove delegation

Update CRD example to use virtualClusters list. Update trust model
to remove delegation references. Remove delegation TODO.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>

* Add secretMounts field for mounting Secrets into the sidecar container

Filters that need credentials (e.g. KMS keys for record encryption) cannot
have those values in the proxy config annotation, which is visible to anyone
who can read pods. The new secretMounts field on KroxyliciousSidecarConfig
lets the admin mount Kubernetes Secrets into the sidecar at fixed paths
under /opt/kroxylicious/secrets/<name>/, keeping the filesystem layout
under webhook control.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>

* Add KT test for secret mount injection

Verifies that when a KroxyliciousSidecarConfig has a secretMounts entry,
the webhook injects the corresponding Secret volume and read-only mount
on the sidecar container, and that the app container does not receive it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>

* Remove stale TODO about CRD module factoring

The CRD is already in its own module (kroxylicious-admission-api).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>

* Unify injection label: use sidecar.kroxylicious.io/injection for both namespaces and pods

Namespaces use value 'enabled', pods use value 'disabled'. This replaces
the previous inconsistency where namespaces used sidecar.kroxylicious.io/injection
and pods used sidecar.kroxylicious.io/inject with value 'false'.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>

* Move CRD to sidecar.kroxylicious.io group and add injection-skipped label

Rename the CRD API group from kroxylicious.io to sidecar.kroxylicious.io
to avoid potential conflicts with the operator's CRDs. Update all generated
class imports, fabric8 generator config, checkstyle suppressions, RBAC
rules, and user docs.

Add a sidecar.kroxylicious.io/injection-skipped label to pods where
injection was skipped (no-config, already-injected) so operators can
find affected pods via label selectors instead of grepping logs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>

* Add multiple-configs skip label and Resolution result type

SidecarConfigResolver now returns a Resolution record that carries both
the resolved config and the resolution outcome (FOUND, NO_CONFIG,
MULTIPLE_CONFIGS). This lets the webhook label skipped pods with
sidecar.kroxylicious.io/injection-skipped=multiple-configs when more
than one KroxyliciousSidecarConfig exists in the namespace and no
explicit config annotation is present.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>

* Avoid < in CRD

See fabric8io/kubernetes-client#7632, which should be fixed in 7.7.0 when that is released

Signed-off-by: Tom Bentley <tbentley@redhat.com>

---------

Signed-off-by: Tom Bentley <tbentley@redhat.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…cious#3869)

Adds admission webhook Docker image building to the docker.yaml workflow,
following the same pattern used for the operator image. The workflow now:
- Builds kroxylicious-admission Maven artifacts
- Configures admission_tags for both PR and push builds
- Builds and optionally pushes the webhook image to the registry

This requires the ADMISSION_IMAGE_NAME repository variable to be configured
for pushing to registries.

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>

Signed-off-by: Robert Young <robertyoungnz@gmail.com>
…ylicious#3875)

* build(admission): add distribution module for release artifacts

Creates kroxylicious-admission-dist module to package the admission
webhook installation manifests, CRDs, and examples into tar.gz and
zip archives for release.

Follows the same pattern as kroxylicious-operator-dist, using a jbang
script to copy and rename CRDs with the 00.CustomResourceDefinition
prefix to ensure correct kubectl apply ordering.

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Robert Young <robertyoungnz@gmail.com>

* fix(build): include LICENSE in distribution archives

Fix LICENSE file path in admission-dist and operator-dist assembly
descriptors. Both were referencing ${project.basedir}/.. which only
reaches kroxylicious-kubernetes directory, not the project root where
LICENSE resides. Changed to ${project.basedir}/../.. to correctly
include LICENSE in distribution archives.

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Robert Young <robertyoungnz@gmail.com>

---------

Signed-off-by: Robert Young <robertyoungnz@gmail.com>
Adds CustomResourceValidationIT following the same pattern as the
operator's validation test. The test validates KroxyliciousSidecarConfig
CRD schema enforcement by applying YAML scenarios against a live
Kubernetes cluster.

Test coverage:
- Required fields (spec.targetBootstrapServers)
- Pattern validations (metadata.name, filterDefinitions, plugins)
- Numeric constraints (port ranges, nodeIdRange minimums)
- Enum validations (pullPolicy)
- Length constraints (maxLength on names)
- Status subresource schema (Kubernetes condition structure)

Infrastructure:
- AdmissionTestUtils: Kubernetes client utilities
- TestFiles: YAML scenario file discovery (duplicated from operator)

Test uses direct Fabric8 client calls (serverSideApply) rather than
JOSDK, consistent with existing admission module patterns.

Also fixes CRD schema issues:
- Anchors filter name pattern to prevent partial matches

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>

Signed-off-by: Robert Young <robertyoungnz@gmail.com>
…me=false` (kroxylicious#3896)

* test(admission): add E2E test for disabled feature gates

Add a second E2E test that exercises the webhook with
SidecarContainers=false,ImageVolume=false to verify correct behaviour
on older Kubernetes clusters: emptyDir plugin volumes, plugin-copy init
containers, and regular (non-native) sidecar containers.

Restructure the test lifecycle so Strimzi and Kafka are installed once
per class (@testinstance PER_CLASS) while the webhook is installed fresh
for each test with the appropriate FEATURE_GATES env var.

Plugin images must now place JARs in /plugins/ rather than at the image
root. This avoids the fragile `find / -maxdepth 1` command that was
needed to skip /proc, /sys, /dev when copying from non-scratch images.

The field table was difficult to keep in sync with the CRD. Point to
the CRD YAML as the source of truth instead. Also fixes the CRD
cross-reference path and group name.

Assisted-by: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>
* Address review feedback on sidecar injection webhook

- Rename injection-skipped label values: no-KroxyliciousSidecarConfig,
  ambiguous-KroxyliciousSidecarConfig, container-name-conflict
- Add Ready=False status condition for invalid KroxyliciousSidecarConfig
  with validation in SidecarConfigResolver
- Rename FEATURE_GATES env var to K8S_FEATURE_GATES
- Set seccompProfile: RuntimeDefault on sidecar and plugin init containers
- Update end user docs and README

* Replace FAILURE_POLICY with UNINJECTED_POD_POLICY, delegate TLS to Netty

Remove the app-level FAILURE_POLICY env var — on unexpected exceptions the
webhook now returns HTTP 500, letting the K8s MutatingWebhookConfiguration
failurePolicy govern the outcome.

Add UNINJECTED_POD_POLICY (Admit/Deny, default Admit) to control whether
pods are admitted or rejected when sidecar injection cannot proceed due to
missing or ambiguous KroxyliciousSidecarConfig.

Refactor WebhookServer TLS to delegate to Netty's SslContextBuilder,
removing hand-rolled PKCS#8-only PEM parsing. The webhook now supports
the same key formats as the proxy.

Assisted-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Tom Bentley <tbentley@redhat.com>
…roxylicious#3879)

* docs(admission): add sidecar injection demo with message uppercasing

Creates a complete example demonstrating the admission webhook's sidecar
injection capability with a message transformation use case. The example
includes a Kafka cluster, a KroxyliciousSidecarConfig that applies
UpperCasing filter to Produces and Replacing filter to Fetches.

The README provides step-by-step instructions from Strimzi installation
through deployment and verification, including how to consume messages
to see the transformations in action.

This example is intended for users who have already installed the
admission webhook and want to see how sidecar injection works end-to-end.

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Robert Young <robertyoungnz@gmail.com>

* build(admission): add run-webhook.sh development script

Automates clean installation of the admission webhook in minikube for
quick development testing. Script performs: minikube reset, Maven
build, image loading, cert-manager setup, webhook installation, and
sidecar injection demo deployment.

Designed for developer use with fail-fast behavior and no idempotence.
Includes verification commands for sidecar injection and message
transformation.

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Robert Young <robertyoungnz@gmail.com>

---------

Signed-off-by: Robert Young <robertyoungnz@gmail.com>
…roxylicious#3904)

* test(admission): add system test for admission webhook installation

Adds Phase 1 system test that validates the admission webhook can be
deployed from distribution manifests. The test:

- Creates AdmissionWebhook installer class that applies CRD and install
  manifests, creates cert-manager Issuer and Certificate for TLS, and
  waits for webhook deployment to be ready
- Adds AdmissionWebhookST test that deploys cert-manager and webhook,
  then verifies deployment has 2 ready replicas and MutatingWebhookConfiguration
  has CA bundle injected
- Configures Maven to unpack admission-dist artifact for test use

Phase 2 will add sidecar injection testing with KroxyliciousSidecarConfig
and message transformation verification.

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Robert Young <robertyoungnz@gmail.com>

* Review feedback and fix static analysis issues

Signed-off-by: Robert Young <robertyoungnz@gmail.com>

* Install admission webhook before system testing

Signed-off-by: Robert Young <robertyoungnz@gmail.com>

* Apply review feedback

Signed-off-by: Robert Young <robertyoungnz@gmail.com>

* Review feedback

Signed-off-by: Robert Young <robertyoungnz@gmail.com>

* Tag admission test for maintainable exclusion

Signed-off-by: Robert Young <robertyoungnz@gmail.com>

---------

Signed-off-by: Robert Young <robertyoungnz@gmail.com>
@tombentley tombentley requested a review from a team as a code owner May 11, 2026 04:11
@robobario
Copy link
Copy Markdown
Member

robobario commented May 11, 2026

damn it. all the "squash merges" I've been doing have taken my redhat.com email as author and it's making DCO flip out. I doubt I'll be able to forcepush to your fork to fix it up.

@tombentley
Copy link
Copy Markdown
Member Author

@robobario I'll close this one then

@tombentley tombentley closed this May 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants