2017-05-30 15:15:38 -04:00
/ *
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 node
import (
"fmt"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/kubernetes/pkg/api"
rbacapi "k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/auth/nodeidentifier"
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac"
"k8s.io/kubernetes/third_party/forked/gonum/graph"
"k8s.io/kubernetes/third_party/forked/gonum/graph/traverse"
)
// NodeAuthorizer authorizes requests from kubelets, with the following logic:
2017-08-26 15:56:33 -04:00
// 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
2017-05-30 15:15:38 -04:00
// 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:
// node <- pod
// node <- pod <- secret
// node <- pod <- configmap
// node <- pod <- pvc
// node <- pod <- pvc <- pv
// node <- pod <- pvc <- pv <- secret
// 4. For other resources, authorize all nodes uniformly using statically defined rules
type NodeAuthorizer struct {
graph * Graph
identifier nodeidentifier . NodeIdentifier
nodeRules [ ] rbacapi . PolicyRule
}
2017-06-23 05:53:35 -04:00
// NewAuthorizer returns a new node authorizer
2017-05-30 15:15:38 -04:00
func NewAuthorizer ( graph * Graph , identifier nodeidentifier . NodeIdentifier , rules [ ] rbacapi . PolicyRule ) authorizer . Authorizer {
return & NodeAuthorizer {
graph : graph ,
identifier : identifier ,
nodeRules : rules ,
}
}
var (
configMapResource = api . Resource ( "configmaps" )
secretResource = api . Resource ( "secrets" )
pvcResource = api . Resource ( "persistentvolumeclaims" )
pvResource = api . Resource ( "persistentvolumes" )
)
func ( r * NodeAuthorizer ) Authorize ( attrs authorizer . Attributes ) ( bool , string , error ) {
nodeName , isNode := r . identifier . NodeIdentity ( attrs . GetUser ( ) )
if ! isNode {
// reject requests from non-nodes
return false , "" , nil
}
if len ( nodeName ) == 0 {
// reject requests from unidentifiable nodes
glog . V ( 2 ) . Infof ( "NODE DENY: unknown node for user %q" , attrs . GetUser ( ) . GetName ( ) )
return false , fmt . Sprintf ( "unknown node for user %q" , attrs . GetUser ( ) . GetName ( ) ) , nil
}
// subdivide access to specific resources
if attrs . IsResourceRequest ( ) {
requestResource := schema . GroupResource { Group : attrs . GetAPIGroup ( ) , Resource : attrs . GetResource ( ) }
switch requestResource {
case secretResource :
return r . authorizeGet ( nodeName , secretVertexType , attrs )
case configMapResource :
return r . authorizeGet ( nodeName , configMapVertexType , attrs )
case pvcResource :
return r . authorizeGet ( nodeName , pvcVertexType , attrs )
case pvResource :
return r . authorizeGet ( nodeName , pvVertexType , attrs )
}
}
// Access to other resources is not subdivided, so just evaluate against the statically defined node rules
return rbac . RulesAllow ( attrs , r . nodeRules ... ) , "" , nil
}
// authorizeGet authorizes "get" requests to objects of the specified type if they are related to the specified node
func ( r * NodeAuthorizer ) authorizeGet ( nodeName string , startingType vertexType , attrs authorizer . Attributes ) ( bool , string , error ) {
if attrs . GetVerb ( ) != "get" || len ( attrs . GetName ( ) ) == 0 {
glog . V ( 2 ) . Infof ( "NODE DENY: %s %#v" , nodeName , attrs )
return false , "can only get individual resources of this type" , nil
}
if len ( attrs . GetSubresource ( ) ) > 0 {
glog . V ( 2 ) . Infof ( "NODE DENY: %s %#v" , nodeName , attrs )
return false , "cannot get subresource" , nil
}
ok , err := r . hasPathFrom ( nodeName , startingType , attrs . GetNamespace ( ) , attrs . GetName ( ) )
if err != nil {
glog . V ( 2 ) . Infof ( "NODE DENY: %v" , err )
return false , "no path found to object" , nil
}
if ! ok {
2017-07-10 22:40:40 -04:00
glog . V ( 2 ) . Infof ( "NODE DENY: %q %#v" , nodeName , attrs )
2017-05-30 15:15:38 -04:00
return false , "no path found to object" , nil
}
return ok , "" , 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 ( )
defer r . graph . lock . RUnlock ( )
nodeVertex , exists := r . graph . getVertex_rlocked ( nodeVertexType , "" , nodeName )
if ! exists {
2017-07-10 22:40:40 -04:00
return false , fmt . Errorf ( "unknown node %q cannot get %s %s/%s" , nodeName , vertexTypes [ startingType ] , startingNamespace , startingName )
2017-05-30 15:15:38 -04:00
}
startingVertex , exists := r . graph . getVertex_rlocked ( startingType , startingNamespace , startingName )
if ! exists {
2017-07-10 22:40:40 -04:00
return false , fmt . Errorf ( "node %q cannot get unknown %s %s/%s" , nodeName , vertexTypes [ startingType ] , startingNamespace , startingName )
2017-05-30 15:15:38 -04:00
}
found := false
traversal := & traverse . VisitingDepthFirst {
EdgeFilter : func ( edge graph . Edge ) bool {
if destinationEdge , ok := edge . ( * destinationEdge ) ; ok {
if destinationEdge . DestinationID ( ) != nodeVertex . ID ( ) {
// Don't follow edges leading to other nodes
return false
}
// We found an edge leading to the node we want
found = true
}
// Visit this edge
return true
} ,
}
traversal . Walk ( r . graph . graph , startingVertex , func ( n graph . Node ) bool {
if n . ID ( ) == nodeVertex . ID ( ) {
// We found the node we want
found = true
}
// Stop visiting if we've found the node we want
return found
} )
if ! found {
2017-07-10 22:40:40 -04:00
return false , fmt . Errorf ( "node %q cannot get %s %s/%s, no path was found" , nodeName , vertexTypes [ startingType ] , startingNamespace , startingName )
2017-05-30 15:15:38 -04:00
}
return true , nil
}