-
Notifications
You must be signed in to change notification settings - Fork 2
/
webhook.go
141 lines (120 loc) · 3.79 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
package configmap
import (
"context"
"encoding/pem"
"strings"
"github.com/prometheus/client_golang/prometheus"
"github.com/snorwin/k8s-generic-webhook/pkg/webhook"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/metrics"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)
const (
// DefaultTruststoreName default name of the generated truststore
DefaultTruststoreName = "cacerts"
// LabelEnabled k8s label to enable java truststore injection
LabelEnabled = "jti.bakito.ch/inject-truststore"
// LabelTruststoreName k8s label to define a custom truststore name
LabelTruststoreName = "jti.bakito.ch/truststore-name"
annotationTruststorePass = "jti.bakito.ch/truststore-password" // #nosec G101
// AnnotationLastTruststoreName k8s annotation to keep track of the last truststore name
AnnotationLastTruststoreName = "jti.bakito.ch/last-injected-truststore-name"
)
var certsInConfigMap = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "jti_certificates_truststore",
Help: "Number certificates in the truststore",
},
[]string{"namespace", "configmap", "truststore"},
)
func init() {
// Register custom metrics with the global prometheus registry
metrics.Registry.MustRegister(certsInConfigMap)
}
// Webhook implementation
type Webhook struct {
webhook.MutatingWebhook
}
// SetupWebhookWithManager setup this webhook
func (w *Webhook) SetupWebhookWithManager(mgr manager.Manager) error {
return webhook.NewGenericWebhookManagedBy(mgr).
For(&corev1.ConfigMap{}).
WithMutatePath("/mutate").
Complete(w)
}
// Mutate the configmap
func (w *Webhook) Mutate(ctx context.Context, _ admission.Request, object runtime.Object) admission.Response {
cm := object.(*corev1.ConfigMap)
l := log.FromContext(ctx).WithValues("configmap", cm.Name)
tsn := DefaultTruststoreName
if cm.Labels != nil {
if n, ok := cm.Labels[LabelTruststoreName]; ok {
tsn = n
}
}
pass := "changeit"
if cm.Annotations != nil {
if p, ok := cm.Annotations[annotationTruststorePass]; ok {
pass = p
}
if ltn, ok := cm.Annotations[AnnotationLastTruststoreName]; ok && cm.BinaryData != nil {
delete(cm.BinaryData, ltn)
certsInConfigMap.DeleteLabelValues(cm.Namespace, cm.Name, ltn)
}
}
// delete if the label is not present anymore
if !isEnabled(cm) {
l.Info("removing truststore")
if cm.BinaryData != nil {
delete(cm.BinaryData, tsn)
}
if cm.Annotations != nil {
delete(cm.Annotations, AnnotationLastTruststoreName)
}
certsInConfigMap.DeleteLabelValues(cm.Namespace, cm.Name, tsn)
return admission.Allowed("")
}
var allPems []*pem.Block
for name, content := range cm.Data {
pems := readCerts(content)
l.WithValues("fileName", name, "certs", len(pems)).V(3).Info("found certs")
allPems = append(allPems, pems...)
}
b, _ := exportCerts(allPems, pass, cm.ObjectMeta.CreationTimestamp.Time)
if cm.BinaryData == nil {
cm.BinaryData = make(map[string][]byte)
}
cm.BinaryData[tsn] = b
if cm.Annotations == nil {
cm.Annotations = make(map[string]string)
}
cm.Annotations[AnnotationLastTruststoreName] = tsn
l.WithValues("certs", len(allPems), "truststore", tsn).Info("added certs to truststore")
certsInConfigMap.WithLabelValues(cm.Namespace, cm.Name, tsn).Set(float64(len(allPems)))
return admission.Allowed("")
}
func readCerts(certFile string) []*pem.Block {
raw := []byte(certFile)
var pems []*pem.Block
for {
block, rest := pem.Decode(raw)
if block == nil {
break
}
if block.Type == "CERTIFICATE" {
pems = append(pems, block)
}
raw = rest
}
return pems
}
func isEnabled(cm *corev1.ConfigMap) bool {
if cm.Labels == nil {
return false
}
value, ok := cm.Labels[LabelEnabled]
return ok && strings.EqualFold("true", value)
}