github.com/Racer159/jackal@v0.32.7-0.20240401174413-0bd2339e4f2e/src/internal/agent/hooks/pods.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: 2021-Present The Jackal Authors 3 4 // Package hooks provides HTTP handlers for the mutating webhook. 5 package hooks 6 7 import ( 8 "encoding/json" 9 "fmt" 10 11 "github.com/Racer159/jackal/src/config" 12 "github.com/Racer159/jackal/src/config/lang" 13 "github.com/Racer159/jackal/src/internal/agent/operations" 14 "github.com/Racer159/jackal/src/internal/agent/state" 15 "github.com/Racer159/jackal/src/pkg/message" 16 "github.com/Racer159/jackal/src/pkg/transform" 17 v1 "k8s.io/api/admission/v1" 18 19 corev1 "k8s.io/api/core/v1" 20 ) 21 22 // NewPodMutationHook creates a new instance of pods mutation hook. 23 func NewPodMutationHook() operations.Hook { 24 message.Debug("hooks.NewMutationHook()") 25 return operations.Hook{ 26 Create: mutatePod, 27 Update: mutatePod, 28 } 29 } 30 31 func parsePod(object []byte) (*corev1.Pod, error) { 32 message.Debugf("pods.parsePod(%s)", string(object)) 33 var pod corev1.Pod 34 if err := json.Unmarshal(object, &pod); err != nil { 35 return nil, err 36 } 37 38 return &pod, nil 39 } 40 41 func mutatePod(r *v1.AdmissionRequest) (*operations.Result, error) { 42 message.Debugf("hooks.mutatePod()(*v1.AdmissionRequest) - %#v , %s/%s: %#v", r.Kind, r.Namespace, r.Name, r.Operation) 43 44 var patchOperations []operations.PatchOperation 45 pod, err := parsePod(r.Object.Raw) 46 if err != nil { 47 return &operations.Result{Msg: err.Error()}, nil 48 } 49 50 if pod.Labels != nil && pod.Labels["jackal-agent"] == "patched" { 51 // We've already played with this pod, just keep swimming 🐟 52 return &operations.Result{ 53 Allowed: true, 54 PatchOps: patchOperations, 55 }, nil 56 } 57 58 // Add the jackal secret to the podspec 59 jackalSecret := []corev1.LocalObjectReference{{Name: config.JackalImagePullSecretName}} 60 patchOperations = append(patchOperations, operations.ReplacePatchOperation("/spec/imagePullSecrets", jackalSecret)) 61 62 jackalState, err := state.GetJackalStateFromAgentPod() 63 if err != nil { 64 return nil, fmt.Errorf(lang.AgentErrGetState, err) 65 } 66 containerRegistryURL := jackalState.RegistryInfo.Address 67 68 // update the image host for each init container 69 for idx, container := range pod.Spec.InitContainers { 70 path := fmt.Sprintf("/spec/initContainers/%d/image", idx) 71 replacement, err := transform.ImageTransformHost(containerRegistryURL, container.Image) 72 if err != nil { 73 message.Warnf(lang.AgentErrImageSwap, container.Image) 74 continue // Continue, because we might as well attempt to mutate the other containers for this pod 75 } 76 patchOperations = append(patchOperations, operations.ReplacePatchOperation(path, replacement)) 77 } 78 79 // update the image host for each ephemeral container 80 for idx, container := range pod.Spec.EphemeralContainers { 81 path := fmt.Sprintf("/spec/ephemeralContainers/%d/image", idx) 82 replacement, err := transform.ImageTransformHost(containerRegistryURL, container.Image) 83 if err != nil { 84 message.Warnf(lang.AgentErrImageSwap, container.Image) 85 continue // Continue, because we might as well attempt to mutate the other containers for this pod 86 } 87 patchOperations = append(patchOperations, operations.ReplacePatchOperation(path, replacement)) 88 } 89 90 // update the image host for each normal container 91 for idx, container := range pod.Spec.Containers { 92 path := fmt.Sprintf("/spec/containers/%d/image", idx) 93 replacement, err := transform.ImageTransformHost(containerRegistryURL, container.Image) 94 if err != nil { 95 message.Warnf(lang.AgentErrImageSwap, container.Image) 96 continue // Continue, because we might as well attempt to mutate the other containers for this pod 97 } 98 patchOperations = append(patchOperations, operations.ReplacePatchOperation(path, replacement)) 99 } 100 101 // Add a label noting the jackal mutation 102 patchOperations = append(patchOperations, operations.ReplacePatchOperation("/metadata/labels/jackal-agent", "patched")) 103 104 return &operations.Result{ 105 Allowed: true, 106 PatchOps: patchOperations, 107 }, nil 108 }