mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-08 03:33:56 +00:00
Merge pull request #116254 from pohly/dra-node-authorizer
node authorizer: limit kubelet access to ResourceClaim objects
This commit is contained in:
commit
f55f2785e2
@ -288,6 +288,9 @@ func (p *Plugin) admitPodStatus(nodeName string, a admission.Attributes) error {
|
||||
if !labels.Equals(oldPod.Labels, newPod.Labels) {
|
||||
return admission.NewForbidden(a, fmt.Errorf("node %q cannot update labels through pod status", nodeName))
|
||||
}
|
||||
if !resourceClaimStatusesEqual(oldPod.Status.ResourceClaimStatuses, newPod.Status.ResourceClaimStatuses) {
|
||||
return admission.NewForbidden(a, fmt.Errorf("node %q cannot update resource claim statues", nodeName))
|
||||
}
|
||||
return nil
|
||||
|
||||
default:
|
||||
@ -295,6 +298,29 @@ func (p *Plugin) admitPodStatus(nodeName string, a admission.Attributes) error {
|
||||
}
|
||||
}
|
||||
|
||||
func resourceClaimStatusesEqual(statusA, statusB []api.PodResourceClaimStatus) bool {
|
||||
if len(statusA) != len(statusB) {
|
||||
return false
|
||||
}
|
||||
// In most cases, status entries only get added once and not modified.
|
||||
// But this cannot be guaranteed, so for the sake of correctness in all
|
||||
// cases this code here has to check.
|
||||
for i := range statusA {
|
||||
if statusA[i].Name != statusB[i].Name {
|
||||
return false
|
||||
}
|
||||
claimNameA := statusA[i].ResourceClaimName
|
||||
claimNameB := statusB[i].ResourceClaimName
|
||||
if (claimNameA == nil) != (claimNameB == nil) {
|
||||
return false
|
||||
}
|
||||
if claimNameA != nil && *claimNameA != *claimNameB {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 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() {
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/component-helpers/storage/ephemeral"
|
||||
"k8s.io/dynamic-resource-allocation/resourceclaim"
|
||||
pvutil "k8s.io/kubernetes/pkg/api/v1/persistentvolume"
|
||||
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
||||
"k8s.io/kubernetes/third_party/forked/gonum/graph"
|
||||
@ -117,6 +118,7 @@ const (
|
||||
podVertexType
|
||||
pvcVertexType
|
||||
pvVertexType
|
||||
resourceClaimVertexType
|
||||
secretVertexType
|
||||
vaVertexType
|
||||
serviceAccountVertexType
|
||||
@ -128,6 +130,7 @@ var vertexTypes = map[vertexType]string{
|
||||
podVertexType: "pod",
|
||||
pvcVertexType: "pvc",
|
||||
pvVertexType: "pv",
|
||||
resourceClaimVertexType: "resourceclaim",
|
||||
secretVertexType: "secret",
|
||||
vaVertexType: "volumeattachment",
|
||||
serviceAccountVertexType: "serviceAccount",
|
||||
@ -393,6 +396,20 @@ func (g *Graph) AddPod(pod *corev1.Pod) {
|
||||
g.addEdgeToDestinationIndex_locked(e)
|
||||
}
|
||||
}
|
||||
|
||||
for _, podResourceClaim := range pod.Spec.ResourceClaims {
|
||||
claimName, _, err := resourceclaim.Name(pod, &podResourceClaim)
|
||||
// Do we have a valid claim name? If yes, add an edge that grants
|
||||
// kubelet access to that claim. An error indicates that a claim
|
||||
// still needs to be created, nil that intentionally no claim
|
||||
// was created and never will be because it isn't needed.
|
||||
if err == nil && claimName != nil {
|
||||
claimVertex := g.getOrCreateVertex_locked(resourceClaimVertexType, pod.Namespace, *claimName)
|
||||
e := newDestinationEdge(claimVertex, podVertex, nodeVertex)
|
||||
g.graph.SetEdge(e)
|
||||
g.addEdgeToDestinationIndex_locked(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
func (g *Graph) DeletePod(name, namespace string) {
|
||||
start := time.Now()
|
||||
|
@ -78,8 +78,9 @@ func (g *graphPopulator) updatePod(oldObj, obj interface{}) {
|
||||
return
|
||||
}
|
||||
if oldPod, ok := oldObj.(*corev1.Pod); ok && oldPod != nil {
|
||||
if (pod.Spec.NodeName == oldPod.Spec.NodeName) && (pod.UID == oldPod.UID) {
|
||||
// Node and uid are unchanged, all object references in the pod spec are immutable
|
||||
if (pod.Spec.NodeName == oldPod.Spec.NodeName) && (pod.UID == oldPod.UID) &&
|
||||
resourceClaimStatusesEqual(oldPod.Status.ResourceClaimStatuses, pod.Status.ResourceClaimStatuses) {
|
||||
// Node and uid are unchanged, all object references in the pod spec are immutable respectively unmodified (claim statuses).
|
||||
klog.V(5).Infof("updatePod %s/%s, node unchanged", pod.Namespace, pod.Name)
|
||||
return
|
||||
}
|
||||
@ -91,6 +92,29 @@ func (g *graphPopulator) updatePod(oldObj, obj interface{}) {
|
||||
klog.V(5).Infof("updatePod %s/%s for node %s completed in %v", pod.Namespace, pod.Name, pod.Spec.NodeName, time.Since(startTime))
|
||||
}
|
||||
|
||||
func resourceClaimStatusesEqual(statusA, statusB []corev1.PodResourceClaimStatus) bool {
|
||||
if len(statusA) != len(statusB) {
|
||||
return false
|
||||
}
|
||||
// In most cases, status entries only get added once and not modified.
|
||||
// But this cannot be guaranteed, so for the sake of correctness in all
|
||||
// cases this code here has to check.
|
||||
for i := range statusA {
|
||||
if statusA[i].Name != statusB[i].Name {
|
||||
return false
|
||||
}
|
||||
claimNameA := statusA[i].ResourceClaimName
|
||||
claimNameB := statusB[i].ResourceClaimName
|
||||
if (claimNameA == nil) != (claimNameB == nil) {
|
||||
return false
|
||||
}
|
||||
if claimNameA != nil && *claimNameA != *claimNameB {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (g *graphPopulator) deletePod(obj interface{}) {
|
||||
if tombstone, ok := obj.(cache.DeletedFinalStateUnknown); ok {
|
||||
obj = tombstone.Obj
|
||||
|
@ -30,6 +30,7 @@ import (
|
||||
"k8s.io/component-base/featuregate"
|
||||
coordapi "k8s.io/kubernetes/pkg/apis/coordination"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
resourceapi "k8s.io/kubernetes/pkg/apis/resource"
|
||||
storageapi "k8s.io/kubernetes/pkg/apis/storage"
|
||||
"k8s.io/kubernetes/pkg/auth/nodeidentifier"
|
||||
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac"
|
||||
@ -40,7 +41,7 @@ import (
|
||||
// NodeAuthorizer authorizes requests from kubelets, with the following logic:
|
||||
// 1. If a request is not from a node (NodeIdentity() returns isNode=false), reject
|
||||
// 2. If a specific node cannot be identified (NodeIdentity() returns nodeName=""), reject
|
||||
// 3. If a request is for a secret, configmap, persistent volume or persistent volume claim, reject unless the verb is get, and the requested object is related to the requesting node:
|
||||
// 3. If a request is for a secret, configmap, persistent volume, resource claim, or persistent volume claim, reject unless the verb is get, and the requested object is related to the requesting node:
|
||||
// node <- configmap
|
||||
// node <- pod
|
||||
// node <- pod <- secret
|
||||
@ -48,6 +49,7 @@ import (
|
||||
// node <- pod <- pvc
|
||||
// node <- pod <- pvc <- pv
|
||||
// node <- pod <- pvc <- pv <- secret
|
||||
// node <- pod <- ResourceClaim
|
||||
// 4. For other resources, authorize all nodes uniformly using statically defined rules
|
||||
type NodeAuthorizer struct {
|
||||
graph *Graph
|
||||
@ -72,14 +74,15 @@ func NewAuthorizer(graph *Graph, identifier nodeidentifier.NodeIdentifier, rules
|
||||
}
|
||||
|
||||
var (
|
||||
configMapResource = api.Resource("configmaps")
|
||||
secretResource = api.Resource("secrets")
|
||||
pvcResource = api.Resource("persistentvolumeclaims")
|
||||
pvResource = api.Resource("persistentvolumes")
|
||||
vaResource = storageapi.Resource("volumeattachments")
|
||||
svcAcctResource = api.Resource("serviceaccounts")
|
||||
leaseResource = coordapi.Resource("leases")
|
||||
csiNodeResource = storageapi.Resource("csinodes")
|
||||
configMapResource = api.Resource("configmaps")
|
||||
secretResource = api.Resource("secrets")
|
||||
pvcResource = api.Resource("persistentvolumeclaims")
|
||||
pvResource = api.Resource("persistentvolumes")
|
||||
resourceClaimResource = resourceapi.Resource("resourceclaims")
|
||||
vaResource = storageapi.Resource("volumeattachments")
|
||||
svcAcctResource = api.Resource("serviceaccounts")
|
||||
leaseResource = coordapi.Resource("leases")
|
||||
csiNodeResource = storageapi.Resource("csinodes")
|
||||
)
|
||||
|
||||
func (r *NodeAuthorizer) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
|
||||
@ -117,6 +120,8 @@ func (r *NodeAuthorizer) Authorize(ctx context.Context, attrs authorizer.Attribu
|
||||
return r.authorizeGet(nodeName, pvcVertexType, attrs)
|
||||
case pvResource:
|
||||
return r.authorizeGet(nodeName, pvVertexType, attrs)
|
||||
case resourceClaimResource:
|
||||
return r.authorizeGet(nodeName, resourceClaimVertexType, attrs)
|
||||
case vaResource:
|
||||
return r.authorizeGet(nodeName, vaVertexType, attrs)
|
||||
case svcAcctResource:
|
||||
|
@ -42,16 +42,19 @@ func TestAuthorizer(t *testing.T) {
|
||||
g := NewGraph()
|
||||
|
||||
opts := &sampleDataOpts{
|
||||
nodes: 2,
|
||||
namespaces: 2,
|
||||
podsPerNode: 2,
|
||||
attachmentsPerNode: 1,
|
||||
sharedConfigMapsPerPod: 0,
|
||||
uniqueConfigMapsPerPod: 1,
|
||||
sharedSecretsPerPod: 1,
|
||||
uniqueSecretsPerPod: 1,
|
||||
sharedPVCsPerPod: 0,
|
||||
uniquePVCsPerPod: 1,
|
||||
nodes: 2,
|
||||
namespaces: 2,
|
||||
podsPerNode: 2,
|
||||
attachmentsPerNode: 1,
|
||||
sharedConfigMapsPerPod: 0,
|
||||
uniqueConfigMapsPerPod: 1,
|
||||
sharedSecretsPerPod: 1,
|
||||
uniqueSecretsPerPod: 1,
|
||||
sharedPVCsPerPod: 0,
|
||||
uniquePVCsPerPod: 1,
|
||||
uniqueResourceClaimsPerPod: 1,
|
||||
uniqueResourceClaimTemplatesPerPod: 1,
|
||||
uniqueResourceClaimTemplatesWithClaimPerPod: 1,
|
||||
}
|
||||
nodes, pods, pvs, attachments := generate(opts)
|
||||
populate(g, nodes, pods, pvs, attachments)
|
||||
@ -117,6 +120,16 @@ func TestAuthorizer(t *testing.T) {
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumeclaims", Name: "pvc0-pod0-node0", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionAllow,
|
||||
},
|
||||
{
|
||||
name: "allowed resource claim",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "resourceclaims", APIGroup: "resource.k8s.io", Name: "claim0-pod0-node0-ns0", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionAllow,
|
||||
},
|
||||
{
|
||||
name: "allowed resource claim with template",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "resourceclaims", APIGroup: "resource.k8s.io", Name: "generated-claim-pod0-node0-ns0-0", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionAllow,
|
||||
},
|
||||
{
|
||||
name: "allowed pv",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumes", Name: "pv0-pod0-node0-ns0", Namespace: ""},
|
||||
@ -142,6 +155,16 @@ func TestAuthorizer(t *testing.T) {
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumeclaims", Name: "pvc0-pod0-node1", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionNoOpinion,
|
||||
},
|
||||
{
|
||||
name: "disallowed resource claim, other node",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "resourceclaims", APIGroup: "resource.k8s.io", Name: "claim0-pod0-node1-ns0", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionNoOpinion,
|
||||
},
|
||||
{
|
||||
name: "disallowed resource claim with template",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "resourceclaims", APIGroup: "resource.k8s.io", Name: "pod0-node1-claimtemplate0", Namespace: "ns0"},
|
||||
expect: authorizer.DecisionNoOpinion,
|
||||
},
|
||||
{
|
||||
name: "disallowed pv",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumes", Name: "pv0-pod0-node1-ns0", Namespace: ""},
|
||||
@ -468,9 +491,12 @@ type sampleDataOpts struct {
|
||||
sharedSecretsPerPod int
|
||||
sharedPVCsPerPod int
|
||||
|
||||
uniqueSecretsPerPod int
|
||||
uniqueConfigMapsPerPod int
|
||||
uniquePVCsPerPod int
|
||||
uniqueSecretsPerPod int
|
||||
uniqueConfigMapsPerPod int
|
||||
uniquePVCsPerPod int
|
||||
uniqueResourceClaimsPerPod int
|
||||
uniqueResourceClaimTemplatesPerPod int
|
||||
uniqueResourceClaimTemplatesWithClaimPerPod int
|
||||
}
|
||||
|
||||
func BenchmarkPopulationAllocation(b *testing.B) {
|
||||
@ -845,6 +871,40 @@ func generatePod(name, namespace, nodeName, svcAccountName string, opts *sampleD
|
||||
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: pv.Spec.ClaimRef.Name},
|
||||
}})
|
||||
}
|
||||
for i := 0; i < opts.uniqueResourceClaimsPerPod; i++ {
|
||||
claimName := fmt.Sprintf("claim%d-%s-%s", i, pod.Name, pod.Namespace)
|
||||
pod.Spec.ResourceClaims = append(pod.Spec.ResourceClaims, corev1.PodResourceClaim{
|
||||
Name: fmt.Sprintf("claim%d", i),
|
||||
Source: corev1.ClaimSource{
|
||||
ResourceClaimName: &claimName,
|
||||
},
|
||||
})
|
||||
}
|
||||
for i := 0; i < opts.uniqueResourceClaimTemplatesPerPod; i++ {
|
||||
claimTemplateName := fmt.Sprintf("claimtemplate%d-%s-%s", i, pod.Name, pod.Namespace)
|
||||
podClaimName := fmt.Sprintf("claimtemplate%d", i)
|
||||
pod.Spec.ResourceClaims = append(pod.Spec.ResourceClaims, corev1.PodResourceClaim{
|
||||
Name: podClaimName,
|
||||
Source: corev1.ClaimSource{
|
||||
ResourceClaimTemplateName: &claimTemplateName,
|
||||
},
|
||||
})
|
||||
}
|
||||
for i := 0; i < opts.uniqueResourceClaimTemplatesWithClaimPerPod; i++ {
|
||||
claimTemplateName := fmt.Sprintf("claimtemplate%d-%s-%s", i, pod.Name, pod.Namespace)
|
||||
podClaimName := fmt.Sprintf("claimtemplate-with-claim%d", i)
|
||||
claimName := fmt.Sprintf("generated-claim-%s-%s-%d", pod.Name, pod.Namespace, i)
|
||||
pod.Spec.ResourceClaims = append(pod.Spec.ResourceClaims, corev1.PodResourceClaim{
|
||||
Name: podClaimName,
|
||||
Source: corev1.ClaimSource{
|
||||
ResourceClaimTemplateName: &claimTemplateName,
|
||||
},
|
||||
})
|
||||
pod.Status.ResourceClaimStatuses = append(pod.Status.ResourceClaimStatuses, corev1.PodResourceClaimStatus{
|
||||
Name: podClaimName,
|
||||
ResourceClaimName: &claimName,
|
||||
})
|
||||
}
|
||||
// Choose shared pvcs randomly from shared pvcs in a namespace.
|
||||
subset = randomSubset(opts.sharedPVCsPerPod, opts.sharedPVCsPerNamespace)
|
||||
for _, i := range subset {
|
||||
|
@ -27,14 +27,18 @@ import (
|
||||
coordination "k8s.io/api/coordination/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
policy "k8s.io/api/policy/v1"
|
||||
"k8s.io/api/resource/v1alpha2"
|
||||
storagev1 "k8s.io/api/storage/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
"k8s.io/utils/pointer"
|
||||
)
|
||||
@ -61,7 +65,10 @@ func TestNodeAuthorizer(t *testing.T) {
|
||||
}, "\n"))
|
||||
tokenFile.Close()
|
||||
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DynamicResourceAllocation, true)()
|
||||
|
||||
server := kubeapiservertesting.StartTestServerOrDie(t, nil, []string{
|
||||
"--runtime-config=api/all=true",
|
||||
"--authorization-mode", "Node,RBAC",
|
||||
"--token-auth-file", tokenFile.Name(),
|
||||
"--enable-admission-plugins", "NodeRestriction",
|
||||
@ -100,6 +107,13 @@ func TestNodeAuthorizer(t *testing.T) {
|
||||
if _, err := superuserClient.CoreV1().ConfigMaps("ns").Create(context.TODO(), &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "myconfigmap"}}, metav1.CreateOptions{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := superuserClient.ResourceV1alpha2().ResourceClaims("ns").Create(context.TODO(), &v1alpha2.ResourceClaim{ObjectMeta: metav1.ObjectMeta{Name: "mynamedresourceclaim"}, Spec: v1alpha2.ResourceClaimSpec{ResourceClassName: "example.com"}}, metav1.CreateOptions{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := superuserClient.ResourceV1alpha2().ResourceClaims("ns").Create(context.TODO(), &v1alpha2.ResourceClaim{ObjectMeta: metav1.ObjectMeta{Name: "mytemplatizedresourceclaim"}, Spec: v1alpha2.ResourceClaimSpec{ResourceClassName: "example.com"}}, metav1.CreateOptions{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pvName := "mypv"
|
||||
if _, err := superuserClientExternal.StorageV1().VolumeAttachments().Create(context.TODO(), &storagev1.VolumeAttachment{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "myattachment"},
|
||||
@ -169,6 +183,34 @@ func TestNodeAuthorizer(t *testing.T) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
getResourceClaim := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
_, err := client.ResourceV1alpha2().ResourceClaims("ns").Get(context.TODO(), "mynamedresourceclaim", metav1.GetOptions{})
|
||||
return err
|
||||
}
|
||||
}
|
||||
getResourceClaimTemplate := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
_, err := client.ResourceV1alpha2().ResourceClaims("ns").Get(context.TODO(), "mytemplatizedresourceclaim", metav1.GetOptions{})
|
||||
return err
|
||||
}
|
||||
}
|
||||
addResourceClaimTemplateReference := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
_, err := client.CoreV1().Pods("ns").Patch(context.TODO(), "node2normalpod", types.MergePatchType,
|
||||
[]byte(`{"status":{"resourceClaimStatuses":[{"name":"templateclaim","resourceClaimName":"mytemplatizedresourceclaim"}]}}`),
|
||||
metav1.PatchOptions{}, "status")
|
||||
return err
|
||||
}
|
||||
}
|
||||
removeResourceClaimReference := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
_, err := client.CoreV1().Pods("ns").Patch(context.TODO(), "node2normalpod", types.MergePatchType,
|
||||
[]byte(`{"status":{"resourceClaimStatuses":null}}`),
|
||||
metav1.PatchOptions{}, "status")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
createNode2NormalPod := func(client clientset.Interface) func() error {
|
||||
return func() error {
|
||||
@ -182,6 +224,10 @@ func TestNodeAuthorizer(t *testing.T) {
|
||||
{Name: "cm", VolumeSource: corev1.VolumeSource{ConfigMap: &corev1.ConfigMapVolumeSource{LocalObjectReference: corev1.LocalObjectReference{Name: "myconfigmap"}}}},
|
||||
{Name: "pvc", VolumeSource: corev1.VolumeSource{PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: "mypvc"}}},
|
||||
},
|
||||
ResourceClaims: []corev1.PodResourceClaim{
|
||||
{Name: "namedclaim", Source: corev1.ClaimSource{ResourceClaimName: pointer.String("mynamedresourceclaim")}},
|
||||
{Name: "templateclaim", Source: corev1.ClaimSource{ResourceClaimTemplateName: pointer.String("myresourceclaimtemplate")}},
|
||||
},
|
||||
},
|
||||
}, metav1.CreateOptions{})
|
||||
return err
|
||||
@ -428,6 +474,8 @@ func TestNodeAuthorizer(t *testing.T) {
|
||||
expectForbidden(t, getConfigMap(nodeanonClient))
|
||||
expectForbidden(t, getPVC(nodeanonClient))
|
||||
expectForbidden(t, getPV(nodeanonClient))
|
||||
expectForbidden(t, getResourceClaim(nodeanonClient))
|
||||
expectForbidden(t, getResourceClaimTemplate(nodeanonClient))
|
||||
expectForbidden(t, createNode2NormalPod(nodeanonClient))
|
||||
expectForbidden(t, deleteNode2NormalPod(nodeanonClient))
|
||||
expectForbidden(t, createNode2MirrorPodEviction(nodeanonClient))
|
||||
@ -440,6 +488,8 @@ func TestNodeAuthorizer(t *testing.T) {
|
||||
expectForbidden(t, getConfigMap(node1Client))
|
||||
expectForbidden(t, getPVC(node1Client))
|
||||
expectForbidden(t, getPV(node1Client))
|
||||
expectForbidden(t, getResourceClaim(node1Client))
|
||||
expectForbidden(t, getResourceClaimTemplate(node1Client))
|
||||
expectForbidden(t, createNode2NormalPod(nodeanonClient))
|
||||
expectNotFound(t, createNode2MirrorPodEviction(node1Client))
|
||||
expectForbidden(t, createNode2(node1Client))
|
||||
@ -452,6 +502,8 @@ func TestNodeAuthorizer(t *testing.T) {
|
||||
expectForbidden(t, getConfigMap(node2Client))
|
||||
expectForbidden(t, getPVC(node2Client))
|
||||
expectForbidden(t, getPV(node2Client))
|
||||
expectForbidden(t, getResourceClaim(node2Client))
|
||||
expectForbidden(t, getResourceClaimTemplate(node2Client))
|
||||
|
||||
expectForbidden(t, createNode2NormalPod(nodeanonClient))
|
||||
// mirror pod and self node lifecycle is allowed
|
||||
@ -479,6 +531,8 @@ func TestNodeAuthorizer(t *testing.T) {
|
||||
expectForbidden(t, getConfigMap(nodeanonClient))
|
||||
expectForbidden(t, getPVC(nodeanonClient))
|
||||
expectForbidden(t, getPV(nodeanonClient))
|
||||
expectForbidden(t, getResourceClaim(nodeanonClient))
|
||||
expectForbidden(t, getResourceClaimTemplate(nodeanonClient))
|
||||
expectForbidden(t, createNode2NormalPod(nodeanonClient))
|
||||
expectForbidden(t, updateNode2NormalPodStatus(nodeanonClient))
|
||||
expectForbidden(t, deleteNode2NormalPod(nodeanonClient))
|
||||
@ -492,6 +546,8 @@ func TestNodeAuthorizer(t *testing.T) {
|
||||
expectForbidden(t, getConfigMap(node1Client))
|
||||
expectForbidden(t, getPVC(node1Client))
|
||||
expectForbidden(t, getPV(node1Client))
|
||||
expectForbidden(t, getResourceClaim(node1Client))
|
||||
expectForbidden(t, getResourceClaimTemplate(node1Client))
|
||||
expectForbidden(t, createNode2NormalPod(node1Client))
|
||||
expectForbidden(t, updateNode2NormalPodStatus(node1Client))
|
||||
expectForbidden(t, deleteNode2NormalPod(node1Client))
|
||||
@ -507,6 +563,26 @@ func TestNodeAuthorizer(t *testing.T) {
|
||||
expectAllowed(t, getPVC(node2Client))
|
||||
expectAllowed(t, getPV(node2Client))
|
||||
|
||||
// node2 can only get direct claim references
|
||||
expectAllowed(t, getResourceClaim(node2Client))
|
||||
expectForbidden(t, getResourceClaimTemplate(node2Client))
|
||||
|
||||
// node cannot add a claim reference
|
||||
expectForbidden(t, addResourceClaimTemplateReference(node2Client))
|
||||
// superuser can add a claim reference
|
||||
expectAllowed(t, addResourceClaimTemplateReference(superuserClient))
|
||||
// node can get direct and template claim references
|
||||
expectAllowed(t, getResourceClaim(node2Client))
|
||||
expectAllowed(t, getResourceClaimTemplate(node2Client))
|
||||
|
||||
// node cannot remove a claim reference
|
||||
expectForbidden(t, removeResourceClaimReference(node2Client))
|
||||
// superuser can remove a claim reference
|
||||
expectAllowed(t, removeResourceClaimReference(superuserClient))
|
||||
// node2 can only get direct claim references
|
||||
expectAllowed(t, getResourceClaim(node2Client))
|
||||
expectForbidden(t, getResourceClaimTemplate(node2Client))
|
||||
|
||||
expectForbidden(t, createNode2NormalPod(node2Client))
|
||||
expectAllowed(t, updateNode2NormalPodStatus(node2Client))
|
||||
expectAllowed(t, deleteNode2NormalPod(node2Client))
|
||||
|
Loading…
Reference in New Issue
Block a user