Add stackit-pod-identity-webhook image and configuration#53
Add stackit-pod-identity-webhook image and configuration#53
Conversation
|
Skipping CI for Draft Pull Request. |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
28ea07b to
fb04800
Compare
fb04800 to
6d649ce
Compare
|
Oops, accidentally marked this PR as ready for review, sorry about that. |
timebertt
left a comment
There was a problem hiding this comment.
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" |
There was a problem hiding this comment.
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 }} |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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" .) }} |
There was a problem hiding this comment.
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
pkg/stackit/types.go
Outdated
| DirectionIngress = "ingress" | ||
|
|
||
| // STACKITPodIdentityWebhookName is a constant for the name of the Pod Identity Webhook. (stackit) | ||
| STACKITPodIdentityWebhookName = "stackit-pod-identity-webhook" |
There was a problem hiding this comment.
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{ |
| return nil | ||
| } | ||
|
|
||
| func getSTACKITPodIdentityWebhookChartValues( |
There was a problem hiding this comment.
Nit: consider removing STACKIT from these function names to increase readability
| } | ||
|
|
||
| return map[string]any{ | ||
| "replicaCount": extensionscontroller.GetControlPlaneReplicas(cluster, scaledDown, 2), |
There was a problem hiding this comment.
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]), |
There was a problem hiding this comment.
Consider passing the raw PEM bundle here and encoding it as base64 in the chart template.
There was a problem hiding this comment.
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-webhookimage 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.
| return map[string]any{ | ||
| "webhook": map[string]any{ | ||
| "caBundle": gardenerutils.EncodeBase64(caSecret.Data[secretutils.DataKeyCertificateBundle]), |
There was a problem hiding this comment.
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.
| 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), |
| stackitPodIdentityWebhookChartShootValues := map[string]any{ | ||
| "webhook": map[string]any{ | ||
| "caBundle": "", | ||
| }, | ||
| } |
There was a problem hiding this comment.
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.
| app.kubernetes.io/name: {{ .Chart.Name }} | ||
| helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }} | ||
| app.kubernetes.io/instance: {{ .Release.Name }} | ||
| automountServiceAccountToken: false |
There was a problem hiding this comment.
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.
| automountServiceAccountToken: false |
| kind: MutatingWebhookConfiguration | ||
| metadata: | ||
| name: {{ include "stackit-pod-identity-webhook.fullname" . }} | ||
| namespace: {{ .Release.Namespace }} |
There was a problem hiding this comment.
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).
| namespace: {{ .Release.Namespace }} |
| service: | ||
| name: {{ include "stackit-pod-identity-webhook.fullname" . }} | ||
| namespace: {{ .Release.Namespace }} | ||
| path: "/mutate--v1-pod" |
There was a problem hiding this comment.
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.
| 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 }} |
| resources: ["pods"] | ||
| admissionReviewVersions: ["v1"] | ||
| sideEffects: None | ||
| failurePolicy: {{ .Values.webhook.failurePolicy | default "Ignore" }} |
There was a problem hiding this comment.
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.
| failurePolicy: {{ .Values.webhook.failurePolicy | default "Ignore" }} | |
| failurePolicy: {{ .Values.webhook.failurePolicy | default "Fail" }} |
| selector: | ||
| app.kubernetes.io/name: {{ .Chart.Name }} | ||
| app.kubernetes.io/instance: {{ .Release.Name }} |
There was a problem hiding this comment.
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).
| memory: 64Mi | ||
|
|
||
| serviceAccount: | ||
| create: true |
There was a problem hiding this comment.
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.
| create: true |
…loyment since they're static
…remove dynamic name generation
…e helpers template
…ode for pod identity webhook service
…base64 encoded version in webhook config and values provider
… for pod-identity-webhook tests
… identity webhook
…ead of shoot name
…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.
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.