k8s.io/apiserver@v0.31.1/pkg/authentication/request/headerrequest/requestheader.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 headerrequest
    18  
    19  import (
    20  	"fmt"
    21  	"net/http"
    22  	"net/url"
    23  	"strings"
    24  
    25  	"k8s.io/apiserver/pkg/authentication/authenticator"
    26  	x509request "k8s.io/apiserver/pkg/authentication/request/x509"
    27  	"k8s.io/apiserver/pkg/authentication/user"
    28  )
    29  
    30  // StringSliceProvider is a way to get a string slice value.  It is heavily used for authentication headers among other places.
    31  type StringSliceProvider interface {
    32  	// Value returns the current string slice.  Callers should never mutate the returned value.
    33  	Value() []string
    34  }
    35  
    36  // StringSliceProviderFunc is a function that matches the StringSliceProvider interface
    37  type StringSliceProviderFunc func() []string
    38  
    39  // Value returns the current string slice.  Callers should never mutate the returned value.
    40  func (d StringSliceProviderFunc) Value() []string {
    41  	return d()
    42  }
    43  
    44  // StaticStringSlice a StringSliceProvider that returns a fixed value
    45  type StaticStringSlice []string
    46  
    47  // Value returns the current string slice.  Callers should never mutate the returned value.
    48  func (s StaticStringSlice) Value() []string {
    49  	return s
    50  }
    51  
    52  type requestHeaderAuthRequestHandler struct {
    53  	// nameHeaders are the headers to check (in order, case-insensitively) for an identity. The first header with a value wins.
    54  	nameHeaders StringSliceProvider
    55  
    56  	// groupHeaders are the headers to check (case-insensitively) for group membership.  All values of all headers will be added.
    57  	groupHeaders StringSliceProvider
    58  
    59  	// extraHeaderPrefixes are the head prefixes to check (case-insensitively) for filling in
    60  	// the user.Info.Extra.  All values of all matching headers will be added.
    61  	extraHeaderPrefixes StringSliceProvider
    62  }
    63  
    64  func New(nameHeaders, groupHeaders, extraHeaderPrefixes []string) (authenticator.Request, error) {
    65  	trimmedNameHeaders, err := trimHeaders(nameHeaders...)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	trimmedGroupHeaders, err := trimHeaders(groupHeaders...)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	trimmedExtraHeaderPrefixes, err := trimHeaders(extraHeaderPrefixes...)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	return NewDynamic(
    79  		StaticStringSlice(trimmedNameHeaders),
    80  		StaticStringSlice(trimmedGroupHeaders),
    81  		StaticStringSlice(trimmedExtraHeaderPrefixes),
    82  	), nil
    83  }
    84  
    85  func NewDynamic(nameHeaders, groupHeaders, extraHeaderPrefixes StringSliceProvider) authenticator.Request {
    86  	return &requestHeaderAuthRequestHandler{
    87  		nameHeaders:         nameHeaders,
    88  		groupHeaders:        groupHeaders,
    89  		extraHeaderPrefixes: extraHeaderPrefixes,
    90  	}
    91  }
    92  
    93  func trimHeaders(headerNames ...string) ([]string, error) {
    94  	ret := []string{}
    95  	for _, headerName := range headerNames {
    96  		trimmedHeader := strings.TrimSpace(headerName)
    97  		if len(trimmedHeader) == 0 {
    98  			return nil, fmt.Errorf("empty header %q", headerName)
    99  		}
   100  		ret = append(ret, trimmedHeader)
   101  	}
   102  
   103  	return ret, nil
   104  }
   105  
   106  func NewDynamicVerifyOptionsSecure(verifyOptionFn x509request.VerifyOptionFunc, proxyClientNames, nameHeaders, groupHeaders, extraHeaderPrefixes StringSliceProvider) authenticator.Request {
   107  	headerAuthenticator := NewDynamic(nameHeaders, groupHeaders, extraHeaderPrefixes)
   108  
   109  	return x509request.NewDynamicCAVerifier(verifyOptionFn, headerAuthenticator, proxyClientNames)
   110  }
   111  
   112  func (a *requestHeaderAuthRequestHandler) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {
   113  	name := headerValue(req.Header, a.nameHeaders.Value())
   114  	if len(name) == 0 {
   115  		return nil, false, nil
   116  	}
   117  	groups := allHeaderValues(req.Header, a.groupHeaders.Value())
   118  	extra := newExtra(req.Header, a.extraHeaderPrefixes.Value())
   119  
   120  	// clear headers used for authentication
   121  	ClearAuthenticationHeaders(req.Header, a.nameHeaders, a.groupHeaders, a.extraHeaderPrefixes)
   122  
   123  	return &authenticator.Response{
   124  		User: &user.DefaultInfo{
   125  			Name:   name,
   126  			Groups: groups,
   127  			Extra:  extra,
   128  		},
   129  	}, true, nil
   130  }
   131  
   132  func ClearAuthenticationHeaders(h http.Header, nameHeaders, groupHeaders, extraHeaderPrefixes StringSliceProvider) {
   133  	for _, headerName := range nameHeaders.Value() {
   134  		h.Del(headerName)
   135  	}
   136  	for _, headerName := range groupHeaders.Value() {
   137  		h.Del(headerName)
   138  	}
   139  	for _, prefix := range extraHeaderPrefixes.Value() {
   140  		for k := range h {
   141  			if hasPrefixIgnoreCase(k, prefix) {
   142  				delete(h, k) // we have the raw key so avoid relying on canonicalization
   143  			}
   144  		}
   145  	}
   146  }
   147  
   148  func hasPrefixIgnoreCase(s, prefix string) bool {
   149  	return len(s) >= len(prefix) && strings.EqualFold(s[:len(prefix)], prefix)
   150  }
   151  
   152  func headerValue(h http.Header, headerNames []string) string {
   153  	for _, headerName := range headerNames {
   154  		headerValue := h.Get(headerName)
   155  		if len(headerValue) > 0 {
   156  			return headerValue
   157  		}
   158  	}
   159  	return ""
   160  }
   161  
   162  func allHeaderValues(h http.Header, headerNames []string) []string {
   163  	ret := []string{}
   164  	for _, headerName := range headerNames {
   165  		headerKey := http.CanonicalHeaderKey(headerName)
   166  		values, ok := h[headerKey]
   167  		if !ok {
   168  			continue
   169  		}
   170  
   171  		for _, headerValue := range values {
   172  			if len(headerValue) > 0 {
   173  				ret = append(ret, headerValue)
   174  			}
   175  		}
   176  	}
   177  	return ret
   178  }
   179  
   180  func unescapeExtraKey(encodedKey string) string {
   181  	key, err := url.PathUnescape(encodedKey) // Decode %-encoded bytes.
   182  	if err != nil {
   183  		return encodedKey // Always record extra strings, even if malformed/unencoded.
   184  	}
   185  	return key
   186  }
   187  
   188  func newExtra(h http.Header, headerPrefixes []string) map[string][]string {
   189  	ret := map[string][]string{}
   190  
   191  	// we have to iterate over prefixes first in order to have proper ordering inside the value slices
   192  	for _, prefix := range headerPrefixes {
   193  		for headerName, vv := range h {
   194  			if !hasPrefixIgnoreCase(headerName, prefix) {
   195  				continue
   196  			}
   197  
   198  			extraKey := unescapeExtraKey(strings.ToLower(headerName[len(prefix):]))
   199  			ret[extraKey] = append(ret[extraKey], vv...)
   200  		}
   201  	}
   202  
   203  	return ret
   204  }