123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615 |
- /*
- Copyright 2017 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package noderestriction
- import (
- "context"
- "fmt"
- "io"
- "strings"
- v1 "k8s.io/api/core/v1"
- apiequality "k8s.io/apimachinery/pkg/api/equality"
- "k8s.io/apimachinery/pkg/api/errors"
- "k8s.io/apimachinery/pkg/api/meta"
- "k8s.io/apimachinery/pkg/labels"
- "k8s.io/apimachinery/pkg/util/diff"
- "k8s.io/apimachinery/pkg/util/sets"
- "k8s.io/apiserver/pkg/admission"
- apiserveradmission "k8s.io/apiserver/pkg/admission/initializer"
- "k8s.io/client-go/informers"
- corev1lister "k8s.io/client-go/listers/core/v1"
- "k8s.io/component-base/featuregate"
- "k8s.io/klog"
- podutil "k8s.io/kubernetes/pkg/api/pod"
- authenticationapi "k8s.io/kubernetes/pkg/apis/authentication"
- coordapi "k8s.io/kubernetes/pkg/apis/coordination"
- api "k8s.io/kubernetes/pkg/apis/core"
- "k8s.io/kubernetes/pkg/apis/policy"
- storage "k8s.io/kubernetes/pkg/apis/storage"
- "k8s.io/kubernetes/pkg/auth/nodeidentifier"
- "k8s.io/kubernetes/pkg/features"
- kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
- )
- // PluginName is a string with the name of the plugin
- const PluginName = "NodeRestriction"
- // Register registers a plugin
- func Register(plugins *admission.Plugins) {
- plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) {
- return NewPlugin(nodeidentifier.NewDefaultNodeIdentifier()), nil
- })
- }
- // NewPlugin creates a new NodeRestriction admission plugin.
- // This plugin identifies requests from nodes
- func NewPlugin(nodeIdentifier nodeidentifier.NodeIdentifier) *Plugin {
- return &Plugin{
- Handler: admission.NewHandler(admission.Create, admission.Update, admission.Delete),
- nodeIdentifier: nodeIdentifier,
- }
- }
- // Plugin holds state for and implements the admission plugin.
- type Plugin struct {
- *admission.Handler
- nodeIdentifier nodeidentifier.NodeIdentifier
- podsGetter corev1lister.PodLister
- nodesGetter corev1lister.NodeLister
- tokenRequestEnabled bool
- csiNodeInfoEnabled bool
- expandPersistentVolumesEnabled bool
- }
- var (
- _ admission.Interface = &Plugin{}
- _ apiserveradmission.WantsExternalKubeInformerFactory = &Plugin{}
- _ apiserveradmission.WantsFeatures = &Plugin{}
- )
- // InspectFeatureGates allows setting bools without taking a dep on a global variable
- func (p *Plugin) InspectFeatureGates(featureGates featuregate.FeatureGate) {
- p.tokenRequestEnabled = featureGates.Enabled(features.TokenRequest)
- p.csiNodeInfoEnabled = featureGates.Enabled(features.CSINodeInfo)
- p.expandPersistentVolumesEnabled = featureGates.Enabled(features.ExpandPersistentVolumes)
- }
- // SetExternalKubeInformerFactory registers an informer factory into Plugin
- func (p *Plugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) {
- p.podsGetter = f.Core().V1().Pods().Lister()
- p.nodesGetter = f.Core().V1().Nodes().Lister()
- }
- // ValidateInitialization validates the Plugin was initialized properly
- func (p *Plugin) ValidateInitialization() error {
- if p.nodeIdentifier == nil {
- return fmt.Errorf("%s requires a node identifier", PluginName)
- }
- if p.podsGetter == nil {
- return fmt.Errorf("%s requires a pod getter", PluginName)
- }
- if p.nodesGetter == nil {
- return fmt.Errorf("%s requires a node getter", PluginName)
- }
- return nil
- }
- var (
- podResource = api.Resource("pods")
- nodeResource = api.Resource("nodes")
- pvcResource = api.Resource("persistentvolumeclaims")
- svcacctResource = api.Resource("serviceaccounts")
- leaseResource = coordapi.Resource("leases")
- csiNodeResource = storage.Resource("csinodes")
- )
- // Admit checks the admission policy and triggers corresponding actions
- func (p *Plugin) Admit(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
- nodeName, isNode := p.nodeIdentifier.NodeIdentity(a.GetUserInfo())
- // Our job is just to restrict nodes
- if !isNode {
- return nil
- }
- if len(nodeName) == 0 {
- // disallow requests we cannot match to a particular node
- return admission.NewForbidden(a, fmt.Errorf("could not determine node from user %q", a.GetUserInfo().GetName()))
- }
- // TODO: if node doesn't exist and this isn't a create node request, then reject.
- switch a.GetResource().GroupResource() {
- case podResource:
- switch a.GetSubresource() {
- case "":
- return p.admitPod(nodeName, a)
- case "status":
- return p.admitPodStatus(nodeName, a)
- case "eviction":
- return p.admitPodEviction(nodeName, a)
- default:
- return admission.NewForbidden(a, fmt.Errorf("unexpected pod subresource %q, only 'status' and 'eviction' are allowed", a.GetSubresource()))
- }
- case nodeResource:
- return p.admitNode(nodeName, a)
- case pvcResource:
- switch a.GetSubresource() {
- case "status":
- return p.admitPVCStatus(nodeName, a)
- default:
- return admission.NewForbidden(a, fmt.Errorf("may only update PVC status"))
- }
- case svcacctResource:
- if p.tokenRequestEnabled {
- return p.admitServiceAccount(nodeName, a)
- }
- return nil
- case leaseResource:
- return p.admitLease(nodeName, a)
- case csiNodeResource:
- if p.csiNodeInfoEnabled {
- return p.admitCSINode(nodeName, a)
- }
- return admission.NewForbidden(a, fmt.Errorf("disabled by feature gates %s", features.CSINodeInfo))
- default:
- return nil
- }
- }
- // admitPod allows creating or deleting a pod if it is assigned to the
- // current node and fulfills related criteria.
- func (p *Plugin) admitPod(nodeName string, a admission.Attributes) error {
- switch a.GetOperation() {
- case admission.Create:
- return p.admitPodCreate(nodeName, a)
- case admission.Delete:
- // get the existing pod
- existingPod, err := p.podsGetter.Pods(a.GetNamespace()).Get(a.GetName())
- if errors.IsNotFound(err) {
- return err
- }
- if err != nil {
- return admission.NewForbidden(a, err)
- }
- // only allow a node to delete a pod bound to itself
- if existingPod.Spec.NodeName != nodeName {
- return admission.NewForbidden(a, fmt.Errorf("node %q can only delete pods with spec.nodeName set to itself", nodeName))
- }
- return nil
- default:
- return admission.NewForbidden(a, fmt.Errorf("unexpected operation %q, node %q can only create and delete mirror pods", a.GetOperation(), nodeName))
- }
- }
- func (p *Plugin) admitPodCreate(nodeName string, a admission.Attributes) error {
- // require a pod object
- pod, ok := a.GetObject().(*api.Pod)
- if !ok {
- return admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetObject()))
- }
- // only allow nodes to create mirror pods
- if _, isMirrorPod := pod.Annotations[api.MirrorPodAnnotationKey]; !isMirrorPod {
- return admission.NewForbidden(a, fmt.Errorf("pod does not have %q annotation, node %q can only create mirror pods", api.MirrorPodAnnotationKey, nodeName))
- }
- // only allow nodes to create a pod bound to itself
- if pod.Spec.NodeName != nodeName {
- return admission.NewForbidden(a, fmt.Errorf("node %q can only create pods with spec.nodeName set to itself", nodeName))
- }
- if len(pod.OwnerReferences) > 1 {
- return admission.NewForbidden(a, fmt.Errorf("node %q can only create pods with a single owner reference set to itself", nodeName))
- }
- if len(pod.OwnerReferences) == 1 {
- owner := pod.OwnerReferences[0]
- if owner.APIVersion != v1.SchemeGroupVersion.String() ||
- owner.Kind != "Node" ||
- owner.Name != nodeName {
- return admission.NewForbidden(a, fmt.Errorf("node %q can only create pods with an owner reference set to itself", nodeName))
- }
- if owner.Controller == nil || !*owner.Controller {
- return admission.NewForbidden(a, fmt.Errorf("node %q can only create pods with a controller owner reference set to itself", nodeName))
- }
- if owner.BlockOwnerDeletion != nil && *owner.BlockOwnerDeletion {
- return admission.NewForbidden(a, fmt.Errorf("node %q must not set blockOwnerDeletion on an owner reference", nodeName))
- }
- // Verify the node UID.
- node, err := p.nodesGetter.Get(nodeName)
- if errors.IsNotFound(err) {
- return err
- }
- if err != nil {
- return admission.NewForbidden(a, fmt.Errorf("error looking up node %s to verify uid: %v", nodeName, err))
- }
- if owner.UID != node.UID {
- return admission.NewForbidden(a, fmt.Errorf("node %s UID mismatch: expected %s got %s", nodeName, owner.UID, node.UID))
- }
- }
- // don't allow a node to create a pod that references any other API objects
- if pod.Spec.ServiceAccountName != "" {
- return admission.NewForbidden(a, fmt.Errorf("node %q can not create pods that reference a service account", nodeName))
- }
- hasSecrets := false
- podutil.VisitPodSecretNames(pod, func(name string) (shouldContinue bool) { hasSecrets = true; return false })
- if hasSecrets {
- return admission.NewForbidden(a, fmt.Errorf("node %q can not create pods that reference secrets", nodeName))
- }
- hasConfigMaps := false
- podutil.VisitPodConfigmapNames(pod, func(name string) (shouldContinue bool) { hasConfigMaps = true; return false })
- if hasConfigMaps {
- return admission.NewForbidden(a, fmt.Errorf("node %q can not create pods that reference configmaps", nodeName))
- }
- for _, v := range pod.Spec.Volumes {
- if v.PersistentVolumeClaim != nil {
- return admission.NewForbidden(a, fmt.Errorf("node %q can not create pods that reference persistentvolumeclaims", nodeName))
- }
- }
- return nil
- }
- // admitPodStatus allows to update the status of a pod if it is
- // assigned to the current node.
- func (p *Plugin) admitPodStatus(nodeName string, a admission.Attributes) error {
- switch a.GetOperation() {
- case admission.Update:
- // require an existing pod
- oldPod, ok := a.GetOldObject().(*api.Pod)
- if !ok {
- return admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetOldObject()))
- }
- // only allow a node to update status of a pod bound to itself
- if oldPod.Spec.NodeName != nodeName {
- return admission.NewForbidden(a, fmt.Errorf("node %q can only update pod status for pods with spec.nodeName set to itself", nodeName))
- }
- newPod, ok := a.GetObject().(*api.Pod)
- if !ok {
- return admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetObject()))
- }
- if !labels.Equals(oldPod.Labels, newPod.Labels) {
- return admission.NewForbidden(a, fmt.Errorf("node %q cannot update labels through pod status", nodeName))
- }
- return nil
- default:
- return admission.NewForbidden(a, fmt.Errorf("unexpected operation %q", a.GetOperation()))
- }
- }
- // admitPodEviction allows to evict a pod if it is assigned to the current node.
- func (p *Plugin) admitPodEviction(nodeName string, a admission.Attributes) error {
- switch a.GetOperation() {
- case admission.Create:
- // require eviction to an existing pod object
- eviction, ok := a.GetObject().(*policy.Eviction)
- if !ok {
- return admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetObject()))
- }
- // use pod name from the admission attributes, if set, rather than from the submitted Eviction object
- podName := a.GetName()
- if len(podName) == 0 {
- if len(eviction.Name) == 0 {
- return admission.NewForbidden(a, fmt.Errorf("could not determine pod from request data"))
- }
- podName = eviction.Name
- }
- // get the existing pod
- existingPod, err := p.podsGetter.Pods(a.GetNamespace()).Get(podName)
- if errors.IsNotFound(err) {
- return err
- }
- if err != nil {
- return admission.NewForbidden(a, err)
- }
- // only allow a node to evict a pod bound to itself
- if existingPod.Spec.NodeName != nodeName {
- return admission.NewForbidden(a, fmt.Errorf("node %s can only evict pods with spec.nodeName set to itself", nodeName))
- }
- return nil
- default:
- return admission.NewForbidden(a, fmt.Errorf("unexpected operation %s", a.GetOperation()))
- }
- }
- func (p *Plugin) admitPVCStatus(nodeName string, a admission.Attributes) error {
- switch a.GetOperation() {
- case admission.Update:
- if !p.expandPersistentVolumesEnabled {
- return admission.NewForbidden(a, fmt.Errorf("node %q is not allowed to update persistentvolumeclaim metadata", nodeName))
- }
- oldPVC, ok := a.GetOldObject().(*api.PersistentVolumeClaim)
- if !ok {
- return admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetOldObject()))
- }
- newPVC, ok := a.GetObject().(*api.PersistentVolumeClaim)
- if !ok {
- return admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetObject()))
- }
- // make copies for comparison
- oldPVC = oldPVC.DeepCopy()
- newPVC = newPVC.DeepCopy()
- // zero out resourceVersion to avoid comparing differences,
- // since the new object could leave it empty to indicate an unconditional update
- oldPVC.ObjectMeta.ResourceVersion = ""
- newPVC.ObjectMeta.ResourceVersion = ""
- oldPVC.Status.Capacity = nil
- newPVC.Status.Capacity = nil
- oldPVC.Status.Conditions = nil
- newPVC.Status.Conditions = nil
- // TODO(apelisse): We don't have a good mechanism to
- // verify that only the things that should have changed
- // have changed. Ignore it for now.
- oldPVC.ObjectMeta.ManagedFields = nil
- newPVC.ObjectMeta.ManagedFields = nil
- // ensure no metadata changed. nodes should not be able to relabel, add finalizers/owners, etc
- if !apiequality.Semantic.DeepEqual(oldPVC, newPVC) {
- return admission.NewForbidden(a, fmt.Errorf("node %q is not allowed to update fields other than status.capacity and status.conditions: %v", nodeName, diff.ObjectReflectDiff(oldPVC, newPVC)))
- }
- return nil
- default:
- return admission.NewForbidden(a, fmt.Errorf("unexpected operation %q", a.GetOperation()))
- }
- }
- func (p *Plugin) admitNode(nodeName string, a admission.Attributes) error {
- requestedName := a.GetName()
- if a.GetOperation() == admission.Create {
- node, ok := a.GetObject().(*api.Node)
- if !ok {
- return admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetObject()))
- }
- // Don't allow a node to create its Node API object with the config source set.
- // We scope node access to things listed in the Node.Spec, so allowing this would allow a view escalation.
- if node.Spec.ConfigSource != nil {
- return admission.NewForbidden(a, fmt.Errorf("node %q is not allowed to create pods with a non-nil configSource", nodeName))
- }
- // Don't allow a node to register with labels outside the allowed set.
- // This would allow a node to add or modify its labels in a way that would let it steer privileged workloads to itself.
- modifiedLabels := getModifiedLabels(node.Labels, nil)
- if forbiddenLabels := p.getForbiddenCreateLabels(modifiedLabels); len(forbiddenLabels) > 0 {
- return admission.NewForbidden(a, fmt.Errorf("node %q is not allowed to set the following labels: %s", nodeName, strings.Join(forbiddenLabels.List(), ", ")))
- }
- // check and warn if nodes set labels on create that would have been forbidden on update
- // TODO(liggitt): in 1.19, expand getForbiddenCreateLabels to match getForbiddenUpdateLabels and drop this
- if forbiddenUpdateLabels := p.getForbiddenUpdateLabels(modifiedLabels); len(forbiddenUpdateLabels) > 0 {
- klog.Warningf("node %q added disallowed labels on node creation: %s", nodeName, strings.Join(forbiddenUpdateLabels.List(), ", "))
- }
- }
- if requestedName != nodeName {
- return admission.NewForbidden(a, fmt.Errorf("node %q is not allowed to modify node %q", nodeName, requestedName))
- }
- if a.GetOperation() == admission.Update {
- node, ok := a.GetObject().(*api.Node)
- if !ok {
- return admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetObject()))
- }
- oldNode, ok := a.GetOldObject().(*api.Node)
- if !ok {
- return admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetObject()))
- }
- // Don't allow a node to update the config source on its Node API object.
- // We scope node access to things listed in the Node.Spec, so allowing this would allow a view escalation.
- // We only do the check if the new node's configSource is non-nil; old kubelets might drop the field during a status update.
- if node.Spec.ConfigSource != nil && !apiequality.Semantic.DeepEqual(node.Spec.ConfigSource, oldNode.Spec.ConfigSource) {
- return admission.NewForbidden(a, fmt.Errorf("node %q is not allowed to update configSource to a new non-nil configSource", nodeName))
- }
- // Don't allow a node to update its own taints. This would allow a node to remove or modify its
- // taints in a way that would let it steer disallowed workloads to itself.
- if !apiequality.Semantic.DeepEqual(node.Spec.Taints, oldNode.Spec.Taints) {
- return admission.NewForbidden(a, fmt.Errorf("node %q is not allowed to modify taints", nodeName))
- }
- // Don't allow a node to update labels outside the allowed set.
- // This would allow a node to add or modify its labels in a way that would let it steer privileged workloads to itself.
- modifiedLabels := getModifiedLabels(node.Labels, oldNode.Labels)
- if forbiddenUpdateLabels := p.getForbiddenUpdateLabels(modifiedLabels); len(forbiddenUpdateLabels) > 0 {
- return admission.NewForbidden(a, fmt.Errorf("is not allowed to modify labels: %s", strings.Join(forbiddenUpdateLabels.List(), ", ")))
- }
- }
- return nil
- }
- // getModifiedLabels returns the set of label keys that are different between the two maps
- func getModifiedLabels(a, b map[string]string) sets.String {
- modified := sets.NewString()
- for k, v1 := range a {
- if v2, ok := b[k]; !ok || v1 != v2 {
- modified.Insert(k)
- }
- }
- for k, v1 := range b {
- if v2, ok := a[k]; !ok || v1 != v2 {
- modified.Insert(k)
- }
- }
- return modified
- }
- func isKubernetesLabel(key string) bool {
- namespace := getLabelNamespace(key)
- if namespace == "kubernetes.io" || strings.HasSuffix(namespace, ".kubernetes.io") {
- return true
- }
- if namespace == "k8s.io" || strings.HasSuffix(namespace, ".k8s.io") {
- return true
- }
- return false
- }
- func getLabelNamespace(key string) string {
- if parts := strings.SplitN(key, "/", 2); len(parts) == 2 {
- return parts[0]
- }
- return ""
- }
- // getForbiddenCreateLabels returns the set of labels that may not be set by the node.
- // TODO(liggitt): in 1.19, expand to match getForbiddenUpdateLabels()
- func (p *Plugin) getForbiddenCreateLabels(modifiedLabels sets.String) sets.String {
- if len(modifiedLabels) == 0 {
- return nil
- }
- forbiddenLabels := sets.NewString()
- for label := range modifiedLabels {
- namespace := getLabelNamespace(label)
- // forbid kubelets from setting node-restriction labels
- if namespace == v1.LabelNamespaceNodeRestriction || strings.HasSuffix(namespace, "."+v1.LabelNamespaceNodeRestriction) {
- forbiddenLabels.Insert(label)
- }
- }
- return forbiddenLabels
- }
- // getForbiddenLabels returns the set of labels that may not be set by the node on update.
- func (p *Plugin) getForbiddenUpdateLabels(modifiedLabels sets.String) sets.String {
- if len(modifiedLabels) == 0 {
- return nil
- }
- forbiddenLabels := sets.NewString()
- for label := range modifiedLabels {
- namespace := getLabelNamespace(label)
- // forbid kubelets from setting node-restriction labels
- if namespace == v1.LabelNamespaceNodeRestriction || strings.HasSuffix(namespace, "."+v1.LabelNamespaceNodeRestriction) {
- forbiddenLabels.Insert(label)
- }
- // forbid kubelets from setting unknown kubernetes.io and k8s.io labels on update
- if isKubernetesLabel(label) && !kubeletapis.IsKubeletLabel(label) {
- // TODO: defer to label policy once available
- forbiddenLabels.Insert(label)
- }
- }
- return forbiddenLabels
- }
- func (p *Plugin) admitServiceAccount(nodeName string, a admission.Attributes) error {
- if a.GetOperation() != admission.Create {
- return nil
- }
- if a.GetSubresource() != "token" {
- return nil
- }
- tr, ok := a.GetObject().(*authenticationapi.TokenRequest)
- if !ok {
- return admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetObject()))
- }
- // TokenRequests from a node must have a pod binding. That pod must be
- // scheduled on the node.
- ref := tr.Spec.BoundObjectRef
- if ref == nil ||
- ref.APIVersion != "v1" ||
- ref.Kind != "Pod" ||
- ref.Name == "" {
- return admission.NewForbidden(a, fmt.Errorf("node requested token not bound to a pod"))
- }
- if ref.UID == "" {
- return admission.NewForbidden(a, fmt.Errorf("node requested token with a pod binding without a uid"))
- }
- pod, err := p.podsGetter.Pods(a.GetNamespace()).Get(ref.Name)
- if errors.IsNotFound(err) {
- return err
- }
- if err != nil {
- return admission.NewForbidden(a, err)
- }
- if ref.UID != pod.UID {
- return admission.NewForbidden(a, fmt.Errorf("the UID in the bound object reference (%s) does not match the UID in record (%s). The object might have been deleted and then recreated", ref.UID, pod.UID))
- }
- if pod.Spec.NodeName != nodeName {
- return admission.NewForbidden(a, fmt.Errorf("node requested token bound to a pod scheduled on a different node"))
- }
- return nil
- }
- func (p *Plugin) admitLease(nodeName string, a admission.Attributes) error {
- // the request must be against the system namespace reserved for node leases
- if a.GetNamespace() != api.NamespaceNodeLease {
- return admission.NewForbidden(a, fmt.Errorf("can only access leases in the %q system namespace", api.NamespaceNodeLease))
- }
- // the request must come from a node with the same name as the lease
- if a.GetOperation() == admission.Create {
- // a.GetName() won't return the name on create, so we drill down to the proposed object
- lease, ok := a.GetObject().(*coordapi.Lease)
- if !ok {
- return admission.NewForbidden(a, fmt.Errorf("unexpected type %T", a.GetObject()))
- }
- if lease.Name != nodeName {
- return admission.NewForbidden(a, fmt.Errorf("can only access node lease with the same name as the requesting node"))
- }
- } else {
- if a.GetName() != nodeName {
- return admission.NewForbidden(a, fmt.Errorf("can only access node lease with the same name as the requesting node"))
- }
- }
- return nil
- }
- func (p *Plugin) admitCSINode(nodeName string, a admission.Attributes) error {
- // the request must come from a node with the same name as the CSINode object
- if a.GetOperation() == admission.Create {
- // a.GetName() won't return the name on create, so we drill down to the proposed object
- accessor, err := meta.Accessor(a.GetObject())
- if err != nil {
- return admission.NewForbidden(a, fmt.Errorf("unable to access the object name"))
- }
- if accessor.GetName() != nodeName {
- return admission.NewForbidden(a, fmt.Errorf("can only access CSINode with the same name as the requesting node"))
- }
- } else {
- if a.GetName() != nodeName {
- return admission.NewForbidden(a, fmt.Errorf("can only access CSINode with the same name as the requesting node"))
- }
- }
- return nil
- }
|