feat: implement pod-specific secret injection for DaemonSets with automated lifecycle management

This commit is contained in:
2026-01-21 06:38:25 +00:00
committed by Pengzhan Hao
commit c6978e24dd
15 changed files with 969 additions and 0 deletions
+165
View File
@@ -0,0 +1,165 @@
package webhook
import (
"context"
"encoding/json"
"fmt"
"math/rand"
"net/http"
"time"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
apierrors "k8s.io/apimachinery/pkg/api/errors"
ctrl "sigs.k8s.io/controller-runtime"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
func randomString(n int) string {
var letters = []rune("abcdefghijklmnopqrstuvwxyz0123456789")
s := make([]rune, n)
for i := range s {
s[i] = letters[rand.Intn(len(letters))]
}
return string(s)
}
var log = ctrl.Log.WithName("pod-mutator")
// PodMutator mutates Pods
type PodMutator struct {
Client client.Client
decoder *admission.Decoder
TargetNS string
TargetDSList []string
}
// PodMutator implements admission.DecoderInjector.
// A decoder will be automatically injected.
// InjectDecoder injects the decoder.
func (a *PodMutator) InjectDecoder(d *admission.Decoder) error {
a.decoder = d
return nil
}
// Handle handles admission requests.
func (a *PodMutator) Handle(ctx context.Context, req admission.Request) admission.Response {
pod := &corev1.Pod{}
err := a.decoder.Decode(req, pod)
if err != nil {
return admission.Errored(http.StatusBadRequest, err)
}
// Check namespace
if pod.Namespace == "" {
pod.Namespace = req.Namespace
}
if pod.Namespace != a.TargetNS {
return admission.Allowed("not in target namespace")
}
// Check if it belongs to target DaemonSet
isTarget := false
dsName := ""
for _, targetDS := range a.TargetDSList {
for _, owner := range pod.OwnerReferences {
if owner.Kind == "DaemonSet" && owner.Name == targetDS {
isTarget = true
dsName = targetDS
break
}
}
if isTarget {
break
}
}
if !isTarget {
return admission.Allowed("not a target daemonset pod")
}
// Extract NodeName from affinity if not set (common for DaemonSets)
nodeName := pod.Spec.NodeName
if nodeName == "" && pod.Spec.Affinity != nil &&
pod.Spec.Affinity.NodeAffinity != nil &&
pod.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
for _, term := range pod.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms {
for _, expr := range term.MatchFields {
if expr.Key == "metadata.name" && expr.Operator == corev1.NodeSelectorOpIn && len(expr.Values) > 0 {
nodeName = expr.Values[0]
break
}
}
}
}
// Requirement: Secret name is the same as pod name.
// Since pod.Name is empty during CREATE, we must assign it.
if pod.Name == "" {
pod.Name = pod.GenerateName + randomString(5)
}
secretName := pod.Name
// Add finalizer to ensure our controller can cleanup the secret before pod is gone
pod.Finalizers = append(pod.Finalizers, "inject-ds-webhook.example.com/cleanup")
log.Info("Mutating pod", "node", nodeName, "podName", pod.Name, "secretName", secretName, "ds", dsName)
// Create Secret
secret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: pod.Namespace,
Labels: map[string]string{
"injected-by": "inject-ds-webhook",
},
},
StringData: map[string]string{
"node-name": nodeName,
"pod-name": pod.Name,
"secret-data": fmt.Sprintf("unique-secret-for-%s-on-%s", pod.Name, nodeName),
},
}
err = a.Client.Create(ctx, secret)
if err != nil && !apierrors.IsAlreadyExists(err) {
return admission.Errored(http.StatusInternalServerError, err)
}
// Mutate Pod to add volume
volumeName := "node-secret-volume"
// Add volume
pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{
Name: volumeName,
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: secretName,
},
},
})
// Add volume mount to all containers
for i := range pod.Spec.Containers {
pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts, corev1.VolumeMount{
Name: volumeName,
MountPath: "/etc/node-secret",
ReadOnly: true,
})
}
marshaledPod, err := json.Marshal(pod)
if err != nil {
return admission.Errored(http.StatusInternalServerError, err)
}
return admission.PatchResponseFromRaw(req.Object.Raw, marshaledPod)
}