mirror of https://github.com/k3s-io/k3s
parent
456ebf5de7
commit
2cc75f0a5a
|
@ -107,16 +107,18 @@ const (
|
|||
pvVertexType
|
||||
secretVertexType
|
||||
vaVertexType
|
||||
serviceAccountVertexType
|
||||
)
|
||||
|
||||
var vertexTypes = map[vertexType]string{
|
||||
configMapVertexType: "configmap",
|
||||
nodeVertexType: "node",
|
||||
podVertexType: "pod",
|
||||
pvcVertexType: "pvc",
|
||||
pvVertexType: "pv",
|
||||
secretVertexType: "secret",
|
||||
vaVertexType: "volumeattachment",
|
||||
configMapVertexType: "configmap",
|
||||
nodeVertexType: "node",
|
||||
podVertexType: "pod",
|
||||
pvcVertexType: "pvc",
|
||||
pvVertexType: "pv",
|
||||
secretVertexType: "secret",
|
||||
vaVertexType: "volumeattachment",
|
||||
serviceAccountVertexType: "serviceAccount",
|
||||
}
|
||||
|
||||
// must be called under a write lock
|
||||
|
@ -204,6 +206,7 @@ func (g *Graph) deleteVertex_locked(vertexType vertexType, namespace, name strin
|
|||
// secret -> pod
|
||||
// configmap -> pod
|
||||
// pvc -> pod
|
||||
// svcacct -> pod
|
||||
func (g *Graph) AddPod(pod *api.Pod) {
|
||||
g.lock.Lock()
|
||||
defer g.lock.Unlock()
|
||||
|
@ -213,6 +216,14 @@ func (g *Graph) AddPod(pod *api.Pod) {
|
|||
nodeVertex := g.getOrCreateVertex_locked(nodeVertexType, "", pod.Spec.NodeName)
|
||||
g.graph.SetEdge(newDestinationEdge(podVertex, nodeVertex, nodeVertex))
|
||||
|
||||
// TODO(mikedanese): If the pod doesn't mount the service account secrets,
|
||||
// should the node still get access to the service account?
|
||||
//
|
||||
// ref https://github.com/kubernetes/kubernetes/issues/58790
|
||||
if len(pod.Spec.ServiceAccountName) > 0 {
|
||||
g.graph.SetEdge(newDestinationEdge(g.getOrCreateVertex_locked(serviceAccountVertexType, pod.Namespace, pod.Spec.ServiceAccountName), podVertex, nodeVertex))
|
||||
}
|
||||
|
||||
podutil.VisitPodSecretNames(pod, func(secret string) bool {
|
||||
g.graph.SetEdge(newDestinationEdge(g.getOrCreateVertex_locked(secretVertexType, pod.Namespace, secret), podVertex, nodeVertex))
|
||||
return true
|
||||
|
|
|
@ -70,6 +70,7 @@ var (
|
|||
pvcResource = api.Resource("persistentvolumeclaims")
|
||||
pvResource = api.Resource("persistentvolumes")
|
||||
vaResource = storageapi.Resource("volumeattachments")
|
||||
svcAcctResource = api.Resource("serviceaccounts")
|
||||
)
|
||||
|
||||
func (r *NodeAuthorizer) Authorize(attrs authorizer.Attributes) (authorizer.Decision, string, error) {
|
||||
|
@ -106,6 +107,11 @@ func (r *NodeAuthorizer) Authorize(attrs authorizer.Attributes) (authorizer.Deci
|
|||
return r.authorizeGet(nodeName, vaVertexType, attrs)
|
||||
}
|
||||
return authorizer.DecisionNoOpinion, fmt.Sprintf("disabled by feature gate %s", features.CSIPersistentVolume), nil
|
||||
case svcAcctResource:
|
||||
if r.features.Enabled(features.TokenRequest) {
|
||||
return r.authorizeCreateToken(nodeName, serviceAccountVertexType, attrs)
|
||||
}
|
||||
return authorizer.DecisionNoOpinion, fmt.Sprintf("disabled by feature gate %s", features.TokenRequest), nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,6 +171,31 @@ func (r *NodeAuthorizer) authorize(nodeName string, startingType vertexType, att
|
|||
return authorizer.DecisionAllow, "", nil
|
||||
}
|
||||
|
||||
// authorizeCreateToken authorizes "create" requests to serviceaccounts 'token'
|
||||
// subresource of pods running on a node
|
||||
func (r *NodeAuthorizer) authorizeCreateToken(nodeName string, startingType vertexType, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
|
||||
if attrs.GetVerb() != "create" || len(attrs.GetName()) == 0 {
|
||||
glog.V(2).Infof("NODE DENY: %s %#v", nodeName, attrs)
|
||||
return authorizer.DecisionNoOpinion, "can only create tokens for individual service accounts", nil
|
||||
}
|
||||
|
||||
if attrs.GetSubresource() != "token" {
|
||||
glog.V(2).Infof("NODE DENY: %s %#v", nodeName, attrs)
|
||||
return authorizer.DecisionNoOpinion, "can only create token subresource of serviceaccount", nil
|
||||
}
|
||||
|
||||
ok, err := r.hasPathFrom(nodeName, startingType, attrs.GetNamespace(), attrs.GetName())
|
||||
if err != nil {
|
||||
glog.V(2).Infof("NODE DENY: %v", err)
|
||||
return authorizer.DecisionNoOpinion, "no path found to object", nil
|
||||
}
|
||||
if !ok {
|
||||
glog.V(2).Infof("NODE DENY: %q %#v", nodeName, attrs)
|
||||
return authorizer.DecisionNoOpinion, "no path found to object", nil
|
||||
}
|
||||
return authorizer.DecisionAllow, "", nil
|
||||
}
|
||||
|
||||
// hasPathFrom returns true if there is a directed path from the specified type/namespace/name to the specified Node
|
||||
func (r *NodeAuthorizer) hasPathFrom(nodeName string, startingType vertexType, startingNamespace, startingName string) (bool, error) {
|
||||
r.graph.lock.RLock()
|
||||
|
|
|
@ -38,6 +38,8 @@ import (
|
|||
var (
|
||||
csiEnabledFeature = utilfeature.NewFeatureGate()
|
||||
csiDisabledFeature = utilfeature.NewFeatureGate()
|
||||
trEnabledFeature = utilfeature.NewFeatureGate()
|
||||
trDisabledFeature = utilfeature.NewFeatureGate()
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -47,6 +49,12 @@ func init() {
|
|||
if err := csiDisabledFeature.Add(map[utilfeature.Feature]utilfeature.FeatureSpec{features.CSIPersistentVolume: {Default: false}}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := trEnabledFeature.Add(map[utilfeature.Feature]utilfeature.FeatureSpec{features.TokenRequest: {Default: true}}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := trDisabledFeature.Add(map[utilfeature.Feature]utilfeature.FeatureSpec{features.TokenRequest: {Default: false}}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthorizer(t *testing.T) {
|
||||
|
@ -152,6 +160,36 @@ func TestAuthorizer(t *testing.T) {
|
|||
features: csiEnabledFeature,
|
||||
expect: authorizer.DecisionAllow,
|
||||
},
|
||||
{
|
||||
name: "allowed svcacct token create - feature enabled",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "serviceaccounts", Subresource: "token", Name: "svcacct0-node0", Namespace: "ns0"},
|
||||
features: trEnabledFeature,
|
||||
expect: authorizer.DecisionAllow,
|
||||
},
|
||||
{
|
||||
name: "disallowed svcacct token create - serviceaccount not attached to node",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "serviceaccounts", Subresource: "token", Name: "svcacct0-node1", Namespace: "ns0"},
|
||||
features: trEnabledFeature,
|
||||
expect: authorizer.DecisionNoOpinion,
|
||||
},
|
||||
{
|
||||
name: "disallowed svcacct token create - feature disabled",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "serviceaccounts", Subresource: "token", Name: "svcacct0-node0", Namespace: "ns0"},
|
||||
features: trDisabledFeature,
|
||||
expect: authorizer.DecisionNoOpinion,
|
||||
},
|
||||
{
|
||||
name: "disallowed svcacct token create - no subresource",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "serviceaccounts", Name: "svcacct0-node0", Namespace: "ns0"},
|
||||
features: trEnabledFeature,
|
||||
expect: authorizer.DecisionNoOpinion,
|
||||
},
|
||||
{
|
||||
name: "disallowed svcacct token create - non create",
|
||||
attrs: authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "update", Resource: "serviceaccounts", Subresource: "token", Name: "svcacct0-node0", Namespace: "ns0"},
|
||||
features: trEnabledFeature,
|
||||
expect: authorizer.DecisionNoOpinion,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
|
@ -459,6 +497,7 @@ func generate(opts sampleDataOpts) ([]*api.Pod, []*api.PersistentVolume, []*stor
|
|||
pod.Namespace = fmt.Sprintf("ns%d", p%opts.namespaces)
|
||||
pod.Name = fmt.Sprintf("pod%d-%s", p, nodeName)
|
||||
pod.Spec.NodeName = nodeName
|
||||
pod.Spec.ServiceAccountName = fmt.Sprintf("svcacct%d-%s", p, nodeName)
|
||||
|
||||
for i := 0; i < opts.uniqueSecretsPerPod; i++ {
|
||||
pod.Spec.Volumes = append(pod.Spec.Volumes, api.Volume{VolumeSource: api.VolumeSource{
|
||||
|
|
Loading…
Reference in New Issue