diff --git a/plugin/pkg/admission/noderestriction/admission.go b/plugin/pkg/admission/noderestriction/admission.go index 3b352d117344..1b9851dd5051 100644 --- a/plugin/pkg/admission/noderestriction/admission.go +++ b/plugin/pkg/admission/noderestriction/admission.go @@ -338,6 +338,12 @@ func (c *nodePlugin) admitNode(nodeName string, a admission.Attributes) error { if node.Spec.ConfigSource != nil && !apiequality.Semantic.DeepEqual(node.Spec.ConfigSource, oldNode.Spec.ConfigSource) { return admission.NewForbidden(a, fmt.Errorf("cannot update configSource to a new non-nil configSource")) } + + // 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("cannot modify taints")) + } } return nil diff --git a/plugin/pkg/admission/noderestriction/admission_test.go b/plugin/pkg/admission/noderestriction/admission_test.go index 684653579456..0c22775dfd83 100644 --- a/plugin/pkg/admission/noderestriction/admission_test.go +++ b/plugin/pkg/admission/noderestriction/admission_test.go @@ -105,7 +105,9 @@ func Test_nodePlugin_Admit(t *testing.T) { UID: "quxUID", KubeletConfigKey: "kubelet", }}}} - othernodeObj = &api.Node{ObjectMeta: metav1.ObjectMeta{Name: "othernode"}} + mynodeObjTaintA = &api.Node{ObjectMeta: mynodeObjMeta, Spec: api.NodeSpec{Taints: []api.Taint{{Key: "mykey", Value: "A"}}}} + mynodeObjTaintB = &api.Node{ObjectMeta: mynodeObjMeta, Spec: api.NodeSpec{Taints: []api.Taint{{Key: "mykey", Value: "B"}}}} + othernodeObj = &api.Node{ObjectMeta: metav1.ObjectMeta{Name: "othernode"}} mymirrorpod = makeTestPod("ns", "mymirrorpod", "mynode", true) othermirrorpod = makeTestPod("ns", "othermirrorpod", "othernode", true) @@ -633,6 +635,12 @@ func Test_nodePlugin_Admit(t *testing.T) { attributes: admission.NewAttributesRecord(mynodeObj, nil, nodeKind, mynodeObj.Namespace, "", nodeResource, "", admission.Create, mynode), err: "", }, + { + name: "allow create of my node with taints", + podsGetter: noExistingPods, + attributes: admission.NewAttributesRecord(mynodeObjTaintA, nil, nodeKind, mynodeObj.Namespace, "", nodeResource, "", admission.Create, mynode), + err: "", + }, { name: "allow update of my node", podsGetter: existingPods, @@ -681,6 +689,30 @@ func Test_nodePlugin_Admit(t *testing.T) { attributes: admission.NewAttributesRecord(mynodeObj, mynodeObjConfigA, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, mynode), err: "", }, + { + name: "allow update of my node: no change to taints", + podsGetter: existingPods, + attributes: admission.NewAttributesRecord(mynodeObjTaintA, mynodeObjTaintA, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, mynode), + err: "", + }, + { + name: "forbid update of my node: add taints", + podsGetter: existingPods, + attributes: admission.NewAttributesRecord(mynodeObjTaintA, mynodeObj, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, mynode), + err: "cannot modify taints", + }, + { + name: "forbid update of my node: remove taints", + podsGetter: existingPods, + attributes: admission.NewAttributesRecord(mynodeObj, mynodeObjTaintA, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, mynode), + err: "cannot modify taints", + }, + { + name: "forbid update of my node: change taints", + podsGetter: existingPods, + attributes: admission.NewAttributesRecord(mynodeObjTaintA, mynodeObjTaintB, nodeKind, mynodeObj.Namespace, mynodeObj.Name, nodeResource, "", admission.Update, mynode), + err: "cannot modify taints", + }, // Other node object {