簡單介紹
舊的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中
創(chuàng)建authorizer
func (config Config) New(ctx context.Context, serverID string) (authorizer.Authorizer, authorizer.RuleResolver, error) {
...
創(chuàng)建node authorizer
r.nodeAuthorizer = node.NewAuthorizer(graph, nodeidentifier.NewDefaultNodeIdentifier(), bootstrappolicy.NodeRules())
...
}
plugin/pkg/auth/authorizer/node/node_authorizer.go中
創(chuàng)建node authorizer
func NewAuthorizer(graph *Graph, identifier nodeidentifier.NodeIdentifier, rules []rbacv1.PolicyRule) *NodeAuthorizer {
return &NodeAuthorizer{
graph: graph,
identifier: identifier,
nodeRules: rules,
features: utilfeature.DefaultFeatureGate,
}
}
權(quán)限校驗(yàn)
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特性則執(zhí)行新的權(quán)限校驗(yàn)方法
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特性則執(zhí)行舊的權(quán)限校驗(yàn)方法
if rbac.RulesAllow(attrs, r.nodeRules...) {
return authorizer.DecisionAllow, "", nil
}
return authorizer.DecisionNoOpinion, "", nil
}
node權(quán)限校驗(yàn)
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":
如果目標(biāo)node是自身則允許
switch attrs.GetName() {
case nodeName:
return authorizer.DecisionAllow, "", nil
}
pod權(quán)限校驗(yàn)
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":
運(yùn)行目標(biāo)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
}
}
檢查目標(biāo)pod是否屬于當(dāng)前自身node
if attrs.GetName() != "" {
return r.authorize(nodeName, podVertexType, attrs)
}
...
}
plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go
var(
...
讀請求
Read = []string{"get", "list", "watch"}
...
)
策略規(guī)則
func NodeRules() []rbacv1.PolicyRule {
nodePolicyRules := []rbacv1.PolicyRule{
...
可以看到?jīng)]有判斷訪問的node是否是自身node
rbacv1helpers.NewRule("create", "get", "list", "watch").Groups(legacyGroup).Resources("nodes").RuleOrDie(),
...
可以看到?jīng)]有判斷訪問的pod是否是屬于自身node
rbacv1helpers.NewRule(Read...).Groups(legacyGroup).Resources("pods").RuleOrDie(),
...
}
return nodePolicyRules
}
noderestriction
plugin/pkg/admission/noderestriction/admission.go中
準(zhǔn)入控制
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準(zhǔn)入檢查
func (p *Plugin) admitPod(nodeName string, a admission.Attributes) error {
switch a.GetOperation() {
case admission.Create:
pod創(chuàng)建準(zhǔn)入檢查
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創(chuàng)建準(zhǔn)入檢查
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準(zhǔn)入檢查
func (p *Plugin) admitNode(nodeName string, a admission.Attributes) error {
...
目標(biāo)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
構(gòu)建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 {
...
權(quán)限校驗(yàn)
authorized, reason, err := a.Authorize(ctx, attributes)
...
allow則進(jìn)入下一級(jí)處理
if authorized == authorizer.DecisionAllow {
...
handler.ServeHTTP(w, req)
return
}
報(bào)錯(cuò)則返回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 {
...
權(quán)限校驗(yàn)
decision, reason, err := a.Authorize(ctx, actingAsAttributes)
報(bào)錯(cuò)或者非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
注冊資源創(chuàng)建處理方法
if isNamedCreater {
handler = restfulCreateNamedResource(namedCreater, reqScope, admit)
} else {
handler = restfulCreateResource(creater, reqScope, admit)
}
...
}
staging/src/k8s.io/apiserver/pkg/endpoints/installer.go中
rest創(chuàng)建資源處理方法
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中
創(chuàng)建方法
func CreateResource(r rest.Creater, scope *RequestScope, admission admission.Interface) http.HandlerFunc {
return createHandler(&namedCreaterAdapter{r}, scope, admission, false)
}
創(chuàng)建方法
func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Interface, includeName bool) http.HandlerFunc {
...
構(gòu)建請求方法
requestFunc := func() (runtime.Object, error) {
return r.Create(
ctx,
name,
obj,
rest.AdmissionToValidateObjectFunc(admit, admissionAttributes, scope),
options,
)
}
...
執(zhí)行請求方法
result, err := requestFunc()
...
}
staging/src/k8s.io/apiserver/pkg/registry/rest/create.go中
admission校驗(yàn)
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)
...
}