in pkg/webhook/webhook.go [117:207]
func (m *podMutator) Handle(ctx context.Context, req admission.Request) (response admission.Response) {
timeStart := time.Now()
defer func() {
ReportRequest(ctx, req.Namespace, time.Since(timeStart))
}()
pod := &corev1.Pod{}
err := m.decoder.Decode(req, pod)
if err != nil {
return admission.Errored(http.StatusBadRequest, err)
}
podName := pod.GetName()
if podName == "" {
podName = pod.GetGenerateName() + " (prefix)"
}
// for daemonset/deployment pods the namespace field is not set in objectMeta
// explicitly set the namespace to request namespace
pod.Namespace = req.Namespace
serviceAccountName := pod.Spec.ServiceAccountName
// When you create a pod, if you do not specify a service account, it is automatically
// assigned the default service account in the same namespace.
// xref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server
if serviceAccountName == "" {
serviceAccountName = "default"
}
logger := mlog.New().WithName("handler").WithValues("pod", podName, "namespace", pod.Namespace, "service-account", serviceAccountName)
// get service account associated with the pod
serviceAccount := &corev1.ServiceAccount{}
if err = m.client.Get(ctx, types.NamespacedName{Name: serviceAccountName, Namespace: pod.Namespace}, serviceAccount); err != nil {
if !apierrors.IsNotFound(err) {
logger.Error("failed to get service account", err)
return admission.Errored(http.StatusBadRequest, err)
}
// bypass cache and get from the API server as it's not found in cache
err = m.reader.Get(ctx, types.NamespacedName{Name: serviceAccountName, Namespace: pod.Namespace}, serviceAccount)
if err != nil {
logger.Error("failed to get service account", err)
return admission.Errored(http.StatusBadRequest, err)
}
}
if shouldInjectProxySidecar(pod) {
// if the pod has hostNetwork set to true, we cannot inject the proxy sidecar
// as it'll end up modifying the network stack of the host and affecting other pods
if pod.Spec.HostNetwork {
err := errors.New("hostNetwork is set to true, cannot inject proxy sidecar")
logger.Error("failed to inject proxy sidecar", err)
return admission.Errored(http.StatusBadRequest, err)
}
proxyPort, err := getProxyPort(pod)
if err != nil {
logger.Error("failed to get proxy port", err)
return admission.Errored(http.StatusBadRequest, err)
}
pod.Spec.InitContainers = m.injectProxyInitContainer(pod.Spec.InitContainers, proxyPort)
if m.useNativeSidecar {
pod.Spec.InitContainers = m.injectProxySidecarContainer(pod.Spec.InitContainers, proxyPort, ptr.To(corev1.ContainerRestartPolicyAlways))
} else {
pod.Spec.Containers = m.injectProxySidecarContainer(pod.Spec.Containers, proxyPort, nil)
}
}
// get service account token expiration
serviceAccountTokenExpiration, err := getServiceAccountTokenExpiration(pod, serviceAccount)
if err != nil {
logger.Error("failed to get service account token expiration", err)
return admission.Errored(http.StatusBadRequest, err)
}
// get the clientID
clientID := getClientID(serviceAccount)
// get the tenantID
tenantID := getTenantID(serviceAccount, m.config)
// get containers to skip
skipContainers := getSkipContainers(pod)
pod.Spec.InitContainers = m.mutateContainers(pod.Spec.InitContainers, clientID, tenantID, skipContainers)
pod.Spec.Containers = m.mutateContainers(pod.Spec.Containers, clientID, tenantID, skipContainers)
// add the projected service account token volume to the pod if not exists
addProjectedServiceAccountTokenVolume(pod, serviceAccountTokenExpiration, m.audience)
marshaledPod, err := json.Marshal(pod)
if err != nil {
logger.Error("failed to marshal pod object", err)
return admission.Errored(http.StatusInternalServerError, err)
}
return admission.PatchResponseFromRaw(req.Object.Raw, marshaledPod)
}