Skip to content

Commit 7607345

Browse files
author
Per Goncalves da Silva
committed
Fix webhook certificate bug
Signed-off-by: Per Goncalves da Silva <[email protected]>
1 parent 0491775 commit 7607345

File tree

2 files changed

+127
-52
lines changed

2 files changed

+127
-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 the webhook and apiservice certificate volumes
41+
// a correctly generated deployment for bundles containing webhooks or apiservices should reference the
42+
// certificate volumes using the configurations below
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: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -188,15 +188,37 @@ 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
201+
{
202+
Name: "webhook-cert",
203+
VolumeSource: corev1.VolumeSource{
204+
EmptyDir: &corev1.EmptyDirVolumeSource{},
205+
},
206+
},
207+
// if they do not exist, they will be created
208+
//{
209+
// Name: "apiservice-cert",
210+
// VolumeSource: corev1.VolumeSource{
211+
// EmptyDir: &corev1.EmptyDirVolumeSource{},
212+
// },
213+
//},
214+
215+
// volumes that point to protected paths will be removed
216+
{
217+
Name: "some-mount",
218+
VolumeSource: corev1.VolumeSource{
219+
EmptyDir: &corev1.EmptyDirVolumeSource{},
220+
},
221+
},
200222
{
201223
Name: "some-webhook-cert-mount",
202224
VolumeSource: corev1.VolumeSource{
@@ -208,19 +230,24 @@ func Test_BundleCSVDeploymentGenerator_WithCertWithCertProvider_Succeeds(t *test
208230
{
209231
Name: "container-1",
210232
VolumeMounts: []corev1.VolumeMount{
211-
// the mount path for this volume mount will be replaced with
212-
// /tmp/k8s-webhook-server/serving-certs
233+
// the mount path for the following volume will be replaced
234+
// since the volume name is protected
213235
{
214236
Name: "webhook-cert",
215237
MountPath: "/webhook-cert-path",
216-
}, {
238+
},
239+
// the following volume will be preserved
240+
{
217241
Name: "some-other-mount",
218242
MountPath: "/some/other/mount/path",
219243
},
220-
// this volume mount will be removed
244+
// these volume mount will be removed for referencing protected cert paths
221245
{
222246
Name: "some-webhook-cert-mount",
223247
MountPath: "/tmp/k8s-webhook-server/serving-certs",
248+
}, {
249+
Name: "some-mount",
250+
MountPath: "/apiserver.local.config/certificates",
224251
},
225252
},
226253
},
@@ -272,6 +299,24 @@ func Test_BundleCSVDeploymentGenerator_WithCertWithCertProvider_Succeeds(t *test
272299
},
273300
},
274301
},
302+
{
303+
Name: "apiservice-cert",
304+
VolumeSource: corev1.VolumeSource{
305+
Secret: &corev1.SecretVolumeSource{
306+
SecretName: "some-secret",
307+
Items: []corev1.KeyToPath{
308+
{
309+
Key: "some-cert-key",
310+
Path: "apiserver.crt",
311+
},
312+
{
313+
Key: "some-private-key-key",
314+
Path: "apiserver.key",
315+
},
316+
},
317+
},
318+
},
319+
},
275320
}, deployment.Spec.Template.Spec.Volumes)
276321
require.Equal(t, []corev1.Container{
277322
{
@@ -285,6 +330,10 @@ func Test_BundleCSVDeploymentGenerator_WithCertWithCertProvider_Succeeds(t *test
285330
Name: "webhook-cert",
286331
MountPath: "/tmp/k8s-webhook-server/serving-certs",
287332
},
333+
{
334+
Name: "apiservice-cert",
335+
MountPath: "/apiserver.local.config/certificates",
336+
},
288337
},
289338
},
290339
{
@@ -294,6 +343,10 @@ func Test_BundleCSVDeploymentGenerator_WithCertWithCertProvider_Succeeds(t *test
294343
Name: "webhook-cert",
295344
MountPath: "/tmp/k8s-webhook-server/serving-certs",
296345
},
346+
{
347+
Name: "apiservice-cert",
348+
MountPath: "/apiserver.local.config/certificates",
349+
},
297350
},
298351
},
299352
}, deployment.Spec.Template.Spec.Containers)

0 commit comments

Comments
 (0)