k8s.io/kubernetes@v1.29.3/test/images/agnhost/webhook/pods.go (about)

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package webhook
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  
    23  	"k8s.io/api/admission/v1"
    24  	corev1 "k8s.io/api/core/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/klog/v2"
    27  )
    28  
    29  const (
    30  	podsInitContainerPatch string = `[
    31  		 {"op":"add","path":"/spec/initContainers","value":[{"image":"webhook-added-image","name":"webhook-added-init-container","resources":{}}]}
    32  	]`
    33  	podsSidecarPatch string = `[
    34  		{"op":"add", "path":"/spec/containers/-","value":{"image":"%v","name":"webhook-added-sidecar","resources":{}}}
    35  	]`
    36  )
    37  
    38  // only allow pods to pull images from specific registry.
    39  func admitPods(ar v1.AdmissionReview) *v1.AdmissionResponse {
    40  	klog.V(2).Info("admitting pods")
    41  	podResource := metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
    42  	if ar.Request.Resource != podResource {
    43  		err := fmt.Errorf("expect resource to be %s", podResource)
    44  		klog.Error(err)
    45  		return toV1AdmissionResponse(err)
    46  	}
    47  
    48  	raw := ar.Request.Object.Raw
    49  	pod := corev1.Pod{}
    50  	deserializer := codecs.UniversalDeserializer()
    51  	if _, _, err := deserializer.Decode(raw, nil, &pod); err != nil {
    52  		klog.Error(err)
    53  		return toV1AdmissionResponse(err)
    54  	}
    55  	reviewResponse := v1.AdmissionResponse{}
    56  	reviewResponse.Allowed = true
    57  
    58  	var msg string
    59  	if v, ok := pod.Labels["webhook-e2e-test"]; ok {
    60  		if v == "webhook-disallow" {
    61  			reviewResponse.Allowed = false
    62  			msg = msg + "the pod contains unwanted label; "
    63  		}
    64  		if v == "wait-forever" {
    65  			reviewResponse.Allowed = false
    66  			msg = msg + "the pod response should not be sent; "
    67  			<-make(chan int) // Sleep forever - no one sends to this channel
    68  		}
    69  	}
    70  	for _, container := range pod.Spec.Containers {
    71  		if strings.Contains(container.Name, "webhook-disallow") {
    72  			reviewResponse.Allowed = false
    73  			msg = msg + "the pod contains unwanted container name; "
    74  		}
    75  	}
    76  	if !reviewResponse.Allowed {
    77  		reviewResponse.Result = &metav1.Status{Message: strings.TrimSpace(msg)}
    78  	}
    79  	return &reviewResponse
    80  }
    81  
    82  func mutatePods(ar v1.AdmissionReview) *v1.AdmissionResponse {
    83  	shouldPatchPod := func(pod *corev1.Pod) bool {
    84  		if pod.Name != "webhook-to-be-mutated" {
    85  			return false
    86  		}
    87  		return !hasContainer(pod.Spec.InitContainers, "webhook-added-init-container")
    88  	}
    89  	return applyPodPatch(ar, shouldPatchPod, podsInitContainerPatch)
    90  }
    91  
    92  func mutatePodsSidecar(ar v1.AdmissionReview) *v1.AdmissionResponse {
    93  	if sidecarImage == "" {
    94  		return &v1.AdmissionResponse{
    95  			Allowed: false,
    96  			Result: &metav1.Status{
    97  				Status:  "Failure",
    98  				Message: "No image specified by the sidecar-image parameter",
    99  				Code:    500,
   100  			},
   101  		}
   102  	}
   103  	shouldPatchPod := func(pod *corev1.Pod) bool {
   104  		return !hasContainer(pod.Spec.Containers, "webhook-added-sidecar")
   105  	}
   106  	return applyPodPatch(ar, shouldPatchPod, fmt.Sprintf(podsSidecarPatch, sidecarImage))
   107  }
   108  
   109  func hasContainer(containers []corev1.Container, containerName string) bool {
   110  	for _, container := range containers {
   111  		if container.Name == containerName {
   112  			return true
   113  		}
   114  	}
   115  	return false
   116  }
   117  
   118  func applyPodPatch(ar v1.AdmissionReview, shouldPatchPod func(*corev1.Pod) bool, patch string) *v1.AdmissionResponse {
   119  	klog.V(2).Info("mutating pods")
   120  	podResource := metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
   121  	if ar.Request.Resource != podResource {
   122  		klog.Errorf("expect resource to be %s", podResource)
   123  		return nil
   124  	}
   125  
   126  	raw := ar.Request.Object.Raw
   127  	pod := corev1.Pod{}
   128  	deserializer := codecs.UniversalDeserializer()
   129  	if _, _, err := deserializer.Decode(raw, nil, &pod); err != nil {
   130  		klog.Error(err)
   131  		return toV1AdmissionResponse(err)
   132  	}
   133  	reviewResponse := v1.AdmissionResponse{}
   134  	reviewResponse.Allowed = true
   135  	if shouldPatchPod(&pod) {
   136  		reviewResponse.Patch = []byte(patch)
   137  		pt := v1.PatchTypeJSONPatch
   138  		reviewResponse.PatchType = &pt
   139  	}
   140  	return &reviewResponse
   141  }
   142  
   143  // denySpecificAttachment denies `kubectl attach to-be-attached-pod -i -c=container1"
   144  // or equivalent client requests.
   145  func denySpecificAttachment(ar v1.AdmissionReview) *v1.AdmissionResponse {
   146  	klog.V(2).Info("handling attaching pods")
   147  	if ar.Request.Name != "to-be-attached-pod" {
   148  		return &v1.AdmissionResponse{Allowed: true}
   149  	}
   150  	podResource := metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
   151  	if e, a := podResource, ar.Request.Resource; e != a {
   152  		err := fmt.Errorf("expect resource to be %s, got %s", e, a)
   153  		klog.Error(err)
   154  		return toV1AdmissionResponse(err)
   155  	}
   156  	if e, a := "attach", ar.Request.SubResource; e != a {
   157  		err := fmt.Errorf("expect subresource to be %s, got %s", e, a)
   158  		klog.Error(err)
   159  		return toV1AdmissionResponse(err)
   160  	}
   161  
   162  	raw := ar.Request.Object.Raw
   163  	podAttachOptions := corev1.PodAttachOptions{}
   164  	deserializer := codecs.UniversalDeserializer()
   165  	if _, _, err := deserializer.Decode(raw, nil, &podAttachOptions); err != nil {
   166  		klog.Error(err)
   167  		return toV1AdmissionResponse(err)
   168  	}
   169  	klog.V(2).Info(fmt.Sprintf("podAttachOptions=%#v\n", podAttachOptions))
   170  	if !podAttachOptions.Stdin || podAttachOptions.Container != "container1" {
   171  		return &v1.AdmissionResponse{Allowed: true}
   172  	}
   173  	return &v1.AdmissionResponse{
   174  		Allowed: false,
   175  		Result: &metav1.Status{
   176  			Message: "attaching to pod 'to-be-attached-pod' is not allowed",
   177  		},
   178  	}
   179  }