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  }