k8s.io/apiserver@v0.31.1/pkg/endpoints/filters/authorization.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package filters 18 19 import ( 20 "context" 21 "errors" 22 "net/http" 23 "time" 24 25 "k8s.io/apimachinery/pkg/fields" 26 "k8s.io/apimachinery/pkg/labels" 27 genericfeatures "k8s.io/apiserver/pkg/features" 28 utilfeature "k8s.io/apiserver/pkg/util/feature" 29 30 "k8s.io/klog/v2" 31 32 "k8s.io/apimachinery/pkg/runtime" 33 "k8s.io/apiserver/pkg/audit" 34 "k8s.io/apiserver/pkg/authorization/authorizer" 35 "k8s.io/apiserver/pkg/endpoints/handlers/responsewriters" 36 "k8s.io/apiserver/pkg/endpoints/request" 37 ) 38 39 const ( 40 // Annotation key names set in advanced audit 41 decisionAnnotationKey = "authorization.k8s.io/decision" 42 reasonAnnotationKey = "authorization.k8s.io/reason" 43 44 // Annotation values set in advanced audit 45 decisionAllow = "allow" 46 decisionForbid = "forbid" 47 reasonError = "internal error" 48 ) 49 50 type recordAuthorizationMetricsFunc func(ctx context.Context, authorized authorizer.Decision, err error, authStart time.Time, authFinish time.Time) 51 52 // WithAuthorization passes all authorized requests on to handler, and returns a forbidden error otherwise. 53 func WithAuthorization(hhandler http.Handler, auth authorizer.Authorizer, s runtime.NegotiatedSerializer) http.Handler { 54 return withAuthorization(hhandler, auth, s, recordAuthorizationMetrics) 55 } 56 57 func withAuthorization(handler http.Handler, a authorizer.Authorizer, s runtime.NegotiatedSerializer, metrics recordAuthorizationMetricsFunc) http.Handler { 58 if a == nil { 59 klog.Warning("Authorization is disabled") 60 return handler 61 } 62 return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 63 ctx := req.Context() 64 authorizationStart := time.Now() 65 66 attributes, err := GetAuthorizerAttributes(ctx) 67 if err != nil { 68 responsewriters.InternalError(w, req, err) 69 return 70 } 71 authorized, reason, err := a.Authorize(ctx, attributes) 72 73 authorizationFinish := time.Now() 74 defer func() { 75 metrics(ctx, authorized, err, authorizationStart, authorizationFinish) 76 }() 77 78 // an authorizer like RBAC could encounter evaluation errors and still allow the request, so authorizer decision is checked before error here. 79 if authorized == authorizer.DecisionAllow { 80 audit.AddAuditAnnotations(ctx, 81 decisionAnnotationKey, decisionAllow, 82 reasonAnnotationKey, reason) 83 handler.ServeHTTP(w, req) 84 return 85 } 86 if err != nil { 87 audit.AddAuditAnnotation(ctx, reasonAnnotationKey, reasonError) 88 responsewriters.InternalError(w, req, err) 89 return 90 } 91 92 klog.V(4).InfoS("Forbidden", "URI", req.RequestURI, "reason", reason) 93 audit.AddAuditAnnotations(ctx, 94 decisionAnnotationKey, decisionForbid, 95 reasonAnnotationKey, reason) 96 responsewriters.Forbidden(ctx, attributes, w, req, reason, s) 97 }) 98 } 99 100 func GetAuthorizerAttributes(ctx context.Context) (authorizer.Attributes, error) { 101 attribs := authorizer.AttributesRecord{} 102 103 user, ok := request.UserFrom(ctx) 104 if ok { 105 attribs.User = user 106 } 107 108 requestInfo, found := request.RequestInfoFrom(ctx) 109 if !found { 110 return nil, errors.New("no RequestInfo found in the context") 111 } 112 113 // Start with common attributes that apply to resource and non-resource requests 114 attribs.ResourceRequest = requestInfo.IsResourceRequest 115 attribs.Path = requestInfo.Path 116 attribs.Verb = requestInfo.Verb 117 118 attribs.APIGroup = requestInfo.APIGroup 119 attribs.APIVersion = requestInfo.APIVersion 120 attribs.Resource = requestInfo.Resource 121 attribs.Subresource = requestInfo.Subresource 122 attribs.Namespace = requestInfo.Namespace 123 attribs.Name = requestInfo.Name 124 125 if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.AuthorizeWithSelectors) { 126 // parsing here makes it easy to keep the AttributesRecord type value-only and avoids any mutex copies when 127 // doing shallow copies in other steps. 128 if len(requestInfo.FieldSelector) > 0 { 129 fieldSelector, err := fields.ParseSelector(requestInfo.FieldSelector) 130 if err != nil { 131 attribs.FieldSelectorRequirements, attribs.FieldSelectorParsingErr = nil, err 132 } else { 133 if requirements := fieldSelector.Requirements(); len(requirements) > 0 { 134 attribs.FieldSelectorRequirements, attribs.FieldSelectorParsingErr = fieldSelector.Requirements(), nil 135 } 136 } 137 } 138 139 if len(requestInfo.LabelSelector) > 0 { 140 labelSelector, err := labels.Parse(requestInfo.LabelSelector) 141 if err != nil { 142 attribs.LabelSelectorRequirements, attribs.LabelSelectorParsingErr = nil, err 143 } else { 144 if requirements, _ /*selectable*/ := labelSelector.Requirements(); len(requirements) > 0 { 145 attribs.LabelSelectorRequirements, attribs.LabelSelectorParsingErr = requirements, nil 146 } 147 } 148 } 149 } 150 151 return &attribs, nil 152 }