Skip to content

Commit e0b86dc

Browse files
authored
Merge pull request #4429 from camilamacedo86/webhook-watcher
✨ feat: add webhook CertWatcher and flags for custom certificate configuration
2 parents 43ee3f6 + 489145d commit e0b86dc

File tree

27 files changed

+513
-192
lines changed

27 files changed

+513
-192
lines changed

.github/workflows/test-e2e-samples.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ jobs:
4242
KUSTOMIZATION_FILE_PATH="testdata/project-v4/config/default/kustomization.yaml"
4343
sed -i '25s/^#//' $KUSTOMIZATION_FILE_PATH
4444
# Uncomment all cert-manager injections
45-
sed -i '55,168s/^#//' $KUSTOMIZATION_FILE_PATH
46-
sed -i '170,185s/^#//' $KUSTOMIZATION_FILE_PATH
45+
sed -i '57,170s/^#//' $KUSTOMIZATION_FILE_PATH
46+
sed -i '172,187s/^#//' $KUSTOMIZATION_FILE_PATH
4747
cd testdata/project-v4/
4848
go mod tidy
4949
@@ -85,10 +85,10 @@ jobs:
8585
# Uncomment only ValidatingWebhookConfiguration
8686
# from cert-manager replaces; we are leaving defaulting uncommented
8787
# since this sample has no defaulting webhooks
88-
sed -i '55,121s/^#//' $KUSTOMIZATION_FILE_PATH
88+
sed -i '57,123s/^#//' $KUSTOMIZATION_FILE_PATH
8989
# Uncomment only --conversion webhooks CA injection
90-
sed -i '153,168s/^#//' $KUSTOMIZATION_FILE_PATH
91-
sed -i '170,185s/^#//' $KUSTOMIZATION_FILE_PATH
90+
sed -i '155,170s/^#//' $KUSTOMIZATION_FILE_PATH
91+
sed -i '172,187s/^#//' $KUSTOMIZATION_FILE_PATH
9292
cd testdata/project-v4-with-plugins/
9393
go mod tidy
9494
@@ -128,8 +128,8 @@ jobs:
128128
KUSTOMIZATION_FILE_PATH="testdata/project-v4-multigroup/config/default/kustomization.yaml"
129129
sed -i '25s/^#//' $KUSTOMIZATION_FILE_PATH
130130
# Uncomment all cert-manager injections
131-
sed -i '55,168s/^#//' $KUSTOMIZATION_FILE_PATH
132-
sed -i '170,185s/^#//' $KUSTOMIZATION_FILE_PATH
131+
sed -i '57,170s/^#//' $KUSTOMIZATION_FILE_PATH
132+
sed -i '172,187s/^#//' $KUSTOMIZATION_FILE_PATH
133133
cd testdata/project-v4-multigroup
134134
go mod tidy
135135

docs/book/src/cronjob-tutorial/testdata/project/cmd/main.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"crypto/tls"
2222
"flag"
2323
"os"
24+
"path/filepath"
2425

2526
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
2627
// to ensure that exec-entrypoint and run can make use of them.
@@ -30,6 +31,7 @@ import (
3031
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
3132
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
3233
ctrl "sigs.k8s.io/controller-runtime"
34+
"sigs.k8s.io/controller-runtime/pkg/certwatcher"
3335
"sigs.k8s.io/controller-runtime/pkg/healthz"
3436
"sigs.k8s.io/controller-runtime/pkg/log/zap"
3537
"sigs.k8s.io/controller-runtime/pkg/metrics/filters"
@@ -74,6 +76,7 @@ func main() {
7476
/*
7577
*/
7678
var metricsAddr string
79+
var webhookCertPath, webhookCertName, webhookCertKey string
7780
var enableLeaderElection bool
7881
var probeAddr string
7982
var secureMetrics bool
@@ -87,6 +90,9 @@ func main() {
8790
"Enabling this will ensure there is only one active controller manager.")
8891
flag.BoolVar(&secureMetrics, "metrics-secure", true,
8992
"If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.")
93+
flag.StringVar(&webhookCertPath, "webhook-cert-path", "", "The directory that contains the webhook certificate.")
94+
flag.StringVar(&webhookCertName, "webhook-cert-name", "tls.crt", "The name of the webhook certificate file.")
95+
flag.StringVar(&webhookCertKey, "webhook-cert-key", "tls.key", "The name of the webhook key file.")
9096
flag.BoolVar(&enableHTTP2, "enable-http2", false,
9197
"If set, HTTP/2 will be enabled for the metrics and webhook servers")
9298
opts := zap.Options{
@@ -112,8 +118,33 @@ func main() {
112118
tlsOpts = append(tlsOpts, disableHTTP2)
113119
}
114120

121+
// Create watchers for metrics certificates
122+
var webhookCertWatcher *certwatcher.CertWatcher
123+
124+
// Initial webhook TLS options
125+
webhookTLSOpts := append([]func(*tls.Config){}, tlsOpts...)
126+
127+
if len(webhookCertPath) > 0 {
128+
setupLog.Info("Initializing webhook certificate watcher using provided certificates",
129+
"webhook-cert-path", webhookCertPath, "webhook-cert-name", webhookCertName, "webhook-cert-key", webhookCertKey)
130+
131+
var err error
132+
webhookCertWatcher, err = certwatcher.New(
133+
filepath.Join(webhookCertPath, webhookCertName),
134+
filepath.Join(webhookCertPath, webhookCertKey),
135+
)
136+
if err != nil {
137+
setupLog.Error(err, "Failed to initialize webhook certificate watcher")
138+
os.Exit(1)
139+
}
140+
141+
webhookTLSOpts = append(webhookTLSOpts, func(config *tls.Config) {
142+
config.GetCertificate = webhookCertWatcher.GetCertificate
143+
})
144+
}
145+
115146
webhookServer := webhook.NewServer(webhook.Options{
116-
TLSOpts: tlsOpts,
147+
TLSOpts: webhookTLSOpts,
117148
})
118149

119150
// Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server.
@@ -196,6 +227,14 @@ func main() {
196227
}
197228
// +kubebuilder:scaffold:builder
198229

230+
if webhookCertWatcher != nil {
231+
setupLog.Info("Adding webhook certificate watcher to manager")
232+
if err := mgr.Add(webhookCertWatcher); err != nil {
233+
setupLog.Error(err, "unable to add webhook certificate watcher to manager")
234+
os.Exit(1)
235+
}
236+
}
237+
199238
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
200239
setupLog.Error(err, "unable to set up health check")
201240
os.Exit(1)

docs/book/src/cronjob-tutorial/testdata/project/config/default/kustomization.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ patches:
4949
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
5050
# crd/kustomization.yaml
5151
- path: manager_webhook_patch.yaml
52+
target:
53+
kind: Deployment
5254

5355
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
5456
# Uncomment the following replacements to add the cert-manager CA injection annotations
Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,32 @@
1-
apiVersion: apps/v1
2-
kind: Deployment
3-
metadata:
4-
name: controller-manager
5-
namespace: system
6-
labels:
7-
app.kubernetes.io/name: project
8-
app.kubernetes.io/managed-by: kustomize
9-
spec:
10-
template:
11-
spec:
12-
containers:
13-
- name: manager
14-
ports:
15-
- containerPort: 9443
16-
name: webhook-server
17-
protocol: TCP
18-
volumeMounts:
19-
- mountPath: /tmp/k8s-webhook-server/serving-certs
20-
name: cert
21-
readOnly: true
22-
volumes:
23-
- name: cert
24-
secret:
25-
defaultMode: 420
26-
secretName: webhook-server-cert
1+
# This patch ensures the webhook certificates are properly mounted in the manager container.
2+
# It configures the necessary volume, volume mounts, and container ports.
3+
- op: add
4+
path: /spec/template/spec/containers/0/args/-
5+
value: --webhook-cert-path=/tmp/k8s-webhook-server/serving-certs
6+
- op: add
7+
path: /spec/template/spec/containers/0/volumeMounts
8+
value: []
9+
- op: add
10+
path: /spec/template/spec/containers/0/volumeMounts/-
11+
value:
12+
mountPath: /tmp/k8s-webhook-server/serving-certs
13+
name: webhook-certs
14+
readOnly: true
15+
- op: add
16+
path: /spec/template/spec/containers/0/ports
17+
value: []
18+
- op: add
19+
path: /spec/template/spec/containers/0/ports/-
20+
value:
21+
containerPort: 9443
22+
name: webhook-server
23+
protocol: TCP
24+
- op: add
25+
path: /spec/template/spec/volumes
26+
value: []
27+
- op: add
28+
path: /spec/template/spec/volumes/-
29+
value:
30+
name: webhook-certs
31+
secret:
32+
secretName: webhook-server-cert

docs/book/src/cronjob-tutorial/testdata/project/dist/install.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4118,6 +4118,7 @@ spec:
41184118
- --metrics-bind-address=:8443
41194119
- --leader-elect
41204120
- --health-probe-bind-address=:8081
4121+
- --webhook-cert-path=/tmp/k8s-webhook-server/serving-certs
41214122
command:
41224123
- /manager
41234124
image: controller:latest
@@ -4152,7 +4153,7 @@ spec:
41524153
- ALL
41534154
volumeMounts:
41544155
- mountPath: /tmp/k8s-webhook-server/serving-certs
4155-
name: cert
4156+
name: webhook-certs
41564157
readOnly: true
41574158
securityContext:
41584159
runAsNonRoot: true
@@ -4161,9 +4162,8 @@ spec:
41614162
serviceAccountName: project-controller-manager
41624163
terminationGracePeriodSeconds: 10
41634164
volumes:
4164-
- name: cert
4165+
- name: webhook-certs
41654166
secret:
4166-
defaultMode: 420
41674167
secretName: webhook-server-cert
41684168
---
41694169
apiVersion: admissionregistration.k8s.io/v1

docs/book/src/getting-started/testdata/project/cmd/main.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"crypto/tls"
2121
"flag"
2222
"os"
23+
"path/filepath"
2324

2425
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
2526
// to ensure that exec-entrypoint and run can make use of them.
@@ -29,6 +30,7 @@ import (
2930
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
3031
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
3132
ctrl "sigs.k8s.io/controller-runtime"
33+
"sigs.k8s.io/controller-runtime/pkg/certwatcher"
3234
"sigs.k8s.io/controller-runtime/pkg/healthz"
3335
"sigs.k8s.io/controller-runtime/pkg/log/zap"
3436
"sigs.k8s.io/controller-runtime/pkg/metrics/filters"
@@ -54,6 +56,7 @@ func init() {
5456

5557
func main() {
5658
var metricsAddr string
59+
var webhookCertPath, webhookCertName, webhookCertKey string
5760
var enableLeaderElection bool
5861
var probeAddr string
5962
var secureMetrics bool
@@ -67,6 +70,9 @@ func main() {
6770
"Enabling this will ensure there is only one active controller manager.")
6871
flag.BoolVar(&secureMetrics, "metrics-secure", true,
6972
"If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.")
73+
flag.StringVar(&webhookCertPath, "webhook-cert-path", "", "The directory that contains the webhook certificate.")
74+
flag.StringVar(&webhookCertName, "webhook-cert-name", "tls.crt", "The name of the webhook certificate file.")
75+
flag.StringVar(&webhookCertKey, "webhook-cert-key", "tls.key", "The name of the webhook key file.")
7076
flag.BoolVar(&enableHTTP2, "enable-http2", false,
7177
"If set, HTTP/2 will be enabled for the metrics and webhook servers")
7278
opts := zap.Options{
@@ -92,8 +98,33 @@ func main() {
9298
tlsOpts = append(tlsOpts, disableHTTP2)
9399
}
94100

101+
// Create watchers for metrics certificates
102+
var webhookCertWatcher *certwatcher.CertWatcher
103+
104+
// Initial webhook TLS options
105+
webhookTLSOpts := append([]func(*tls.Config){}, tlsOpts...)
106+
107+
if len(webhookCertPath) > 0 {
108+
setupLog.Info("Initializing webhook certificate watcher using provided certificates",
109+
"webhook-cert-path", webhookCertPath, "webhook-cert-name", webhookCertName, "webhook-cert-key", webhookCertKey)
110+
111+
var err error
112+
webhookCertWatcher, err = certwatcher.New(
113+
filepath.Join(webhookCertPath, webhookCertName),
114+
filepath.Join(webhookCertPath, webhookCertKey),
115+
)
116+
if err != nil {
117+
setupLog.Error(err, "Failed to initialize webhook certificate watcher")
118+
os.Exit(1)
119+
}
120+
121+
webhookTLSOpts = append(webhookTLSOpts, func(config *tls.Config) {
122+
config.GetCertificate = webhookCertWatcher.GetCertificate
123+
})
124+
}
125+
95126
webhookServer := webhook.NewServer(webhook.Options{
96-
TLSOpts: tlsOpts,
127+
TLSOpts: webhookTLSOpts,
97128
})
98129

99130
// Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server.
@@ -158,6 +189,14 @@ func main() {
158189
}
159190
// +kubebuilder:scaffold:builder
160191

192+
if webhookCertWatcher != nil {
193+
setupLog.Info("Adding webhook certificate watcher to manager")
194+
if err := mgr.Add(webhookCertWatcher); err != nil {
195+
setupLog.Error(err, "unable to add webhook certificate watcher to manager")
196+
os.Exit(1)
197+
}
198+
}
199+
161200
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
162201
setupLog.Error(err, "unable to set up health check")
163202
os.Exit(1)

docs/book/src/getting-started/testdata/project/config/default/kustomization.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ patches:
4949
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
5050
# crd/kustomization.yaml
5151
#- path: manager_webhook_patch.yaml
52+
# target:
53+
# kind: Deployment
5254

5355
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
5456
# Uncomment the following replacements to add the cert-manager CA injection annotations

docs/book/src/multiversion-tutorial/testdata/project/cmd/main.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"crypto/tls"
2222
"flag"
2323
"os"
24+
"path/filepath"
2425

2526
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
2627
// to ensure that exec-entrypoint and run can make use of them.
@@ -31,6 +32,7 @@ import (
3132
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
3233
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
3334
ctrl "sigs.k8s.io/controller-runtime"
35+
"sigs.k8s.io/controller-runtime/pkg/certwatcher"
3436
"sigs.k8s.io/controller-runtime/pkg/healthz"
3537
"sigs.k8s.io/controller-runtime/pkg/log/zap"
3638
"sigs.k8s.io/controller-runtime/pkg/metrics/filters"
@@ -73,6 +75,7 @@ func main() {
7375
/*
7476
*/
7577
var metricsAddr string
78+
var webhookCertPath, webhookCertName, webhookCertKey string
7679
var enableLeaderElection bool
7780
var probeAddr string
7881
var secureMetrics bool
@@ -86,6 +89,9 @@ func main() {
8689
"Enabling this will ensure there is only one active controller manager.")
8790
flag.BoolVar(&secureMetrics, "metrics-secure", true,
8891
"If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.")
92+
flag.StringVar(&webhookCertPath, "webhook-cert-path", "", "The directory that contains the webhook certificate.")
93+
flag.StringVar(&webhookCertName, "webhook-cert-name", "tls.crt", "The name of the webhook certificate file.")
94+
flag.StringVar(&webhookCertKey, "webhook-cert-key", "tls.key", "The name of the webhook key file.")
8995
flag.BoolVar(&enableHTTP2, "enable-http2", false,
9096
"If set, HTTP/2 will be enabled for the metrics and webhook servers")
9197
opts := zap.Options{
@@ -111,8 +117,33 @@ func main() {
111117
tlsOpts = append(tlsOpts, disableHTTP2)
112118
}
113119

120+
// Create watchers for metrics certificates
121+
var webhookCertWatcher *certwatcher.CertWatcher
122+
123+
// Initial webhook TLS options
124+
webhookTLSOpts := append([]func(*tls.Config){}, tlsOpts...)
125+
126+
if len(webhookCertPath) > 0 {
127+
setupLog.Info("Initializing webhook certificate watcher using provided certificates",
128+
"webhook-cert-path", webhookCertPath, "webhook-cert-name", webhookCertName, "webhook-cert-key", webhookCertKey)
129+
130+
var err error
131+
webhookCertWatcher, err = certwatcher.New(
132+
filepath.Join(webhookCertPath, webhookCertName),
133+
filepath.Join(webhookCertPath, webhookCertKey),
134+
)
135+
if err != nil {
136+
setupLog.Error(err, "Failed to initialize webhook certificate watcher")
137+
os.Exit(1)
138+
}
139+
140+
webhookTLSOpts = append(webhookTLSOpts, func(config *tls.Config) {
141+
config.GetCertificate = webhookCertWatcher.GetCertificate
142+
})
143+
}
144+
114145
webhookServer := webhook.NewServer(webhook.Options{
115-
TLSOpts: tlsOpts,
146+
TLSOpts: webhookTLSOpts,
116147
})
117148

118149
// Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server.
@@ -199,6 +230,14 @@ func main() {
199230
/*
200231
*/
201232

233+
if webhookCertWatcher != nil {
234+
setupLog.Info("Adding webhook certificate watcher to manager")
235+
if err := mgr.Add(webhookCertWatcher); err != nil {
236+
setupLog.Error(err, "unable to add webhook certificate watcher to manager")
237+
os.Exit(1)
238+
}
239+
}
240+
202241
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
203242
setupLog.Error(err, "unable to set up health check")
204243
os.Exit(1)

0 commit comments

Comments
 (0)