Skip to content

Commit 5f5142d

Browse files
perdasilvaPer Goncalves da Silva
andauthored
Fix webhook certificate bug (#2107)
Signed-off-by: Per Goncalves da Silva <[email protected]> Co-authored-by: Per Goncalves da Silva <[email protected]>
1 parent 850e4a1 commit 5f5142d

File tree

2 files changed

+119
-52
lines changed

2 files changed

+119
-52
lines changed

internal/operator-controller/rukpak/render/registryv1/generators/generators.go

Lines changed: 67 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package generators
33
import (
44
"cmp"
55
"fmt"
6-
"maps"
76
"slices"
87
"strconv"
98
"strings"
@@ -28,15 +27,31 @@ import (
2827
)
2928

3029
const (
31-
tlsCrtPath = "tls.crt"
32-
tlsKeyPath = "tls.key"
33-
3430
labelKubernetesNamespaceMetadataName = "kubernetes.io/metadata.name"
3531
)
3632

37-
// volume mount name -> mount path
38-
var certVolumeMounts = map[string]string{
39-
"webhook-cert": "/tmp/k8s-webhook-server/serving-certs",
33+
type certVolumeConfig struct {
34+
Name string
35+
Path string
36+
TLSCertPath string
37+
TLSKeyPath string
38+
}
39+
40+
// certVolumeConfigs contain the expected configurations for certificate volume/mounts
41+
// that the generated Deployment resources for bundle containing webhooks and/or apiservices
42+
// should contain.
43+
var certVolumeConfigs = []certVolumeConfig{
44+
{
45+
Name: "webhook-cert",
46+
Path: "/tmp/k8s-webhook-server/serving-certs",
47+
TLSCertPath: "tls.crt",
48+
TLSKeyPath: "tls.key",
49+
}, {
50+
Name: "apiservice-cert",
51+
Path: "/apiserver.local.config/certificates",
52+
TLSCertPath: "apiserver.crt",
53+
TLSKeyPath: "apiserver.key",
54+
},
4055
}
4156

4257
// BundleCSVDeploymentGenerator generates all deployments defined in rv1's cluster service version (CSV). The generated
@@ -80,7 +95,7 @@ func BundleCSVDeploymentGenerator(rv1 *bundle.RegistryV1, opts render.Options) (
8095

8196
secretInfo := render.CertProvisionerFor(depSpec.Name, opts).GetCertSecretInfo()
8297
if webhookDeployments.Has(depSpec.Name) && secretInfo != nil {
83-
addCertVolumesToDeployment(deploymentResource, *secretInfo)
98+
ensureCorrectDeploymentCertVolumes(deploymentResource, *secretInfo)
8499
}
85100

86101
objs = append(objs, deploymentResource)
@@ -488,60 +503,67 @@ func getWebhookServicePort(wh v1alpha1.WebhookDescription) corev1.ServicePort {
488503
}
489504
}
490505

491-
func addCertVolumesToDeployment(dep *appsv1.Deployment, certSecretInfo render.CertSecretInfo) {
492-
volumeMountsToReplace := sets.New(slices.Collect(maps.Keys(certVolumeMounts))...)
493-
certVolumeMountPaths := sets.New(slices.Collect(maps.Values(certVolumeMounts))...)
506+
// ensureCorrectDeploymentCertVolumes ensures the deployment has the correct certificate volume mounts by
507+
// - removing all existing volumes with protected certificate volume names (i.e. webhook-cert and apiservice-cert)
508+
// - removing all existing volumes that point to the protected certificate paths (e.g. /tmp/k8s-webhook-server/serving-certs)
509+
// - adding the correct certificate volumes with the correct configuration
510+
// - applying the same changes to all container volume mounts
511+
func ensureCorrectDeploymentCertVolumes(dep *appsv1.Deployment, certSecretInfo render.CertSecretInfo) {
512+
// collect volumes and paths to replace
513+
volumesToRemove := sets.New[string]()
514+
protectedVolumePaths := sets.New[string]()
515+
certVolumes := make([]corev1.Volume, 0, len(certVolumeConfigs))
516+
certVolumeMounts := make([]corev1.VolumeMount, 0, len(certVolumeConfigs))
517+
for _, cfg := range certVolumeConfigs {
518+
volumesToRemove.Insert(cfg.Name)
519+
protectedVolumePaths.Insert(cfg.Path)
520+
certVolumes = append(certVolumes, corev1.Volume{
521+
Name: cfg.Name,
522+
VolumeSource: corev1.VolumeSource{
523+
Secret: &corev1.SecretVolumeSource{
524+
SecretName: certSecretInfo.SecretName,
525+
Items: []corev1.KeyToPath{
526+
{
527+
Key: certSecretInfo.CertificateKey,
528+
Path: cfg.TLSCertPath,
529+
},
530+
{
531+
Key: certSecretInfo.PrivateKeyKey,
532+
Path: cfg.TLSKeyPath,
533+
},
534+
},
535+
},
536+
},
537+
})
538+
certVolumeMounts = append(certVolumeMounts, corev1.VolumeMount{
539+
Name: cfg.Name,
540+
MountPath: cfg.Path,
541+
})
542+
}
543+
494544
for _, c := range dep.Spec.Template.Spec.Containers {
495545
for _, containerVolumeMount := range c.VolumeMounts {
496-
if certVolumeMountPaths.Has(containerVolumeMount.MountPath) {
497-
volumeMountsToReplace.Insert(containerVolumeMount.Name)
546+
if protectedVolumePaths.Has(containerVolumeMount.MountPath) {
547+
volumesToRemove.Insert(containerVolumeMount.Name)
498548
}
499549
}
500550
}
501551

502552
// update pod volumes
503553
dep.Spec.Template.Spec.Volumes = slices.Concat(
504554
slices.DeleteFunc(dep.Spec.Template.Spec.Volumes, func(v corev1.Volume) bool {
505-
return volumeMountsToReplace.Has(v.Name)
555+
return volumesToRemove.Has(v.Name)
506556
}),
507-
[]corev1.Volume{
508-
{
509-
Name: "webhook-cert",
510-
VolumeSource: corev1.VolumeSource{
511-
Secret: &corev1.SecretVolumeSource{
512-
SecretName: certSecretInfo.SecretName,
513-
Items: []corev1.KeyToPath{
514-
{
515-
Key: certSecretInfo.CertificateKey,
516-
Path: tlsCrtPath,
517-
},
518-
{
519-
Key: certSecretInfo.PrivateKeyKey,
520-
Path: tlsKeyPath,
521-
},
522-
},
523-
},
524-
},
525-
},
526-
},
557+
certVolumes,
527558
)
528559

529560
// update container volume mounts
530561
for i := range dep.Spec.Template.Spec.Containers {
531562
dep.Spec.Template.Spec.Containers[i].VolumeMounts = slices.Concat(
532563
slices.DeleteFunc(dep.Spec.Template.Spec.Containers[i].VolumeMounts, func(v corev1.VolumeMount) bool {
533-
return volumeMountsToReplace.Has(v.Name)
564+
return volumesToRemove.Has(v.Name)
534565
}),
535-
func() []corev1.VolumeMount {
536-
volumeMounts := make([]corev1.VolumeMount, 0, len(certVolumeMounts))
537-
for _, name := range slices.Sorted(maps.Keys(certVolumeMounts)) {
538-
volumeMounts = append(volumeMounts, corev1.VolumeMount{
539-
Name: name,
540-
MountPath: certVolumeMounts[name],
541-
})
542-
}
543-
return volumeMounts
544-
}(),
566+
certVolumeMounts,
545567
)
546568
}
547569
}

internal/operator-controller/rukpak/render/registryv1/generators/generators_test.go

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -188,15 +188,29 @@ func Test_BundleCSVDeploymentGenerator_WithCertWithCertProvider_Succeeds(t *test
188188
Template: corev1.PodTemplateSpec{
189189
Spec: corev1.PodSpec{
190190
Volumes: []corev1.Volume{
191+
// volume that have neither protected names: webhook-cert and apiservice-cert,
192+
// or target protected certificate paths should remain untouched
191193
{
192194
Name: "some-other-mount",
193195
VolumeSource: corev1.VolumeSource{
194196
EmptyDir: &corev1.EmptyDirVolumeSource{},
195197
},
196198
},
197-
// this volume should be replaced by the webhook-cert volume
198-
// because it has a volume mount targeting the protected path
199-
// /tmp/k8s-webhook-server/serving-certs
199+
// volume mounts with protected names will be rewritten to ensure they point to
200+
// the right certificate path. If they do not exist, they will be created.
201+
{
202+
Name: "webhook-cert",
203+
VolumeSource: corev1.VolumeSource{
204+
EmptyDir: &corev1.EmptyDirVolumeSource{},
205+
},
206+
},
207+
// volumes that point to protected paths will be removed
208+
{
209+
Name: "some-mount",
210+
VolumeSource: corev1.VolumeSource{
211+
EmptyDir: &corev1.EmptyDirVolumeSource{},
212+
},
213+
},
200214
{
201215
Name: "some-webhook-cert-mount",
202216
VolumeSource: corev1.VolumeSource{
@@ -208,19 +222,24 @@ func Test_BundleCSVDeploymentGenerator_WithCertWithCertProvider_Succeeds(t *test
208222
{
209223
Name: "container-1",
210224
VolumeMounts: []corev1.VolumeMount{
211-
// the mount path for this volume mount will be replaced with
212-
// /tmp/k8s-webhook-server/serving-certs
225+
// the mount path for the following volume will be replaced
226+
// since the volume name is protected
213227
{
214228
Name: "webhook-cert",
215229
MountPath: "/webhook-cert-path",
216-
}, {
230+
},
231+
// the following volume will be preserved
232+
{
217233
Name: "some-other-mount",
218234
MountPath: "/some/other/mount/path",
219235
},
220-
// this volume mount will be removed
236+
// these volume mount will be removed for referencing protected cert paths
221237
{
222238
Name: "some-webhook-cert-mount",
223239
MountPath: "/tmp/k8s-webhook-server/serving-certs",
240+
}, {
241+
Name: "some-mount",
242+
MountPath: "/apiserver.local.config/certificates",
224243
},
225244
},
226245
},
@@ -272,6 +291,24 @@ func Test_BundleCSVDeploymentGenerator_WithCertWithCertProvider_Succeeds(t *test
272291
},
273292
},
274293
},
294+
{
295+
Name: "apiservice-cert",
296+
VolumeSource: corev1.VolumeSource{
297+
Secret: &corev1.SecretVolumeSource{
298+
SecretName: "some-secret",
299+
Items: []corev1.KeyToPath{
300+
{
301+
Key: "some-cert-key",
302+
Path: "apiserver.crt",
303+
},
304+
{
305+
Key: "some-private-key-key",
306+
Path: "apiserver.key",
307+
},
308+
},
309+
},
310+
},
311+
},
275312
}, deployment.Spec.Template.Spec.Volumes)
276313
require.Equal(t, []corev1.Container{
277314
{
@@ -285,6 +322,10 @@ func Test_BundleCSVDeploymentGenerator_WithCertWithCertProvider_Succeeds(t *test
285322
Name: "webhook-cert",
286323
MountPath: "/tmp/k8s-webhook-server/serving-certs",
287324
},
325+
{
326+
Name: "apiservice-cert",
327+
MountPath: "/apiserver.local.config/certificates",
328+
},
288329
},
289330
},
290331
{
@@ -294,6 +335,10 @@ func Test_BundleCSVDeploymentGenerator_WithCertWithCertProvider_Succeeds(t *test
294335
Name: "webhook-cert",
295336
MountPath: "/tmp/k8s-webhook-server/serving-certs",
296337
},
338+
{
339+
Name: "apiservice-cert",
340+
MountPath: "/apiserver.local.config/certificates",
341+
},
297342
},
298343
},
299344
}, deployment.Spec.Template.Spec.Containers)

0 commit comments

Comments
 (0)