简单介绍
旧的nodeAuthorizer不限制node对node/pod资源的list/watch/get操作,也就是可以拿到所有资源
开启AuthorizeNodeWithSelectors特性后限制node只能获取自身node以及属于自己node的pod
noderestriction限制create/delete等修改操作
AuthorizeNodeWithSelectors的nodeAuthorizer限制get/list/watch等读操作
源码
nodeAuthorize
pkg/kubeapiserver/authorizer/config.go中
创建authorizer
func (config Config) New(ctx context.Context, serverID string) (authorizer.Authorizer, authorizer.RuleResolver, error) {
...
创建node authorizer
r.nodeAuthorizer = node.NewAuthorizer(graph, nodeidentifier.NewDefaultNodeIdentifier(), bootstrappolicy.NodeRules())
...
}
plugin/pkg/auth/authorizer/node/node_authorizer.go中
创建node authorizer
func NewAuthorizer(graph *Graph, identifier nodeidentifier.NodeIdentifier, rules []rbacv1.PolicyRule) *NodeAuthorizer {
return &NodeAuthorizer{
graph: graph,
identifier: identifier,
nodeRules: rules,
features: utilfeature.DefaultFeatureGate,
}
}
权限校验
func (r *NodeAuthorizer) Authorize(ctx context.Context, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
...
判断是否是资源请求
if attrs.IsResourceRequest() {
requestResource := schema.GroupResource{Group: attrs.GetAPIGroup(), Resource: attrs.GetResource()}
switch requestResource {
...
如果是node/pod资源请求且开启了AuthorizeNodeWithSelectors特性则执行新的权限校验方法
case nodeResource:
if r.features.Enabled(features.AuthorizeNodeWithSelectors) {
return r.authorizeNode(nodeName, attrs)
}
case podResource:
if r.features.Enabled(features.AuthorizeNodeWithSelectors) {
return r.authorizePod(nodeName, attrs)
}
}
...
非资源请求或者未开启AuthorizeNodeWithSelectors特性则执行旧的权限校验方法
if rbac.RulesAllow(attrs, r.nodeRules...) {
return authorizer.DecisionAllow, "", nil
}
return authorizer.DecisionNoOpinion, "", nil
}
node权限校验
func (r *NodeAuthorizer) authorizeNode(nodeName string, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
...
switch attrs.GetSubresource() {
create/update/patch则允许
case "create", "update", "patch":
return authorizer.DecisionAllow, "", nil
case "get", "list", "watch":
如果目标node是自身则允许
switch attrs.GetName() {
case nodeName:
return authorizer.DecisionAllow, "", nil
}
pod权限校验
func (r *NodeAuthorizer) authorizePod(nodeName string, attrs authorizer.Attributes) (authorizer.Decision, string, error) {
...
switch attrs.GetSubresource() {
case "":
switch attrs.GetVerb() {
case "get":
return r.authorizeGet(nodeName, podVertexType, attrs)
case "list", "watch":
运行目标spec.nodeName为自身的pod
reqs, _ := attrs.GetFieldSelector()
for _, req := range reqs {
if req.Field == "spec.nodeName" && req.Operator == selection.Equals && req.Value == nodeName {
return authorizer.DecisionAllow, "", nil
}
}
检查目标pod是否属于当前自身node
if attrs.GetName() != "" {
return r.authorize(nodeName, podVertexType, attrs)
}
...
}
plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go
var(
...
读请求
Read = []string{"get", "list", "watch"}
...
)
策略规则
func NodeRules() []rbacv1.PolicyRule {
nodePolicyRules := []rbacv1.PolicyRule{
...
可以看到没有判断访问的node是否是自身node
rbacv1helpers.NewRule("create", "get", "list", "watch").Groups(legacyGroup).Resources("nodes").RuleOrDie(),
...
可以看到没有判断访问的pod是否是属于自身node
rbacv1helpers.NewRule(Read...).Groups(legacyGroup).Resources("pods").RuleOrDie(),
...
}
return nodePolicyRules
}
noderestriction
plugin/pkg/admission/noderestriction/admission.go中
准入控制
func (p *Plugin) Admit(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
...
判断请求的资源
switch a.GetResource().GroupResource() {
case podResource:
switch a.GetSubresource() {
case "":
检查是否可以操作pod
return p.admitPod(nodeName, a)
...
case nodeResource:
检查是否可以操作node
return p.admitNode(nodeName, a)
...
}
pod准入检查
func (p *Plugin) admitPod(nodeName string, a admission.Attributes) error {
switch a.GetOperation() {
case admission.Create:
pod创建准入检查
return p.admitPodCreate(nodeName, a)
case admission.Delete:
待删除的pod是否是自身node
existingPod, err := p.podsGetter.Pods(a.GetNamespace()).Get(a.GetName())
if apierrors.IsNotFound(err) {
return err
}
if err != nil {
return admission.NewForbidden(a, err)
}
if existingPod.Spec.NodeName != nodeName {
return admission.NewForbidden(a, fmt.Errorf("node %q can only delete pods with spec.nodeName set to itself", nodeName))
}
return nil
}
pod创建准入检查
func (p *Plugin) admitPodCreate(nodeName string, a admission.Attributes) error {
...
pod所属node为自身node
if pod.Spec.NodeName != nodeName {
return admission.NewForbidden(a, fmt.Errorf("node %q can only create pods with spec.nodeName set to itself", nodeName))
}
...
pod的OwnerReferences为自身node
if len(pod.OwnerReferences) == 1 {
owner := pod.OwnerReferences[0]
if owner.APIVersion != v1.SchemeGroupVersion.String() ||
owner.Kind != "Node" ||
owner.Name != nodeName {
return admission.NewForbidden(a, fmt.Errorf("node %q can only create pods with an owner reference set to itself", nodeName))
}
if owner.Controller == nil || !*owner.Controller {
return admission.NewForbidden(a, fmt.Errorf("node %q can only create pods with a controller owner reference set to itself", nodeName))
}
if owner.BlockOwnerDeletion != nil && *owner.BlockOwnerDeletion {
return admission.NewForbidden(a, fmt.Errorf("node %q must not set blockOwnerDeletion on an owner reference", nodeName))
}
// Verify the node UID.
node, err := p.nodesGetter.Get(nodeName)
if apierrors.IsNotFound(err) {
return err
}
if err != nil {
return admission.NewForbidden(a, fmt.Errorf("error looking up node %s to verify uid: %v", nodeName, err))
}
if owner.UID != node.UID {
return admission.NewForbidden(a, fmt.Errorf("node %s UID mismatch: expected %s got %s", nodeName, owner.UID, node.UID))
}
}
...
}
node准入检查
func (p *Plugin) admitNode(nodeName string, a admission.Attributes) error {
...
目标node是否是自身node
if requestedName != nodeName {
return admission.NewForbidden(a, fmt.Errorf("node %q is not allowed to modify node %q", nodeName, requestedName))
}
...
}
http handler
authorize
staging/src/k8s.io/apiserver/pkg/server/config.go
构建http handler chain
func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
...
添加authorization middleware
handler = genericapifilters.WithAuthorization(handler, c.Authorization.Authorizer, c.Serializer)
...
}
staging/src/k8s.io/apiserver/pkg/endpoints/filters/authorization.go中
添加authorization middleware
func WithAuthorization(hhandler http.Handler, auth authorizer.Authorizer, s runtime.NegotiatedSerializer) http.Handler {
return withAuthorization(hhandler, auth, s, recordAuthorizationMetrics)
}
添加authorization middleware
func withAuthorization(handler http.Handler, a authorizer.Authorizer, s runtime.NegotiatedSerializer, metrics recordAuthorizationMetricsFunc) http.Handler {
...
权限校验
authorized, reason, err := a.Authorize(ctx, attributes)
...
allow则进入下一级处理
if authorized == authorizer.DecisionAllow {
...
handler.ServeHTTP(w, req)
return
}
报错则返回500
if err != nil {
...
responsewriters.InternalError(w, req, err)
return
}
...
非allow(DecisionNoOpinion,DecisionDeny)则返回403
responsewriters.Forbidden(ctx, attributes, w, req, reason, s)
}
添加伪装身份检查的middleware
func WithImpersonation(handler http.Handler, a authorizer.Authorizer, s runtime.NegotiatedSerializer) http.Handler {
...
权限校验
decision, reason, err := a.Authorize(ctx, actingAsAttributes)
报错或者非allow(DecisionNoOpinion,DecisionDeny)则返回403
if err != nil || decision != authorizer.DecisionAllow {
...
responsewriters.Forbidden(ctx, actingAsAttributes, w, req, reason, s)
return
}
...
}
admit
注册资源处理方法
func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storage, ws restful.WebService) (metav1.APIResource, *storageversion.ResourceInfo, error) {
...
var handler restful.RouteFunction
注册资源创建处理方法
if isNamedCreater {
handler = restfulCreateNamedResource(namedCreater, reqScope, admit)
} else {
handler = restfulCreateResource(creater, reqScope, admit)
}
...
}
staging/src/k8s.io/apiserver/pkg/endpoints/installer.go中
rest创建资源处理方法
func restfulCreateResource(r rest.Creater, scope handlers.RequestScope, admit admission.Interface) restful.RouteFunction {
return func(req *restful.Request, res *restful.Response) {
handlers.CreateResource(r, &scope, admit)(res.ResponseWriter, req.Request)
}
}
staging/src/k8s.io/apiserver/pkg/endpoints/handlers/create.go中
创建方法
func CreateResource(r rest.Creater, scope *RequestScope, admission admission.Interface) http.HandlerFunc {
return createHandler(&namedCreaterAdapter{r}, scope, admission, false)
}
创建方法
func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Interface, includeName bool) http.HandlerFunc {
...
构建请求方法
requestFunc := func() (runtime.Object, error) {
return r.Create(
ctx,
name,
obj,
rest.AdmissionToValidateObjectFunc(admit, admissionAttributes, scope),
options,
)
}
...
执行请求方法
result, err := requestFunc()
...
}
staging/src/k8s.io/apiserver/pkg/registry/rest/create.go中
admission校验
func AdmissionToValidateObjectFunc(admit admission.Interface, staticAttributes admission.Attributes, o admission.ObjectInterfaces) ValidateObjectFunc {
...
finalAttributes := admission.NewAttributesRecord(
obj,
staticAttributes.GetOldObject(),
staticAttributes.GetKind(),
staticAttributes.GetNamespace(),
name,
staticAttributes.GetResource(),
staticAttributes.GetSubresource(),
staticAttributes.GetOperation(),
staticAttributes.GetOperationOptions(),
staticAttributes.IsDryRun(),
staticAttributes.GetUserInfo(),
)
if !validatingAdmission.Handles(finalAttributes.GetOperation()) {
return nil
}
return validatingAdmission.Validate(ctx, finalAttributes, o)
...
}