-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
webhook.go
142 lines (130 loc) · 3.85 KB
/
webhook.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package cert
import (
"bytes"
"context"
"encoding/base64"
errors "golang.org/x/xerrors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
klog "k8s.io/klog/v2"
)
type WebhookType string
const (
ValidatingV1 WebhookType = "ValidatingV1"
ValidatingV1Beta1 WebhookType = "ValidatingV1Beta1"
MutatingV1 WebhookType = "MutatingV1"
MutatingV1Beta1 WebhookType = "MutatingV1Beta1"
)
type WebhookInfo struct {
Type WebhookType
Name string
}
type webhookManager struct {
webhooks []WebhookInfo
dyclient dynamic.Interface
}
func (w *webhookManager) ensureCA(ctx context.Context, caPem []byte) error {
for _, info := range w.webhooks {
if err := w.ensureWebhookCA(ctx, info, caPem); err != nil {
return errors.Errorf("ensure ca for webhook %s: %w", info.Name, err)
}
}
return nil
}
func (w *webhookManager) ensureWebhookCA(ctx context.Context, info WebhookInfo, caPem []byte) error {
gvs, err := info.Type.gvr()
if err != nil {
return errors.Errorf(": %w", err)
}
client := w.dyclient.Resource(*gvs)
obj, err := client.Get(ctx, info.Name, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
klog.Warningf("webhook %s is not found skip ensure ca", info.Name)
return nil
}
return err
}
changed, err := injectCertToWebhook(obj, caPem)
if err != nil {
return errors.Errorf("ensure ca for webhook %s: %w", info.Name, err)
}
if !changed {
klog.Warningf("no need to update ca for webhook %s", info.Name)
return nil
}
if _, err := client.Update(ctx, obj, metav1.UpdateOptions{}); err != nil {
return errors.Errorf("ensure ca for webhook %s: %w", info.Name, err)
}
return nil
}
func (t WebhookType) gvr() (*schema.GroupVersionResource, error) {
switch t {
case ValidatingV1:
return &schema.GroupVersionResource{
Group: "admissionregistration.k8s.io",
Version: "v1",
Resource: "validatingwebhookconfigurations",
}, nil
case ValidatingV1Beta1:
return &schema.GroupVersionResource{
Group: "admissionregistration.k8s.io",
Version: "v1beta1",
Resource: "validatingwebhookconfigurations",
}, nil
case MutatingV1:
return &schema.GroupVersionResource{
Group: "admissionregistration.k8s.io",
Version: "v1",
Resource: "mutatingwebhookconfigurations",
}, nil
case MutatingV1Beta1:
return &schema.GroupVersionResource{
Group: "admissionregistration.k8s.io",
Version: "v1beta1",
Resource: "mutatingwebhookconfigurations",
}, nil
}
return nil, errors.Errorf("unknown type: %s", t)
}
func injectCertToWebhook(wh *unstructured.Unstructured, caPem []byte) (changed bool, err error) {
webhooks, found, err := unstructured.NestedSlice(wh.Object, "webhooks")
if err != nil {
return false, errors.Errorf(": %w", err)
}
if !found {
return false, errors.Errorf("`webhooks` field not found in %s", wh.GetKind())
}
for i, h := range webhooks {
h := h
hook, ok := h.(map[string]interface{})
if !ok {
return false, errors.Errorf("webhook %d is not well-formed", i)
}
var oldPem []byte
oldCABundle, found, err := unstructured.NestedString(hook, "clientConfig", "caBundle")
if err == nil && found {
b, err := base64.StdEncoding.DecodeString(oldCABundle)
if err == nil && len(bytes.TrimSpace(b)) != 0 {
oldPem = b
}
}
ch, certPem := mergeCAPemCerts(oldPem, caPem)
if len(certPem) == 0 || !ch {
continue
} else {
changed = true
}
if err := unstructured.SetNestedField(hook, base64.StdEncoding.EncodeToString(certPem), "clientConfig", "caBundle"); err != nil {
return false, errors.Errorf(": %w", err)
}
webhooks[i] = hook
}
if err := unstructured.SetNestedSlice(wh.Object, webhooks, "webhooks"); err != nil {
return false, errors.Errorf(": %w", err)
}
return changed, nil
}