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 }