mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-06 18:54:06 +00:00
node authorizer changes to allow read on svcaccounts
Signed-off-by: Anish Ramasekar <anish.ramasekar@gmail.com>
This commit is contained in:
parent
d398de294d
commit
6defd8c0bd
@ -135,7 +135,7 @@ func (r *NodeAuthorizer) Authorize(ctx context.Context, attrs authorizer.Attribu
|
|||||||
case vaResource:
|
case vaResource:
|
||||||
return r.authorizeGet(nodeName, vaVertexType, attrs)
|
return r.authorizeGet(nodeName, vaVertexType, attrs)
|
||||||
case svcAcctResource:
|
case svcAcctResource:
|
||||||
return r.authorizeCreateToken(nodeName, serviceAccountVertexType, attrs)
|
return r.authorizeServiceAccount(nodeName, attrs)
|
||||||
case leaseResource:
|
case leaseResource:
|
||||||
return r.authorizeLease(nodeName, attrs)
|
return r.authorizeLease(nodeName, attrs)
|
||||||
case csiNodeResource:
|
case csiNodeResource:
|
||||||
@ -196,7 +196,7 @@ func (r *NodeAuthorizer) authorizeGet(nodeName string, startingType vertexType,
|
|||||||
func (r *NodeAuthorizer) authorizeReadNamespacedObject(nodeName string, startingType vertexType, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
|
func (r *NodeAuthorizer) authorizeReadNamespacedObject(nodeName string, startingType vertexType, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
|
||||||
switch attrs.GetVerb() {
|
switch attrs.GetVerb() {
|
||||||
case "get", "list", "watch":
|
case "get", "list", "watch":
|
||||||
//ok
|
// ok
|
||||||
default:
|
default:
|
||||||
klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
|
klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
|
||||||
return authorizer.DecisionNoOpinion, "can only read resources of this type", nil
|
return authorizer.DecisionNoOpinion, "can only read resources of this type", nil
|
||||||
@ -231,6 +231,23 @@ func (r *NodeAuthorizer) authorize(nodeName string, startingType vertexType, att
|
|||||||
return authorizer.DecisionAllow, "", nil
|
return authorizer.DecisionAllow, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// authorizeServiceAccount authorizes
|
||||||
|
// - "get" requests to serviceaccounts when KubeletServiceAccountTokenForCredentialProviders feature is enabled
|
||||||
|
// - "create" requests to serviceaccounts 'token' subresource of pods running on a node
|
||||||
|
func (r *NodeAuthorizer) authorizeServiceAccount(nodeName string, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
|
||||||
|
verb := attrs.GetVerb()
|
||||||
|
|
||||||
|
if verb == "get" && attrs.GetSubresource() == "" {
|
||||||
|
if !r.features.Enabled(features.KubeletServiceAccountTokenForCredentialProviders) {
|
||||||
|
klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
|
||||||
|
return authorizer.DecisionNoOpinion, "not allowed to get service accounts", nil
|
||||||
|
}
|
||||||
|
return r.authorizeReadNamespacedObject(nodeName, serviceAccountVertexType, attrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.authorizeCreateToken(nodeName, serviceAccountVertexType, attrs)
|
||||||
|
}
|
||||||
|
|
||||||
// authorizeCreateToken authorizes "create" requests to serviceaccounts 'token'
|
// authorizeCreateToken authorizes "create" requests to serviceaccounts 'token'
|
||||||
// subresource of pods running on a node
|
// subresource of pods running on a node
|
||||||
func (r *NodeAuthorizer) authorizeCreateToken(nodeName string, startingType vertexType, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
|
func (r *NodeAuthorizer) authorizeCreateToken(nodeName string, startingType vertexType, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
|
||||||
@ -262,7 +279,7 @@ func (r *NodeAuthorizer) authorizeLease(nodeName string, attrs authorizer.Attrib
|
|||||||
verb := attrs.GetVerb()
|
verb := attrs.GetVerb()
|
||||||
switch verb {
|
switch verb {
|
||||||
case "get", "create", "update", "patch", "delete":
|
case "get", "create", "update", "patch", "delete":
|
||||||
//ok
|
// ok
|
||||||
default:
|
default:
|
||||||
klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
|
klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
|
||||||
return authorizer.DecisionNoOpinion, "can only get, create, update, patch, or delete a node lease", nil
|
return authorizer.DecisionNoOpinion, "can only get, create, update, patch, or delete a node lease", nil
|
||||||
@ -291,7 +308,7 @@ func (r *NodeAuthorizer) authorizeCSINode(nodeName string, attrs authorizer.Attr
|
|||||||
verb := attrs.GetVerb()
|
verb := attrs.GetVerb()
|
||||||
switch verb {
|
switch verb {
|
||||||
case "get", "create", "update", "patch", "delete":
|
case "get", "create", "update", "patch", "delete":
|
||||||
//ok
|
// ok
|
||||||
default:
|
default:
|
||||||
klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
|
klog.V(2).Infof("NODE DENY: '%s' %#v", nodeName, attrs)
|
||||||
return authorizer.DecisionNoOpinion, "can only get, create, update, patch, or delete a CSINode", nil
|
return authorizer.DecisionNoOpinion, "can only get, create, update, patch, or delete a CSINode", nil
|
||||||
|
@ -80,6 +80,12 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
featuregatetesting.SetFeatureGateDuringTest(t, selectorAuthzEnabled, genericfeatures.AuthorizeWithSelectors, true)
|
featuregatetesting.SetFeatureGateDuringTest(t, selectorAuthzEnabled, genericfeatures.AuthorizeWithSelectors, true)
|
||||||
featuregatetesting.SetFeatureGateDuringTest(t, selectorAuthzEnabled, features.AuthorizeNodeWithSelectors, true)
|
featuregatetesting.SetFeatureGateDuringTest(t, selectorAuthzEnabled, features.AuthorizeNodeWithSelectors, true)
|
||||||
|
|
||||||
|
serviceAccountTokenForCredentialProvidersDisabled := utilfeature.DefaultFeatureGate.DeepCopy()
|
||||||
|
featuregatetesting.SetFeatureGateDuringTest(t, serviceAccountTokenForCredentialProvidersDisabled, features.KubeletServiceAccountTokenForCredentialProviders, false)
|
||||||
|
|
||||||
|
serviceAccountTokenForCredentialProvidersEnabled := utilfeature.DefaultFeatureGate.DeepCopy()
|
||||||
|
featuregatetesting.SetFeatureGateDuringTest(t, serviceAccountTokenForCredentialProvidersEnabled, features.KubeletServiceAccountTokenForCredentialProviders, true)
|
||||||
|
|
||||||
featureVariants := []struct {
|
featureVariants := []struct {
|
||||||
suffix string
|
suffix string
|
||||||
features featuregate.FeatureGate
|
features featuregate.FeatureGate
|
||||||
@ -92,6 +98,7 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
attrs authorizer.AttributesRecord
|
attrs authorizer.AttributesRecord
|
||||||
expect authorizer.Decision
|
expect authorizer.Decision
|
||||||
|
expectReason string
|
||||||
features featuregate.FeatureGate
|
features featuregate.FeatureGate
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@ -118,16 +125,19 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
name: "disallowed list many secrets",
|
name: "disallowed list many secrets",
|
||||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "list", Resource: "secrets", Name: "", Namespace: "ns0"},
|
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "list", Resource: "secrets", Name: "", Namespace: "ns0"},
|
||||||
expect: authorizer.DecisionNoOpinion,
|
expect: authorizer.DecisionNoOpinion,
|
||||||
|
expectReason: "No Object name found,",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "disallowed watch many secrets",
|
name: "disallowed watch many secrets",
|
||||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "watch", Resource: "secrets", Name: "", Namespace: "ns0"},
|
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "watch", Resource: "secrets", Name: "", Namespace: "ns0"},
|
||||||
expect: authorizer.DecisionNoOpinion,
|
expect: authorizer.DecisionNoOpinion,
|
||||||
|
expectReason: "No Object name found,",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "disallowed list secrets from all namespaces with name",
|
name: "disallowed list secrets from all namespaces with name",
|
||||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "list", Resource: "secrets", Name: "secret0-pod0-node0", Namespace: ""},
|
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "list", Resource: "secrets", Name: "secret0-pod0-node0", Namespace: ""},
|
||||||
expect: authorizer.DecisionNoOpinion,
|
expect: authorizer.DecisionNoOpinion,
|
||||||
|
expectReason: "can only read namespaced object of this type",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "allowed shared secret via pod",
|
name: "allowed shared secret via pod",
|
||||||
@ -219,6 +229,33 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "update", Resource: "serviceaccounts", Subresource: "token", Name: "svcacct0-node0", Namespace: "ns0"},
|
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "update", Resource: "serviceaccounts", Subresource: "token", Name: "svcacct0-node0", Namespace: "ns0"},
|
||||||
expect: authorizer.DecisionNoOpinion,
|
expect: authorizer.DecisionNoOpinion,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "get allowed svcacct via pod - feature enabled",
|
||||||
|
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "serviceaccounts", Name: "svcacct0-node0", Namespace: "ns0"},
|
||||||
|
expect: authorizer.DecisionAllow,
|
||||||
|
features: serviceAccountTokenForCredentialProvidersEnabled,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "disallowed get svcacct via pod - feature disabled",
|
||||||
|
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "serviceaccounts", Name: "svcacct0-node0", Namespace: "ns0"},
|
||||||
|
expect: authorizer.DecisionNoOpinion,
|
||||||
|
features: serviceAccountTokenForCredentialProvidersDisabled,
|
||||||
|
expectReason: "not allowed to get service accounts",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "disallowed list svcacct via pod - feature disabled",
|
||||||
|
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "list", Resource: "serviceaccounts", Name: "svcacct0-node0", Namespace: "ns0"},
|
||||||
|
expect: authorizer.DecisionNoOpinion,
|
||||||
|
features: serviceAccountTokenForCredentialProvidersDisabled,
|
||||||
|
expectReason: "can only create tokens for individual service accounts",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "disallowed watch svcacct via pod - feature disabled",
|
||||||
|
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "watch", Resource: "serviceaccounts", Name: "svcacct0-node0", Namespace: "ns0"},
|
||||||
|
expect: authorizer.DecisionNoOpinion,
|
||||||
|
features: serviceAccountTokenForCredentialProvidersDisabled,
|
||||||
|
expectReason: "can only create tokens for individual service accounts",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "disallowed get lease in namespace other than kube-node-lease - feature enabled",
|
name: "disallowed get lease in namespace other than kube-node-lease - feature enabled",
|
||||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: "foo"},
|
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: "foo"},
|
||||||
@ -402,6 +439,7 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "list", Resource: "resourceslices", APIGroup: "resource.k8s.io"},
|
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "list", Resource: "resourceslices", APIGroup: "resource.k8s.io"},
|
||||||
expect: authorizer.DecisionNoOpinion,
|
expect: authorizer.DecisionNoOpinion,
|
||||||
features: selectorAuthzEnabled,
|
features: selectorAuthzEnabled,
|
||||||
|
expectReason: "can only list/watch/deletecollection resourceslices with nodeName field selector",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "allowed filtered watch ResourceSlices",
|
name: "allowed filtered watch ResourceSlices",
|
||||||
@ -419,6 +457,7 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "watch", Resource: "resourceslices", APIGroup: "resource.k8s.io"},
|
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "watch", Resource: "resourceslices", APIGroup: "resource.k8s.io"},
|
||||||
expect: authorizer.DecisionNoOpinion,
|
expect: authorizer.DecisionNoOpinion,
|
||||||
features: selectorAuthzEnabled,
|
features: selectorAuthzEnabled,
|
||||||
|
expectReason: "can only list/watch/deletecollection resourceslices with nodeName field selector",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "allowed get ResourceSlice",
|
name: "allowed get ResourceSlice",
|
||||||
@ -464,6 +503,7 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "pods", APIGroup: "", Name: "pod0-node1", Namespace: "ns0"},
|
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "pods", APIGroup: "", Name: "pod0-node1", Namespace: "ns0"},
|
||||||
expect: authorizer.DecisionNoOpinion, // stricter with selector authz enabled
|
expect: authorizer.DecisionNoOpinion, // stricter with selector authz enabled
|
||||||
features: selectorAuthzEnabled,
|
features: selectorAuthzEnabled,
|
||||||
|
expectReason: "no relationship found between node 'node0' and this object",
|
||||||
},
|
},
|
||||||
// list pods
|
// list pods
|
||||||
{
|
{
|
||||||
@ -492,6 +532,7 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "list", Resource: "pods", APIGroup: ""},
|
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "list", Resource: "pods", APIGroup: ""},
|
||||||
expect: authorizer.DecisionNoOpinion, // stricter with selector authz enabled
|
expect: authorizer.DecisionNoOpinion, // stricter with selector authz enabled
|
||||||
features: selectorAuthzEnabled,
|
features: selectorAuthzEnabled,
|
||||||
|
expectReason: "can only list/watch pods with spec.nodeName field selector",
|
||||||
},
|
},
|
||||||
// watch pods
|
// watch pods
|
||||||
{
|
{
|
||||||
@ -520,6 +561,7 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "watch", Resource: "pods", APIGroup: ""},
|
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "watch", Resource: "pods", APIGroup: ""},
|
||||||
expect: authorizer.DecisionNoOpinion, // stricter with selector authz enabled
|
expect: authorizer.DecisionNoOpinion, // stricter with selector authz enabled
|
||||||
features: selectorAuthzEnabled,
|
features: selectorAuthzEnabled,
|
||||||
|
expectReason: "can only list/watch pods with spec.nodeName field selector",
|
||||||
},
|
},
|
||||||
// create, delete pods
|
// create, delete pods
|
||||||
{
|
{
|
||||||
@ -608,6 +650,7 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "nodes", APIGroup: "", Name: "node1"},
|
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "nodes", APIGroup: "", Name: "node1"},
|
||||||
expect: authorizer.DecisionNoOpinion, // stricter with selector authz enabled
|
expect: authorizer.DecisionNoOpinion, // stricter with selector authz enabled
|
||||||
features: selectorAuthzEnabled,
|
features: selectorAuthzEnabled,
|
||||||
|
expectReason: "node 'node0' cannot read 'node1', only its own Node object",
|
||||||
},
|
},
|
||||||
// list nodes
|
// list nodes
|
||||||
{
|
{
|
||||||
@ -626,6 +669,7 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "list", Resource: "nodes", APIGroup: "", Name: "node1"},
|
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "list", Resource: "nodes", APIGroup: "", Name: "node1"},
|
||||||
expect: authorizer.DecisionNoOpinion, // stricter with selector authz enabled
|
expect: authorizer.DecisionNoOpinion, // stricter with selector authz enabled
|
||||||
features: selectorAuthzEnabled,
|
features: selectorAuthzEnabled,
|
||||||
|
expectReason: "node 'node0' cannot read 'node1', only its own Node object",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "list all nodes - selector disabled",
|
name: "list all nodes - selector disabled",
|
||||||
@ -638,6 +682,7 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "list", Resource: "nodes", APIGroup: ""},
|
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "list", Resource: "nodes", APIGroup: ""},
|
||||||
expect: authorizer.DecisionNoOpinion, // stricter with selector authz enabled
|
expect: authorizer.DecisionNoOpinion, // stricter with selector authz enabled
|
||||||
features: selectorAuthzEnabled,
|
features: selectorAuthzEnabled,
|
||||||
|
expectReason: "node 'node0' cannot read all nodes, only its own Node object",
|
||||||
},
|
},
|
||||||
// watch nodes
|
// watch nodes
|
||||||
{
|
{
|
||||||
@ -656,6 +701,7 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "watch", Resource: "nodes", APIGroup: "", Name: "node1"},
|
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "watch", Resource: "nodes", APIGroup: "", Name: "node1"},
|
||||||
expect: authorizer.DecisionNoOpinion, // stricter with selector authz enabled
|
expect: authorizer.DecisionNoOpinion, // stricter with selector authz enabled
|
||||||
features: selectorAuthzEnabled,
|
features: selectorAuthzEnabled,
|
||||||
|
expectReason: "node 'node0' cannot read 'node1', only its own Node object",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "watch all nodes - selector disabled",
|
name: "watch all nodes - selector disabled",
|
||||||
@ -668,6 +714,7 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "watch", Resource: "nodes", APIGroup: ""},
|
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "watch", Resource: "nodes", APIGroup: ""},
|
||||||
expect: authorizer.DecisionNoOpinion, // stricter with selector authz enabled
|
expect: authorizer.DecisionNoOpinion, // stricter with selector authz enabled
|
||||||
features: selectorAuthzEnabled,
|
features: selectorAuthzEnabled,
|
||||||
|
expectReason: "node 'node0' cannot read all nodes, only its own Node object",
|
||||||
},
|
},
|
||||||
// create nodes
|
// create nodes
|
||||||
{
|
{
|
||||||
@ -737,6 +784,9 @@ func TestNodeAuthorizer(t *testing.T) {
|
|||||||
if decision != tc.expect {
|
if decision != tc.expect {
|
||||||
t.Errorf("expected %v, got %v (%s)", tc.expect, decision, reason)
|
t.Errorf("expected %v, got %v (%s)", tc.expect, decision, reason)
|
||||||
}
|
}
|
||||||
|
if reason != tc.expectReason {
|
||||||
|
t.Errorf("expected reason %q, got %q", tc.expectReason, reason)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,6 +189,11 @@ func NodeRules() []rbacv1.PolicyRule {
|
|||||||
if utilfeature.DefaultFeatureGate.Enabled(features.ClusterTrustBundle) {
|
if utilfeature.DefaultFeatureGate.Enabled(features.ClusterTrustBundle) {
|
||||||
nodePolicyRules = append(nodePolicyRules, rbacv1helpers.NewRule("get", "list", "watch").Groups(certificatesGroup).Resources("clustertrustbundles").RuleOrDie())
|
nodePolicyRules = append(nodePolicyRules, rbacv1helpers.NewRule("get", "list", "watch").Groups(certificatesGroup).Resources("clustertrustbundles").RuleOrDie())
|
||||||
}
|
}
|
||||||
|
// Kubelet needs access to ServiceAccounts to support sending service account tokens to the credential provider.
|
||||||
|
// Use the Node authorizer to limit get to service accounts related to the node.
|
||||||
|
if utilfeature.DefaultFeatureGate.Enabled(features.KubeletServiceAccountTokenForCredentialProviders) {
|
||||||
|
nodePolicyRules = append(nodePolicyRules, rbacv1helpers.NewRule("get").Groups(legacyGroup).Resources("serviceaccounts").RuleOrDie())
|
||||||
|
}
|
||||||
|
|
||||||
return nodePolicyRules
|
return nodePolicyRules
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user