...

Source file src/k8s.io/kubernetes/plugin/pkg/admission/network/denyserviceexternalips/admission.go

Documentation: k8s.io/kubernetes/plugin/pkg/admission/network/denyserviceexternalips

     1  /*
     2  Copyright 2020 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 denyserviceexternalips
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"io"
    23  
    24  	"k8s.io/apimachinery/pkg/api/errors"
    25  	"k8s.io/apiserver/pkg/admission"
    26  	"k8s.io/klog/v2"
    27  	"k8s.io/kubernetes/pkg/apis/core"
    28  )
    29  
    30  const (
    31  	// PluginName is the name of this admission controller plugin
    32  	PluginName = "DenyServiceExternalIPs"
    33  )
    34  
    35  // Register registers a plugin
    36  func Register(plugins *admission.Plugins) {
    37  	plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
    38  		plugin := newPlugin()
    39  		return plugin, nil
    40  	})
    41  }
    42  
    43  // externalIPsDenierPlugin holds state for and implements the admission plugin.
    44  type externalIPsDenierPlugin struct {
    45  	*admission.Handler
    46  }
    47  
    48  var _ admission.Interface = &externalIPsDenierPlugin{}
    49  var _ admission.ValidationInterface = &externalIPsDenierPlugin{}
    50  
    51  // newPlugin creates a new admission plugin.
    52  func newPlugin() *externalIPsDenierPlugin {
    53  	return &externalIPsDenierPlugin{
    54  		Handler: admission.NewHandler(admission.Create, admission.Update),
    55  	}
    56  }
    57  
    58  // Admit ensures that modifications of the Service.Spec.ExternalIPs field are
    59  // denied
    60  func (plug *externalIPsDenierPlugin) Validate(ctx context.Context, attr admission.Attributes, o admission.ObjectInterfaces) error {
    61  	if attr.GetResource().GroupResource() != core.Resource("services") {
    62  		return nil
    63  	}
    64  
    65  	if len(attr.GetSubresource()) != 0 {
    66  		return nil
    67  	}
    68  
    69  	// if we can't convert then we don't handle this object so just return
    70  	newSvc, ok := attr.GetObject().(*core.Service)
    71  	if !ok {
    72  		klog.V(3).Infof("Expected Service resource, got: %v", attr.GetKind())
    73  		return errors.NewInternalError(fmt.Errorf("Expected Service resource, got: %v", attr.GetKind()))
    74  	}
    75  
    76  	var oldSvc *core.Service
    77  	if old := attr.GetOldObject(); old != nil {
    78  		tmp, ok := old.(*core.Service)
    79  		if !ok {
    80  			klog.V(3).Infof("Expected Service resource, got: %v", attr.GetKind())
    81  			return errors.NewInternalError(fmt.Errorf("Expected Service resource, got: %v", attr.GetKind()))
    82  		}
    83  		oldSvc = tmp
    84  	}
    85  
    86  	if isSubset(newSvc, oldSvc) {
    87  		return nil
    88  	}
    89  
    90  	klog.V(4).Infof("Denying new use of ExternalIPs on Service %s/%s", newSvc.Namespace, newSvc.Name)
    91  	return admission.NewForbidden(attr, fmt.Errorf("Use of external IPs is denied by admission control"))
    92  }
    93  
    94  func isSubset(newSvc, oldSvc *core.Service) bool {
    95  	// If new has none, it's a subset.
    96  	if len(newSvc.Spec.ExternalIPs) == 0 {
    97  		return true
    98  	}
    99  	// If we have some but it's not an update, it's not a subset.
   100  	if oldSvc == nil {
   101  		return false
   102  	}
   103  	oldIPs := map[string]bool{}
   104  	for _, ip := range oldSvc.Spec.ExternalIPs {
   105  		oldIPs[ip] = true
   106  	}
   107  	// Every IP in newSvc must be in oldSvc
   108  	for _, ip := range newSvc.Spec.ExternalIPs {
   109  		if oldIPs[ip] == false {
   110  			return false
   111  		}
   112  	}
   113  	return true
   114  }
   115  

View as plain text