Skip to content

Add stackit-pod-identity-webhook image and configuration#53

Draft
jastBytes wants to merge 42 commits intomainfrom
feat/STACKITSKE-6021-pod-identity-webhook
Draft

Add stackit-pod-identity-webhook image and configuration#53
jastBytes wants to merge 42 commits intomainfrom
feat/STACKITSKE-6021-pod-identity-webhook

Conversation

@jastBytes
Copy link
Contributor

@jastBytes jastBytes commented Mar 13, 2026

How to categorize this PR?

/kind enhancement

What this PR does / why we need it:
The changes introduce a new Pod Identity Webhook component for STACKIT cloud provider, adding a new chart for the webhook and its associated resources (Deployment, Service, RBAC, MutatingWebhookConfiguration). The webhook is configured to validate pod identities and enforce workload identity. The changes are integrated into the control plane and shoot system components, with proper configuration and error handling.

Special notes for your reviewer:

Breaking changes:

No breaking changes.

@ske-prow
Copy link

ske-prow bot commented Mar 13, 2026

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@ske-prow ske-prow bot added kind/enhancement Enhancement, improvement, extension do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. labels Mar 13, 2026
@ske-prow
Copy link

ske-prow bot commented Mar 13, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign xoxys for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ske-prow ske-prow bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Mar 13, 2026
@jastBytes jastBytes force-pushed the feat/STACKITSKE-6021-pod-identity-webhook branch from 28ea07b to fb04800 Compare March 23, 2026 10:50
@jastBytes jastBytes force-pushed the feat/STACKITSKE-6021-pod-identity-webhook branch from fb04800 to 6d649ce Compare March 23, 2026 13:13
@jastBytes jastBytes requested a review from timebertt March 24, 2026 09:24
@timebertt timebertt marked this pull request as ready for review March 25, 2026 07:24
Copilot AI review requested due to automatic review settings March 25, 2026 07:24
@ske-prow ske-prow bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Mar 25, 2026
@timebertt timebertt marked this pull request as draft March 25, 2026 07:24
@ske-prow ske-prow bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Mar 25, 2026
@timebertt
Copy link
Member

Oops, accidentally marked this PR as ready for review, sorry about that.

Copy link
Member

@timebertt timebertt left a comment

Choose a reason for hiding this comment

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

Thanks for the PR, looking forward to this feature :)

tag: "1245"
- name: stackit-pod-identity-webhook
repository: reg3.infra.ske.eu01.stackit.cloud/stackitcloud/stackit-pod-identity-webhook
tag: "726f2f0@sha256:fca1f67cd7e6a515e795a34ae45d0c239379d051e494dc202033f6987b41b154"
Copy link
Member

Choose a reason for hiding this comment

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

For the record (as discussed in chat): the stackit-pod-identity-webhook repository should be public and released before merging this integration PR in the extension.

name: {{ include "stackit-pod-identity-webhook.fullname" . }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "stackit-pod-identity-webhook.labels" . | nindent 4 }}
Copy link
Member

Choose a reason for hiding this comment

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

This should have the high-availability-config.resources.gardener.cloud/type=server label, ref https://github.com/gardener/gardener/blob/master/docs/development/component-checklist.md#high-availability--stability

{{- include "stackit-pod-identity-webhook.selectorLabels" . | nindent 8 }}
workload-identity.stackit.cloud/skip-pod-identity-webhook: "true"
gardener.cloud/role: controlplane
high-availability-config.resources.gardener.cloud/type: controller
Copy link
Member

Choose a reason for hiding this comment

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

This looks wrong here on the pod template.

high-availability-config.resources.gardener.cloud/type: controller
networking.gardener.cloud/to-dns: allowed
networking.gardener.cloud/to-public-networks: allowed
networking.gardener.cloud/to-private-networks: allowed
Copy link
Member

Choose a reason for hiding this comment

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

Why does this component need the to-public-networks and to-private-networks network policies?
I would expect that we only need a policy for talking to kube-apiserver (+dns) and allowing kube-apiserver to talk to this webhook server, see https://github.com/gardener/gardener/blob/master/docs/operations/network_policies.md#webhook-servers

networking.gardener.cloud/to-private-networks: allowed
networking.resources.gardener.cloud/to-kube-apiserver-tcp-443: allowed
spec:
serviceAccountName: {{ .Values.serviceAccount.name | default (include "stackit-pod-identity-webhook.fullname" .) }}
Copy link
Member

Choose a reason for hiding this comment

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

I'm almost certain that this component doesn't need to talk to the seed's API server, so it doesn't need a ServiceAccount. It should set automountServiceAccountToken=false and use a shoot access secret, ref https://github.com/gardener/gardener/blob/master/docs/development/component-checklist.md#security

DirectionIngress = "ingress"

// STACKITPodIdentityWebhookName is a constant for the name of the Pod Identity Webhook. (stackit)
STACKITPodIdentityWebhookName = "stackit-pod-identity-webhook"
Copy link
Member

Choose a reason for hiding this comment

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

Nit: the package name is already stackit, consider renaming this to PodIdentityWebhookName to remove the duplicated "STACKIT" from references.

{
Name: stackit.STACKITPodIdentityWebhookName,
Images: []string{imagevector.ImageNameStackitPodIdentityWebhook},
Objects: []*chart.Object{
Copy link
Member

Choose a reason for hiding this comment

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

This list looks incomplete.

return nil
}

func getSTACKITPodIdentityWebhookChartValues(
Copy link
Member

Choose a reason for hiding this comment

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

Nit: consider removing STACKIT from these function names to increase readability

}

return map[string]any{
"replicaCount": extensionscontroller.GetControlPlaneReplicas(cluster, scaledDown, 2),
Copy link
Member

Choose a reason for hiding this comment

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

Nit: typically 1 is specified here, because the high availability config webhook will manage the replica count


return map[string]any{
"webhook": map[string]any{
"caBundle": gardenerutils.EncodeBase64(caSecret.Data[secretutils.DataKeyCertificateBundle]),
Copy link
Member

Choose a reason for hiding this comment

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

Consider passing the raw PEM bundle here and encoding it as base64 in the chart template.

Copy link

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

This PR introduces a new STACKIT Pod Identity Webhook component and wires it into the extension’s seed control-plane and shoot system components charts/values so that workload identity can be enforced via a mutating admission webhook.

Changes:

  • Add a new stackit-pod-identity-webhook image entry and image name constant.
  • Add new Helm charts for the webhook (seed-controlplane: Deployment/Service/RBAC/PDB; shoot-system-components: MutatingWebhookConfiguration).
  • Extend the controlplane values provider and tests to generate TLS/CA-related chart values and required secrets.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
pkg/stackit/types.go Adds a constant for the webhook name.
pkg/controller/controlplane/valuesprovider.go Generates webhook TLS secret config and passes chart values for seed + shoot charts.
pkg/controller/controlplane/valuesprovider_test.go Extends expected chart values and fake secrets for the new component.
imagevector/images.yaml Adds the webhook image (repo/tag+digest).
imagevector/images.go Adds ImageNameStackitPodIdentityWebhook.
charts/internal/shoot-system-components/charts/stackit-pod-identity-webhook/* New shoot chart for MutatingWebhookConfiguration.
charts/internal/seed-controlplane/charts/stackit-pod-identity-webhook/* New seed chart for webhook runtime resources (Deployment/Service/RBAC/PDB).

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

Comment on lines +1321 to +1323
return map[string]any{
"webhook": map[string]any{
"caBundle": gardenerutils.EncodeBase64(caSecret.Data[secretutils.DataKeyCertificateBundle]),
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

caBundle is derived from caSecret.Data[secretutils.DataKeyCertificateBundle] without validating that the key exists / is non-empty. If the CA secret is malformed, this will silently configure an empty bundle and the webhook will fail TLS verification. Consider validating the presence of the certificate bundle and returning an explicit error if it's missing.

Suggested change
return map[string]any{
"webhook": map[string]any{
"caBundle": gardenerutils.EncodeBase64(caSecret.Data[secretutils.DataKeyCertificateBundle]),
caBundle, ok := caSecret.Data[secretutils.DataKeyCertificateBundle]
if !ok || len(caBundle) == 0 {
return nil, fmt.Errorf("secret %q is missing non-empty %q data", caNameControlPlane, secretutils.DataKeyCertificateBundle)
}
return map[string]any{
"webhook": map[string]any{
"caBundle": gardenerutils.EncodeBase64(caBundle),

Copilot uses AI. Check for mistakes.
Comment on lines +895 to +899
stackitPodIdentityWebhookChartShootValues := map[string]any{
"webhook": map[string]any{
"caBundle": "",
},
}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

The new shoot chart values test asserts caBundle is empty. Since the valuesprovider now base64-encodes the CA bundle from the CA secret, it would be better to populate the fake ca-provider-openstack-controlplane secret with a non-empty secretutils.DataKeyCertificateBundle and assert the expected base64 output, so the encoding and wiring are actually exercised.

Copilot uses AI. Check for mistakes.
app.kubernetes.io/name: {{ .Chart.Name }}
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app.kubernetes.io/instance: {{ .Release.Name }}
automountServiceAccountToken: false
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

The ServiceAccount sets automountServiceAccountToken: false, and the Pod spec does not override it or mount any kubeconfig/token. At the same time, RBAC is granted to read ServiceAccounts, which typically requires in-cluster authentication. If the webhook needs to call the Kubernetes API, it will fail without credentials. Either mount credentials explicitly / enable token automount for this workload, or remove the RBAC rules if no API access is required.

Suggested change
automountServiceAccountToken: false

Copilot uses AI. Check for mistakes.
kind: MutatingWebhookConfiguration
metadata:
name: {{ include "stackit-pod-identity-webhook.fullname" . }}
namespace: {{ .Release.Namespace }}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

MutatingWebhookConfiguration is cluster-scoped; setting metadata.namespace makes the manifest invalid and will be rejected by the API server. Remove the namespace field from metadata (keep clientConfig.service.namespace or switch to url as appropriate).

Suggested change
namespace: {{ .Release.Namespace }}

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +15
service:
name: {{ include "stackit-pod-identity-webhook.fullname" . }}
namespace: {{ .Release.Namespace }}
path: "/mutate--v1-pod"
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

clientConfig currently references a Service in .Release.Namespace, but this chart does not deploy that Service into the shoot cluster (the Service/Deployment are in the seed-controlplane chart). As a result, the webhook will be unreachable and pod admissions will fail. Additionally, the webhook config is missing clientConfig.caBundle even though .Values.webhook.caBundle is provided by the valuesprovider. Fix by either (a) deploying the webhook Service/Endpoints into the shoot cluster and referencing it here, or (b) switching to clientConfig.url that points to the webhook Service in the seed control-plane namespace, and set caBundle from .Values.webhook.caBundle.

Suggested change
service:
name: {{ include "stackit-pod-identity-webhook.fullname" . }}
namespace: {{ .Release.Namespace }}
path: "/mutate--v1-pod"
url: {{ .Values.webhook.url | quote }}
caBundle: {{ .Values.webhook.caBundle | quote }}

Copilot uses AI. Check for mistakes.
resources: ["pods"]
admissionReviewVersions: ["v1"]
sideEffects: None
failurePolicy: {{ .Values.webhook.failurePolicy | default "Ignore" }}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

The template defaults failurePolicy to Ignore, but the chart values/comments state that the default should be Fail to enforce workload identity. Align the default behavior (prefer defaulting to Fail here) to avoid accidentally weakening enforcement when failurePolicy isn't explicitly set.

Suggested change
failurePolicy: {{ .Values.webhook.failurePolicy | default "Ignore" }}
failurePolicy: {{ .Values.webhook.failurePolicy | default "Fail" }}

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +18
selector:
app.kubernetes.io/name: {{ .Chart.Name }}
app.kubernetes.io/instance: {{ .Release.Name }}
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

The Service selector uses app.kubernetes.io/name: {{ .Chart.Name }}, but the Deployment's pod labels come from include "stackit-pod-identity-webhook.selectorLabels", which can differ when nameOverride is set. This can break Service -> Pod selection. Use the same helper-generated selector labels in the Service (and keep labels/selectors consistent across resources).

Copilot uses AI. Check for mistakes.
memory: 64Mi

serviceAccount:
create: true
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

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

serviceAccount.create is defined in values but not used in the templates (the ServiceAccount is always created). Either honor this flag with a conditional around the ServiceAccount manifest or remove the value to avoid misleading configuration.

Suggested change
create: true

Copilot uses AI. Check for mistakes.
…base64 encoded version in webhook config and values provider
…entity webhook

Updated the service to prefer close traffic distribution and add endpoint slice hints to improve pod identity handling. Also adjusted the allowed ports to 9443 for the webhook service. Modified the mutating webhook configuration to exclude the gardener extension provider stackit to prevent conflicts.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. kind/enhancement Enhancement, improvement, extension size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants