mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 10:33:35 +00:00
rebase: update replaced k8s.io modules to v0.33.0
Signed-off-by: Niels de Vos <ndevos@ibm.com>
This commit is contained in:
committed by
mergify[bot]
parent
dd77e72800
commit
107407b44b
90
e2e/vendor/k8s.io/apiserver/pkg/authentication/authenticator/audagnostic.go
generated
vendored
90
e2e/vendor/k8s.io/apiserver/pkg/authentication/authenticator/audagnostic.go
generated
vendored
@ -1,90 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package authenticator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func authenticate(ctx context.Context, implicitAuds Audiences, authenticate func() (*Response, bool, error)) (*Response, bool, error) {
|
||||
targetAuds, ok := AudiencesFrom(ctx)
|
||||
// We can remove this once api audiences is never empty. That will probably
|
||||
// be N releases after TokenRequest is GA.
|
||||
if !ok {
|
||||
return authenticate()
|
||||
}
|
||||
auds := implicitAuds.Intersect(targetAuds)
|
||||
if len(auds) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
resp, ok, err := authenticate()
|
||||
if err != nil || !ok {
|
||||
return nil, false, err
|
||||
}
|
||||
if len(resp.Audiences) > 0 {
|
||||
// maybe the authenticator was audience aware after all.
|
||||
return nil, false, fmt.Errorf("audience agnostic authenticator wrapped an authenticator that returned audiences: %q", resp.Audiences)
|
||||
}
|
||||
resp.Audiences = auds
|
||||
return resp, true, nil
|
||||
}
|
||||
|
||||
type audAgnosticRequestAuthenticator struct {
|
||||
implicit Audiences
|
||||
delegate Request
|
||||
}
|
||||
|
||||
var _ = Request(&audAgnosticRequestAuthenticator{})
|
||||
|
||||
func (a *audAgnosticRequestAuthenticator) AuthenticateRequest(req *http.Request) (*Response, bool, error) {
|
||||
return authenticate(req.Context(), a.implicit, func() (*Response, bool, error) {
|
||||
return a.delegate.AuthenticateRequest(req)
|
||||
})
|
||||
}
|
||||
|
||||
// WrapAudienceAgnosticRequest wraps an audience agnostic request authenticator
|
||||
// to restrict its accepted audiences to a set of implicit audiences.
|
||||
func WrapAudienceAgnosticRequest(implicit Audiences, delegate Request) Request {
|
||||
return &audAgnosticRequestAuthenticator{
|
||||
implicit: implicit,
|
||||
delegate: delegate,
|
||||
}
|
||||
}
|
||||
|
||||
type audAgnosticTokenAuthenticator struct {
|
||||
implicit Audiences
|
||||
delegate Token
|
||||
}
|
||||
|
||||
var _ = Token(&audAgnosticTokenAuthenticator{})
|
||||
|
||||
func (a *audAgnosticTokenAuthenticator) AuthenticateToken(ctx context.Context, tok string) (*Response, bool, error) {
|
||||
return authenticate(ctx, a.implicit, func() (*Response, bool, error) {
|
||||
return a.delegate.AuthenticateToken(ctx, tok)
|
||||
})
|
||||
}
|
||||
|
||||
// WrapAudienceAgnosticToken wraps an audience agnostic token authenticator to
|
||||
// restrict its accepted audiences to a set of implicit audiences.
|
||||
func WrapAudienceAgnosticToken(implicit Audiences, delegate Token) Token {
|
||||
return &audAgnosticTokenAuthenticator{
|
||||
implicit: implicit,
|
||||
delegate: delegate,
|
||||
}
|
||||
}
|
63
e2e/vendor/k8s.io/apiserver/pkg/authentication/authenticator/audiences.go
generated
vendored
63
e2e/vendor/k8s.io/apiserver/pkg/authentication/authenticator/audiences.go
generated
vendored
@ -1,63 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package authenticator
|
||||
|
||||
import "context"
|
||||
|
||||
// Audiences is a container for the Audiences of a token.
|
||||
type Audiences []string
|
||||
|
||||
// The key type is unexported to prevent collisions
|
||||
type key int
|
||||
|
||||
const (
|
||||
// audiencesKey is the context key for request audiences.
|
||||
audiencesKey key = iota
|
||||
)
|
||||
|
||||
// WithAudiences returns a context that stores a request's expected audiences.
|
||||
func WithAudiences(ctx context.Context, auds Audiences) context.Context {
|
||||
return context.WithValue(ctx, audiencesKey, auds)
|
||||
}
|
||||
|
||||
// AudiencesFrom returns a request's expected audiences stored in the request context.
|
||||
func AudiencesFrom(ctx context.Context) (Audiences, bool) {
|
||||
auds, ok := ctx.Value(audiencesKey).(Audiences)
|
||||
return auds, ok
|
||||
}
|
||||
|
||||
// Has checks if Audiences contains a specific audiences.
|
||||
func (a Audiences) Has(taud string) bool {
|
||||
for _, aud := range a {
|
||||
if aud == taud {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Intersect intersects Audiences with a target Audiences and returns all
|
||||
// elements in both.
|
||||
func (a Audiences) Intersect(tauds Audiences) Audiences {
|
||||
selected := Audiences{}
|
||||
for _, taud := range tauds {
|
||||
if a.Has(taud) {
|
||||
selected = append(selected, taud)
|
||||
}
|
||||
}
|
||||
return selected
|
||||
}
|
65
e2e/vendor/k8s.io/apiserver/pkg/authentication/authenticator/interfaces.go
generated
vendored
65
e2e/vendor/k8s.io/apiserver/pkg/authentication/authenticator/interfaces.go
generated
vendored
@ -1,65 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package authenticator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
)
|
||||
|
||||
// Token checks a string value against a backing authentication store and
|
||||
// returns a Response or an error if the token could not be checked.
|
||||
type Token interface {
|
||||
AuthenticateToken(ctx context.Context, token string) (*Response, bool, error)
|
||||
}
|
||||
|
||||
// Request attempts to extract authentication information from a request and
|
||||
// returns a Response or an error if the request could not be checked.
|
||||
type Request interface {
|
||||
AuthenticateRequest(req *http.Request) (*Response, bool, error)
|
||||
}
|
||||
|
||||
// TokenFunc is a function that implements the Token interface.
|
||||
type TokenFunc func(ctx context.Context, token string) (*Response, bool, error)
|
||||
|
||||
// AuthenticateToken implements authenticator.Token.
|
||||
func (f TokenFunc) AuthenticateToken(ctx context.Context, token string) (*Response, bool, error) {
|
||||
return f(ctx, token)
|
||||
}
|
||||
|
||||
// RequestFunc is a function that implements the Request interface.
|
||||
type RequestFunc func(req *http.Request) (*Response, bool, error)
|
||||
|
||||
// AuthenticateRequest implements authenticator.Request.
|
||||
func (f RequestFunc) AuthenticateRequest(req *http.Request) (*Response, bool, error) {
|
||||
return f(req)
|
||||
}
|
||||
|
||||
// Response is the struct returned by authenticator interfaces upon successful
|
||||
// authentication. It contains information about whether the authenticator
|
||||
// authenticated the request, information about the context of the
|
||||
// authentication, and information about the authenticated user.
|
||||
type Response struct {
|
||||
// Audiences is the set of audiences the authenticator was able to validate
|
||||
// the token against. If the authenticator is not audience aware, this field
|
||||
// will be empty.
|
||||
Audiences Audiences
|
||||
// User is the UserInfo associated with the authentication context.
|
||||
User user.Info
|
||||
}
|
128
e2e/vendor/k8s.io/apiserver/pkg/authentication/authenticatorfactory/delegating.go
generated
vendored
128
e2e/vendor/k8s.io/apiserver/pkg/authentication/authenticatorfactory/delegating.go
generated
vendored
@ -1,128 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package authenticatorfactory
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apiserver/pkg/apis/apiserver"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/authentication/group"
|
||||
"k8s.io/apiserver/pkg/authentication/request/anonymous"
|
||||
"k8s.io/apiserver/pkg/authentication/request/bearertoken"
|
||||
"k8s.io/apiserver/pkg/authentication/request/headerrequest"
|
||||
unionauth "k8s.io/apiserver/pkg/authentication/request/union"
|
||||
"k8s.io/apiserver/pkg/authentication/request/websocket"
|
||||
"k8s.io/apiserver/pkg/authentication/request/x509"
|
||||
"k8s.io/apiserver/pkg/authentication/token/cache"
|
||||
"k8s.io/apiserver/pkg/server/dynamiccertificates"
|
||||
webhooktoken "k8s.io/apiserver/plugin/pkg/authenticator/token/webhook"
|
||||
authenticationclient "k8s.io/client-go/kubernetes/typed/authentication/v1"
|
||||
"k8s.io/kube-openapi/pkg/validation/spec"
|
||||
)
|
||||
|
||||
// DelegatingAuthenticatorConfig is the minimal configuration needed to create an authenticator
|
||||
// built to delegate authentication to a kube API server
|
||||
type DelegatingAuthenticatorConfig struct {
|
||||
Anonymous *apiserver.AnonymousAuthConfig
|
||||
|
||||
// TokenAccessReviewClient is a client to do token review. It can be nil. Then every token is ignored.
|
||||
TokenAccessReviewClient authenticationclient.AuthenticationV1Interface
|
||||
|
||||
// TokenAccessReviewTimeout specifies a time limit for requests made by the authorization webhook client.
|
||||
TokenAccessReviewTimeout time.Duration
|
||||
|
||||
// WebhookRetryBackoff specifies the backoff parameters for the authentication webhook retry logic.
|
||||
// This allows us to configure the sleep time at each iteration and the maximum number of retries allowed
|
||||
// before we fail the webhook call in order to limit the fan out that ensues when the system is degraded.
|
||||
WebhookRetryBackoff *wait.Backoff
|
||||
|
||||
// CacheTTL is the length of time that a token authentication answer will be cached.
|
||||
CacheTTL time.Duration
|
||||
|
||||
// CAContentProvider are the options for verifying incoming connections using mTLS and directly assigning to users.
|
||||
// Generally this is the CA bundle file used to authenticate client certificates
|
||||
// If this is nil, then mTLS will not be used.
|
||||
ClientCertificateCAContentProvider dynamiccertificates.CAContentProvider
|
||||
|
||||
APIAudiences authenticator.Audiences
|
||||
|
||||
RequestHeaderConfig *RequestHeaderConfig
|
||||
}
|
||||
|
||||
func (c DelegatingAuthenticatorConfig) New() (authenticator.Request, *spec.SecurityDefinitions, error) {
|
||||
authenticators := []authenticator.Request{}
|
||||
securityDefinitions := spec.SecurityDefinitions{}
|
||||
|
||||
// front-proxy first, then remote
|
||||
// Add the front proxy authenticator if requested
|
||||
if c.RequestHeaderConfig != nil {
|
||||
requestHeaderAuthenticator := headerrequest.NewDynamicVerifyOptionsSecure(
|
||||
c.RequestHeaderConfig.CAContentProvider.VerifyOptions,
|
||||
c.RequestHeaderConfig.AllowedClientNames,
|
||||
c.RequestHeaderConfig.UsernameHeaders,
|
||||
c.RequestHeaderConfig.UIDHeaders,
|
||||
c.RequestHeaderConfig.GroupHeaders,
|
||||
c.RequestHeaderConfig.ExtraHeaderPrefixes,
|
||||
)
|
||||
authenticators = append(authenticators, requestHeaderAuthenticator)
|
||||
}
|
||||
|
||||
// x509 client cert auth
|
||||
if c.ClientCertificateCAContentProvider != nil {
|
||||
authenticators = append(authenticators, x509.NewDynamic(c.ClientCertificateCAContentProvider.VerifyOptions, x509.CommonNameUserConversion))
|
||||
}
|
||||
|
||||
if c.TokenAccessReviewClient != nil {
|
||||
if c.WebhookRetryBackoff == nil {
|
||||
return nil, nil, errors.New("retry backoff parameters for delegating authentication webhook has not been specified")
|
||||
}
|
||||
tokenAuth, err := webhooktoken.NewFromInterface(c.TokenAccessReviewClient, c.APIAudiences, *c.WebhookRetryBackoff, c.TokenAccessReviewTimeout, webhooktoken.AuthenticatorMetrics{
|
||||
RecordRequestTotal: RecordRequestTotal,
|
||||
RecordRequestLatency: RecordRequestLatency,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
cachingTokenAuth := cache.New(tokenAuth, false, c.CacheTTL, c.CacheTTL)
|
||||
authenticators = append(authenticators, bearertoken.New(cachingTokenAuth), websocket.NewProtocolAuthenticator(cachingTokenAuth))
|
||||
|
||||
securityDefinitions["BearerToken"] = &spec.SecurityScheme{
|
||||
SecuritySchemeProps: spec.SecuritySchemeProps{
|
||||
Type: "apiKey",
|
||||
Name: "authorization",
|
||||
In: "header",
|
||||
Description: "Bearer Token authentication",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if len(authenticators) == 0 {
|
||||
if c.Anonymous != nil && c.Anonymous.Enabled {
|
||||
return anonymous.NewAuthenticator(c.Anonymous.Conditions), &securityDefinitions, nil
|
||||
}
|
||||
return nil, nil, errors.New("no authentication method configured")
|
||||
}
|
||||
|
||||
authenticator := group.NewAuthenticatedGroupAdder(unionauth.New(authenticators...))
|
||||
if c.Anonymous != nil && c.Anonymous.Enabled {
|
||||
authenticator = unionauth.NewFailOnError(authenticator, anonymous.NewAuthenticator(c.Anonymous.Conditions))
|
||||
}
|
||||
return authenticator, &securityDefinitions, nil
|
||||
}
|
29
e2e/vendor/k8s.io/apiserver/pkg/authentication/authenticatorfactory/loopback.go
generated
vendored
29
e2e/vendor/k8s.io/apiserver/pkg/authentication/authenticatorfactory/loopback.go
generated
vendored
@ -1,29 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package authenticatorfactory
|
||||
|
||||
import (
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/authentication/request/bearertoken"
|
||||
"k8s.io/apiserver/pkg/authentication/token/tokenfile"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
)
|
||||
|
||||
// NewFromTokens returns an authenticator.Request or an error
|
||||
func NewFromTokens(tokens map[string]*user.DefaultInfo, audiences authenticator.Audiences) authenticator.Request {
|
||||
return bearertoken.New(authenticator.WrapAudienceAgnosticToken(audiences, tokenfile.New(tokens)))
|
||||
}
|
69
e2e/vendor/k8s.io/apiserver/pkg/authentication/authenticatorfactory/metrics.go
generated
vendored
69
e2e/vendor/k8s.io/apiserver/pkg/authentication/authenticatorfactory/metrics.go
generated
vendored
@ -1,69 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package authenticatorfactory
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
compbasemetrics "k8s.io/component-base/metrics"
|
||||
"k8s.io/component-base/metrics/legacyregistry"
|
||||
)
|
||||
|
||||
type registerables []compbasemetrics.Registerable
|
||||
|
||||
// init registers all metrics.
|
||||
func init() {
|
||||
for _, metric := range metrics {
|
||||
legacyregistry.MustRegister(metric)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
requestTotal = compbasemetrics.NewCounterVec(
|
||||
&compbasemetrics.CounterOpts{
|
||||
Name: "apiserver_delegated_authn_request_total",
|
||||
Help: "Number of HTTP requests partitioned by status code.",
|
||||
StabilityLevel: compbasemetrics.ALPHA,
|
||||
},
|
||||
[]string{"code"},
|
||||
)
|
||||
|
||||
requestLatency = compbasemetrics.NewHistogramVec(
|
||||
&compbasemetrics.HistogramOpts{
|
||||
Name: "apiserver_delegated_authn_request_duration_seconds",
|
||||
Help: "Request latency in seconds. Broken down by status code.",
|
||||
Buckets: []float64{0.25, 0.5, 0.7, 1, 1.5, 3, 5, 10},
|
||||
StabilityLevel: compbasemetrics.ALPHA,
|
||||
},
|
||||
[]string{"code"},
|
||||
)
|
||||
|
||||
metrics = registerables{
|
||||
requestTotal,
|
||||
requestLatency,
|
||||
}
|
||||
)
|
||||
|
||||
// RecordRequestTotal increments the total number of requests for the delegated authentication.
|
||||
func RecordRequestTotal(ctx context.Context, code string) {
|
||||
requestTotal.WithContext(ctx).WithLabelValues(code).Inc()
|
||||
}
|
||||
|
||||
// RecordRequestLatency measures request latency in seconds for the delegated authentication. Broken down by status code.
|
||||
func RecordRequestLatency(ctx context.Context, code string, latency float64) {
|
||||
requestLatency.WithContext(ctx).WithLabelValues(code).Observe(latency)
|
||||
}
|
39
e2e/vendor/k8s.io/apiserver/pkg/authentication/authenticatorfactory/requestheader.go
generated
vendored
39
e2e/vendor/k8s.io/apiserver/pkg/authentication/authenticatorfactory/requestheader.go
generated
vendored
@ -1,39 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package authenticatorfactory
|
||||
|
||||
import (
|
||||
"k8s.io/apiserver/pkg/authentication/request/headerrequest"
|
||||
"k8s.io/apiserver/pkg/server/dynamiccertificates"
|
||||
)
|
||||
|
||||
type RequestHeaderConfig struct {
|
||||
// UsernameHeaders are the headers to check (in order, case-insensitively) for an identity. The first header with a value wins.
|
||||
UsernameHeaders headerrequest.StringSliceProvider
|
||||
// UsernameHeaders are the headers to check (in order, case-insensitively) for an identity UID. The first header with a value wins.
|
||||
UIDHeaders headerrequest.StringSliceProvider
|
||||
// GroupHeaders are the headers to check (case-insensitively) for a group names. All values will be used.
|
||||
GroupHeaders headerrequest.StringSliceProvider
|
||||
// ExtraHeaderPrefixes are the head prefixes to check (case-insentively) for filling in
|
||||
// the user.Info.Extra. All values of all matching headers will be added.
|
||||
ExtraHeaderPrefixes headerrequest.StringSliceProvider
|
||||
// CAContentProvider the options for verifying incoming connections using mTLS. Generally this points to CA bundle file which is used verify the identity of the front proxy.
|
||||
// It may produce different options at will.
|
||||
CAContentProvider dynamiccertificates.CAContentProvider
|
||||
// AllowedClientNames is a list of common names that may be presented by the authenticating front proxy. Empty means: accept any.
|
||||
AllowedClientNames headerrequest.StringSliceProvider
|
||||
}
|
161
e2e/vendor/k8s.io/apiserver/pkg/authentication/cel/compile.go
generated
vendored
161
e2e/vendor/k8s.io/apiserver/pkg/authentication/cel/compile.go
generated
vendored
@ -1,161 +0,0 @@
|
||||
/*
|
||||
Copyright 2023 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/cel-go/cel"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/version"
|
||||
apiservercel "k8s.io/apiserver/pkg/cel"
|
||||
"k8s.io/apiserver/pkg/cel/environment"
|
||||
)
|
||||
|
||||
const (
|
||||
claimsVarName = "claims"
|
||||
userVarName = "user"
|
||||
)
|
||||
|
||||
// compiler implements the Compiler interface.
|
||||
type compiler struct {
|
||||
// varEnvs is a map of CEL environments, keyed by the name of the CEL variable.
|
||||
// The CEL variable is available to the expression.
|
||||
// We have 2 environments, one for claims and one for user.
|
||||
varEnvs map[string]*environment.EnvSet
|
||||
}
|
||||
|
||||
// NewDefaultCompiler returns a new Compiler following the default compatibility version.
|
||||
// Note: the compiler construction depends on feature gates and the compatibility version to be initialized.
|
||||
func NewDefaultCompiler() Compiler {
|
||||
return NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
|
||||
}
|
||||
|
||||
// NewCompiler returns a new Compiler.
|
||||
func NewCompiler(env *environment.EnvSet) Compiler {
|
||||
return &compiler{
|
||||
varEnvs: mustBuildEnvs(env),
|
||||
}
|
||||
}
|
||||
|
||||
// CompileClaimsExpression compiles the given expressionAccessor into a CEL program that can be evaluated.
|
||||
// The claims CEL variable is available to the expression.
|
||||
func (c compiler) CompileClaimsExpression(expressionAccessor ExpressionAccessor) (CompilationResult, error) {
|
||||
return c.compile(expressionAccessor, claimsVarName)
|
||||
}
|
||||
|
||||
// CompileUserExpression compiles the given expressionAccessor into a CEL program that can be evaluated.
|
||||
// The user CEL variable is available to the expression.
|
||||
func (c compiler) CompileUserExpression(expressionAccessor ExpressionAccessor) (CompilationResult, error) {
|
||||
return c.compile(expressionAccessor, userVarName)
|
||||
}
|
||||
|
||||
func (c compiler) compile(expressionAccessor ExpressionAccessor, envVarName string) (CompilationResult, error) {
|
||||
resultError := func(errorString string, errType apiservercel.ErrorType) (CompilationResult, error) {
|
||||
return CompilationResult{}, &apiservercel.Error{
|
||||
Type: errType,
|
||||
Detail: errorString,
|
||||
}
|
||||
}
|
||||
|
||||
env, err := c.varEnvs[envVarName].Env(environment.StoredExpressions)
|
||||
if err != nil {
|
||||
return resultError(fmt.Sprintf("unexpected error loading CEL environment: %v", err), apiservercel.ErrorTypeInternal)
|
||||
}
|
||||
|
||||
ast, issues := env.Compile(expressionAccessor.GetExpression())
|
||||
if issues != nil {
|
||||
return resultError("compilation failed: "+issues.String(), apiservercel.ErrorTypeInvalid)
|
||||
}
|
||||
|
||||
found := false
|
||||
returnTypes := expressionAccessor.ReturnTypes()
|
||||
for _, returnType := range returnTypes {
|
||||
if ast.OutputType() == returnType || cel.AnyType == returnType {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
var reason string
|
||||
if len(returnTypes) == 1 {
|
||||
reason = fmt.Sprintf("must evaluate to %v", returnTypes[0].String())
|
||||
} else {
|
||||
reason = fmt.Sprintf("must evaluate to one of %v", returnTypes)
|
||||
}
|
||||
|
||||
return resultError(reason, apiservercel.ErrorTypeInvalid)
|
||||
}
|
||||
|
||||
if _, err = cel.AstToCheckedExpr(ast); err != nil {
|
||||
// should be impossible since env.Compile returned no issues
|
||||
return resultError("unexpected compilation error: "+err.Error(), apiservercel.ErrorTypeInternal)
|
||||
}
|
||||
prog, err := env.Program(ast)
|
||||
if err != nil {
|
||||
return resultError("program instantiation failed: "+err.Error(), apiservercel.ErrorTypeInternal)
|
||||
}
|
||||
|
||||
return CompilationResult{
|
||||
Program: prog,
|
||||
AST: ast,
|
||||
ExpressionAccessor: expressionAccessor,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func buildUserType() *apiservercel.DeclType {
|
||||
field := func(name string, declType *apiservercel.DeclType, required bool) *apiservercel.DeclField {
|
||||
return apiservercel.NewDeclField(name, declType, required, nil, nil)
|
||||
}
|
||||
fields := func(fields ...*apiservercel.DeclField) map[string]*apiservercel.DeclField {
|
||||
result := make(map[string]*apiservercel.DeclField, len(fields))
|
||||
for _, f := range fields {
|
||||
result[f.Name] = f
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
return apiservercel.NewObjectType("kubernetes.UserInfo", fields(
|
||||
field("username", apiservercel.StringType, false),
|
||||
field("uid", apiservercel.StringType, false),
|
||||
field("groups", apiservercel.NewListType(apiservercel.StringType, -1), false),
|
||||
field("extra", apiservercel.NewMapType(apiservercel.StringType, apiservercel.NewListType(apiservercel.StringType, -1), -1), false),
|
||||
))
|
||||
}
|
||||
|
||||
func mustBuildEnvs(baseEnv *environment.EnvSet) map[string]*environment.EnvSet {
|
||||
buildEnvSet := func(envOpts []cel.EnvOption, declTypes []*apiservercel.DeclType) *environment.EnvSet {
|
||||
env, err := baseEnv.Extend(environment.VersionedOptions{
|
||||
IntroducedVersion: version.MajorMinor(1, 0),
|
||||
EnvOptions: envOpts,
|
||||
DeclTypes: declTypes,
|
||||
})
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("environment misconfigured: %v", err))
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
||||
userType := buildUserType()
|
||||
claimsType := apiservercel.NewMapType(apiservercel.StringType, apiservercel.AnyType, -1)
|
||||
|
||||
envs := make(map[string]*environment.EnvSet, 2) // build two environments, one for claims and one for user
|
||||
envs[claimsVarName] = buildEnvSet([]cel.EnvOption{cel.Variable(claimsVarName, claimsType.CelType())}, []*apiservercel.DeclType{claimsType})
|
||||
envs[userVarName] = buildEnvSet([]cel.EnvOption{cel.Variable(userVarName, userType.CelType())}, []*apiservercel.DeclType{userType})
|
||||
|
||||
return envs
|
||||
}
|
148
e2e/vendor/k8s.io/apiserver/pkg/authentication/cel/interface.go
generated
vendored
148
e2e/vendor/k8s.io/apiserver/pkg/authentication/cel/interface.go
generated
vendored
@ -1,148 +0,0 @@
|
||||
/*
|
||||
Copyright 2023 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package cel contains the CEL related interfaces and structs for authentication.
|
||||
package cel
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
celgo "github.com/google/cel-go/cel"
|
||||
"github.com/google/cel-go/common/types/ref"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
// ExpressionAccessor is an interface that provides access to a CEL expression.
|
||||
type ExpressionAccessor interface {
|
||||
GetExpression() string
|
||||
ReturnTypes() []*celgo.Type
|
||||
}
|
||||
|
||||
// CompilationResult represents a compiled validations expression.
|
||||
type CompilationResult struct {
|
||||
Program celgo.Program
|
||||
AST *celgo.Ast
|
||||
ExpressionAccessor ExpressionAccessor
|
||||
}
|
||||
|
||||
// EvaluationResult contains the minimal required fields and metadata of a cel evaluation
|
||||
type EvaluationResult struct {
|
||||
EvalResult ref.Val
|
||||
ExpressionAccessor ExpressionAccessor
|
||||
}
|
||||
|
||||
// Compiler provides a CEL expression compiler configured with the desired authentication related CEL variables.
|
||||
type Compiler interface {
|
||||
CompileClaimsExpression(expressionAccessor ExpressionAccessor) (CompilationResult, error)
|
||||
CompileUserExpression(expressionAccessor ExpressionAccessor) (CompilationResult, error)
|
||||
}
|
||||
|
||||
// ClaimsMapper provides a CEL expression mapper configured with the claims CEL variable.
|
||||
type ClaimsMapper interface {
|
||||
// EvalClaimMapping evaluates the given claim mapping expression and returns a EvaluationResult.
|
||||
// This is used for username, groups and uid claim mapping that contains a single expression.
|
||||
EvalClaimMapping(ctx context.Context, claims *unstructured.Unstructured) (EvaluationResult, error)
|
||||
// EvalClaimMappings evaluates the given expressions and returns a list of EvaluationResult.
|
||||
// This is used for extra claim mapping and claim validation that contains a list of expressions.
|
||||
EvalClaimMappings(ctx context.Context, claims *unstructured.Unstructured) ([]EvaluationResult, error)
|
||||
}
|
||||
|
||||
// UserMapper provides a CEL expression mapper configured with the user CEL variable.
|
||||
type UserMapper interface {
|
||||
// EvalUser evaluates the given user expressions and returns a list of EvaluationResult.
|
||||
// This is used for user validation that contains a list of expressions.
|
||||
EvalUser(ctx context.Context, userInfo *unstructured.Unstructured) ([]EvaluationResult, error)
|
||||
}
|
||||
|
||||
var _ ExpressionAccessor = &ClaimMappingExpression{}
|
||||
|
||||
// ClaimMappingExpression is a CEL expression that maps a claim.
|
||||
type ClaimMappingExpression struct {
|
||||
Expression string
|
||||
}
|
||||
|
||||
// GetExpression returns the CEL expression.
|
||||
func (v *ClaimMappingExpression) GetExpression() string {
|
||||
return v.Expression
|
||||
}
|
||||
|
||||
// ReturnTypes returns the CEL expression return types.
|
||||
func (v *ClaimMappingExpression) ReturnTypes() []*celgo.Type {
|
||||
// return types is only used for validation. The claims variable that's available
|
||||
// to the claim mapping expressions is a map[string]interface{}, so we can't
|
||||
// really know what the return type is during compilation. Strict type checking
|
||||
// is done during evaluation.
|
||||
return []*celgo.Type{celgo.AnyType}
|
||||
}
|
||||
|
||||
var _ ExpressionAccessor = &ClaimValidationCondition{}
|
||||
|
||||
// ClaimValidationCondition is a CEL expression that validates a claim.
|
||||
type ClaimValidationCondition struct {
|
||||
Expression string
|
||||
Message string
|
||||
}
|
||||
|
||||
// GetExpression returns the CEL expression.
|
||||
func (v *ClaimValidationCondition) GetExpression() string {
|
||||
return v.Expression
|
||||
}
|
||||
|
||||
// ReturnTypes returns the CEL expression return types.
|
||||
func (v *ClaimValidationCondition) ReturnTypes() []*celgo.Type {
|
||||
return []*celgo.Type{celgo.BoolType}
|
||||
}
|
||||
|
||||
var _ ExpressionAccessor = &ExtraMappingExpression{}
|
||||
|
||||
// ExtraMappingExpression is a CEL expression that maps an extra to a list of values.
|
||||
type ExtraMappingExpression struct {
|
||||
Key string
|
||||
Expression string
|
||||
}
|
||||
|
||||
// GetExpression returns the CEL expression.
|
||||
func (v *ExtraMappingExpression) GetExpression() string {
|
||||
return v.Expression
|
||||
}
|
||||
|
||||
// ReturnTypes returns the CEL expression return types.
|
||||
func (v *ExtraMappingExpression) ReturnTypes() []*celgo.Type {
|
||||
// return types is only used for validation. The claims variable that's available
|
||||
// to the claim mapping expressions is a map[string]interface{}, so we can't
|
||||
// really know what the return type is during compilation. Strict type checking
|
||||
// is done during evaluation.
|
||||
return []*celgo.Type{celgo.AnyType}
|
||||
}
|
||||
|
||||
var _ ExpressionAccessor = &UserValidationCondition{}
|
||||
|
||||
// UserValidationCondition is a CEL expression that validates a User.
|
||||
type UserValidationCondition struct {
|
||||
Expression string
|
||||
Message string
|
||||
}
|
||||
|
||||
// GetExpression returns the CEL expression.
|
||||
func (v *UserValidationCondition) GetExpression() string {
|
||||
return v.Expression
|
||||
}
|
||||
|
||||
// ReturnTypes returns the CEL expression return types.
|
||||
func (v *UserValidationCondition) ReturnTypes() []*celgo.Type {
|
||||
return []*celgo.Type{celgo.BoolType}
|
||||
}
|
97
e2e/vendor/k8s.io/apiserver/pkg/authentication/cel/mapper.go
generated
vendored
97
e2e/vendor/k8s.io/apiserver/pkg/authentication/cel/mapper.go
generated
vendored
@ -1,97 +0,0 @@
|
||||
/*
|
||||
Copyright 2023 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
var _ ClaimsMapper = &mapper{}
|
||||
var _ UserMapper = &mapper{}
|
||||
|
||||
// mapper implements the ClaimsMapper and UserMapper interface.
|
||||
type mapper struct {
|
||||
compilationResults []CompilationResult
|
||||
}
|
||||
|
||||
// CELMapper is a struct that holds the compiled expressions for
|
||||
// username, groups, uid, extra, claimValidation and userValidation
|
||||
type CELMapper struct {
|
||||
Username ClaimsMapper
|
||||
Groups ClaimsMapper
|
||||
UID ClaimsMapper
|
||||
Extra ClaimsMapper
|
||||
ClaimValidationRules ClaimsMapper
|
||||
UserValidationRules UserMapper
|
||||
}
|
||||
|
||||
// NewClaimsMapper returns a new ClaimsMapper.
|
||||
func NewClaimsMapper(compilationResults []CompilationResult) ClaimsMapper {
|
||||
return &mapper{
|
||||
compilationResults: compilationResults,
|
||||
}
|
||||
}
|
||||
|
||||
// NewUserMapper returns a new UserMapper.
|
||||
func NewUserMapper(compilationResults []CompilationResult) UserMapper {
|
||||
return &mapper{
|
||||
compilationResults: compilationResults,
|
||||
}
|
||||
}
|
||||
|
||||
// EvalClaimMapping evaluates the given claim mapping expression and returns a EvaluationResult.
|
||||
func (m *mapper) EvalClaimMapping(ctx context.Context, claims *unstructured.Unstructured) (EvaluationResult, error) {
|
||||
results, err := m.eval(ctx, map[string]interface{}{claimsVarName: claims.Object})
|
||||
if err != nil {
|
||||
return EvaluationResult{}, err
|
||||
}
|
||||
if len(results) != 1 {
|
||||
return EvaluationResult{}, fmt.Errorf("expected 1 evaluation result, got %d", len(results))
|
||||
}
|
||||
return results[0], nil
|
||||
}
|
||||
|
||||
// EvalClaimMappings evaluates the given expressions and returns a list of EvaluationResult.
|
||||
func (m *mapper) EvalClaimMappings(ctx context.Context, claims *unstructured.Unstructured) ([]EvaluationResult, error) {
|
||||
return m.eval(ctx, map[string]interface{}{claimsVarName: claims.Object})
|
||||
}
|
||||
|
||||
// EvalUser evaluates the given user expressions and returns a list of EvaluationResult.
|
||||
func (m *mapper) EvalUser(ctx context.Context, userInfo *unstructured.Unstructured) ([]EvaluationResult, error) {
|
||||
return m.eval(ctx, map[string]interface{}{userVarName: userInfo.Object})
|
||||
}
|
||||
|
||||
func (m *mapper) eval(ctx context.Context, input map[string]interface{}) ([]EvaluationResult, error) {
|
||||
evaluations := make([]EvaluationResult, len(m.compilationResults))
|
||||
|
||||
for i, compilationResult := range m.compilationResults {
|
||||
var evaluation = &evaluations[i]
|
||||
evaluation.ExpressionAccessor = compilationResult.ExpressionAccessor
|
||||
|
||||
evalResult, _, err := compilationResult.Program.ContextEval(ctx, input)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("expression '%s' resulted in error: %w", compilationResult.ExpressionAccessor.GetExpression(), err)
|
||||
}
|
||||
|
||||
evaluation.EvalResult = evalResult
|
||||
}
|
||||
|
||||
return evaluations, nil
|
||||
}
|
66
e2e/vendor/k8s.io/apiserver/pkg/authentication/group/authenticated_group_adder.go
generated
vendored
66
e2e/vendor/k8s.io/apiserver/pkg/authentication/group/authenticated_group_adder.go
generated
vendored
@ -1,66 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package group
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
)
|
||||
|
||||
// AuthenticatedGroupAdder adds system:authenticated group when appropriate
|
||||
type AuthenticatedGroupAdder struct {
|
||||
// Authenticator is delegated to make the authentication decision
|
||||
Authenticator authenticator.Request
|
||||
}
|
||||
|
||||
// NewAuthenticatedGroupAdder wraps a request authenticator, and adds the system:authenticated group when appropriate.
|
||||
// Authentication must succeed, the user must not be system:anonymous, the groups system:authenticated or system:unauthenticated must
|
||||
// not be present
|
||||
func NewAuthenticatedGroupAdder(auth authenticator.Request) authenticator.Request {
|
||||
return &AuthenticatedGroupAdder{auth}
|
||||
}
|
||||
|
||||
func (g *AuthenticatedGroupAdder) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {
|
||||
r, ok, err := g.Authenticator.AuthenticateRequest(req)
|
||||
if err != nil || !ok {
|
||||
return nil, ok, err
|
||||
}
|
||||
|
||||
if r.User.GetName() == user.Anonymous {
|
||||
return r, true, nil
|
||||
}
|
||||
for _, group := range r.User.GetGroups() {
|
||||
if group == user.AllAuthenticated || group == user.AllUnauthenticated {
|
||||
return r, true, nil
|
||||
}
|
||||
}
|
||||
|
||||
newGroups := make([]string, 0, len(r.User.GetGroups())+1)
|
||||
newGroups = append(newGroups, r.User.GetGroups()...)
|
||||
newGroups = append(newGroups, user.AllAuthenticated)
|
||||
|
||||
ret := *r // shallow copy
|
||||
ret.User = &user.DefaultInfo{
|
||||
Name: r.User.GetName(),
|
||||
UID: r.User.GetUID(),
|
||||
Groups: newGroups,
|
||||
Extra: r.User.GetExtra(),
|
||||
}
|
||||
return &ret, true, nil
|
||||
}
|
57
e2e/vendor/k8s.io/apiserver/pkg/authentication/group/group_adder.go
generated
vendored
57
e2e/vendor/k8s.io/apiserver/pkg/authentication/group/group_adder.go
generated
vendored
@ -1,57 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package group
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
)
|
||||
|
||||
// GroupAdder adds groups to an authenticated user.Info
|
||||
type GroupAdder struct {
|
||||
// Authenticator is delegated to make the authentication decision
|
||||
Authenticator authenticator.Request
|
||||
// Groups are additional groups to add to the user.Info from a successful authentication
|
||||
Groups []string
|
||||
}
|
||||
|
||||
// NewGroupAdder wraps a request authenticator, and adds the specified groups to the returned user when authentication succeeds
|
||||
func NewGroupAdder(auth authenticator.Request, groups []string) authenticator.Request {
|
||||
return &GroupAdder{auth, groups}
|
||||
}
|
||||
|
||||
func (g *GroupAdder) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {
|
||||
r, ok, err := g.Authenticator.AuthenticateRequest(req)
|
||||
if err != nil || !ok {
|
||||
return nil, ok, err
|
||||
}
|
||||
|
||||
newGroups := make([]string, 0, len(r.User.GetGroups())+len(g.Groups))
|
||||
newGroups = append(newGroups, r.User.GetGroups()...)
|
||||
newGroups = append(newGroups, g.Groups...)
|
||||
|
||||
ret := *r // shallow copy
|
||||
ret.User = &user.DefaultInfo{
|
||||
Name: r.User.GetName(),
|
||||
UID: r.User.GetUID(),
|
||||
Groups: newGroups,
|
||||
Extra: r.User.GetExtra(),
|
||||
}
|
||||
return &ret, true, nil
|
||||
}
|
57
e2e/vendor/k8s.io/apiserver/pkg/authentication/group/token_group_adder.go
generated
vendored
57
e2e/vendor/k8s.io/apiserver/pkg/authentication/group/token_group_adder.go
generated
vendored
@ -1,57 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package group
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
)
|
||||
|
||||
// TokenGroupAdder adds groups to an authenticated user.Info
|
||||
type TokenGroupAdder struct {
|
||||
// Authenticator is delegated to make the authentication decision
|
||||
Authenticator authenticator.Token
|
||||
// Groups are additional groups to add to the user.Info from a successful authentication
|
||||
Groups []string
|
||||
}
|
||||
|
||||
// NewTokenGroupAdder wraps a token authenticator, and adds the specified groups to the returned user when authentication succeeds
|
||||
func NewTokenGroupAdder(auth authenticator.Token, groups []string) authenticator.Token {
|
||||
return &TokenGroupAdder{auth, groups}
|
||||
}
|
||||
|
||||
func (g *TokenGroupAdder) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) {
|
||||
r, ok, err := g.Authenticator.AuthenticateToken(ctx, token)
|
||||
if err != nil || !ok {
|
||||
return nil, ok, err
|
||||
}
|
||||
|
||||
newGroups := make([]string, 0, len(r.User.GetGroups())+len(g.Groups))
|
||||
newGroups = append(newGroups, r.User.GetGroups()...)
|
||||
newGroups = append(newGroups, g.Groups...)
|
||||
|
||||
ret := *r // shallow copy
|
||||
ret.User = &user.DefaultInfo{
|
||||
Name: r.User.GetName(),
|
||||
UID: r.User.GetUID(),
|
||||
Groups: newGroups,
|
||||
Extra: r.User.GetExtra(),
|
||||
}
|
||||
return &ret, true, nil
|
||||
}
|
62
e2e/vendor/k8s.io/apiserver/pkg/authentication/request/anonymous/anonymous.go
generated
vendored
62
e2e/vendor/k8s.io/apiserver/pkg/authentication/request/anonymous/anonymous.go
generated
vendored
@ -1,62 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package anonymous
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"k8s.io/apiserver/pkg/apis/apiserver"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
)
|
||||
|
||||
const (
|
||||
anonymousUser = user.Anonymous
|
||||
unauthenticatedGroup = user.AllUnauthenticated
|
||||
)
|
||||
|
||||
type Authenticator struct {
|
||||
allowedPaths map[string]bool
|
||||
}
|
||||
|
||||
func (a *Authenticator) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {
|
||||
if len(a.allowedPaths) > 0 && !a.allowedPaths[req.URL.Path] {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
auds, _ := authenticator.AudiencesFrom(req.Context())
|
||||
return &authenticator.Response{
|
||||
User: &user.DefaultInfo{
|
||||
Name: anonymousUser,
|
||||
Groups: []string{unauthenticatedGroup},
|
||||
},
|
||||
Audiences: auds,
|
||||
}, true, nil
|
||||
}
|
||||
|
||||
// NewAuthenticator returns a new anonymous authenticator.
|
||||
// When conditions is empty all requests are authenticated as anonymous.
|
||||
// When conditions are non-empty only those requests that match the at-least one
|
||||
// condition are authenticated as anonymous.
|
||||
func NewAuthenticator(conditions []apiserver.AnonymousAuthCondition) authenticator.Request {
|
||||
allowedPaths := make(map[string]bool)
|
||||
for _, c := range conditions {
|
||||
allowedPaths[c.Path] = true
|
||||
}
|
||||
|
||||
return &Authenticator{allowedPaths: allowedPaths}
|
||||
}
|
76
e2e/vendor/k8s.io/apiserver/pkg/authentication/request/bearertoken/bearertoken.go
generated
vendored
76
e2e/vendor/k8s.io/apiserver/pkg/authentication/request/bearertoken/bearertoken.go
generated
vendored
@ -1,76 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package bearertoken
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/warning"
|
||||
)
|
||||
|
||||
const (
|
||||
invalidTokenWithSpaceWarning = "the provided Authorization header contains extra space before the bearer token, and is ignored"
|
||||
)
|
||||
|
||||
type Authenticator struct {
|
||||
auth authenticator.Token
|
||||
}
|
||||
|
||||
func New(auth authenticator.Token) *Authenticator {
|
||||
return &Authenticator{auth}
|
||||
}
|
||||
|
||||
var invalidToken = errors.New("invalid bearer token")
|
||||
|
||||
func (a *Authenticator) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {
|
||||
auth := strings.TrimSpace(req.Header.Get("Authorization"))
|
||||
if auth == "" {
|
||||
return nil, false, nil
|
||||
}
|
||||
parts := strings.SplitN(auth, " ", 3)
|
||||
if len(parts) < 2 || strings.ToLower(parts[0]) != "bearer" {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
token := parts[1]
|
||||
|
||||
// Empty bearer tokens aren't valid
|
||||
if len(token) == 0 {
|
||||
// The space before the token case
|
||||
if len(parts) == 3 {
|
||||
warning.AddWarning(req.Context(), "", invalidTokenWithSpaceWarning)
|
||||
}
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
resp, ok, err := a.auth.AuthenticateToken(req.Context(), token)
|
||||
// if we authenticated successfully, go ahead and remove the bearer token so that no one
|
||||
// is ever tempted to use it inside of the API server
|
||||
if ok {
|
||||
req.Header.Del("Authorization")
|
||||
}
|
||||
|
||||
// If the token authenticator didn't error, provide a default error
|
||||
if !ok && err == nil {
|
||||
err = invalidToken
|
||||
}
|
||||
|
||||
return resp, ok, err
|
||||
}
|
218
e2e/vendor/k8s.io/apiserver/pkg/authentication/request/headerrequest/requestheader.go
generated
vendored
218
e2e/vendor/k8s.io/apiserver/pkg/authentication/request/headerrequest/requestheader.go
generated
vendored
@ -1,218 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package headerrequest
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
x509request "k8s.io/apiserver/pkg/authentication/request/x509"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
)
|
||||
|
||||
// StringSliceProvider is a way to get a string slice value. It is heavily used for authentication headers among other places.
|
||||
type StringSliceProvider interface {
|
||||
// Value returns the current string slice. Callers should never mutate the returned value.
|
||||
Value() []string
|
||||
}
|
||||
|
||||
// StringSliceProviderFunc is a function that matches the StringSliceProvider interface
|
||||
type StringSliceProviderFunc func() []string
|
||||
|
||||
// Value returns the current string slice. Callers should never mutate the returned value.
|
||||
func (d StringSliceProviderFunc) Value() []string {
|
||||
return d()
|
||||
}
|
||||
|
||||
// StaticStringSlice a StringSliceProvider that returns a fixed value
|
||||
type StaticStringSlice []string
|
||||
|
||||
// Value returns the current string slice. Callers should never mutate the returned value.
|
||||
func (s StaticStringSlice) Value() []string {
|
||||
return s
|
||||
}
|
||||
|
||||
type requestHeaderAuthRequestHandler struct {
|
||||
// nameHeaders are the headers to check (in order, case-insensitively) for an identity. The first header with a value wins.
|
||||
nameHeaders StringSliceProvider
|
||||
|
||||
// nameHeaders are the headers to check (in order, case-insensitively) for an identity UID. The first header with a value wins.
|
||||
uidHeaders StringSliceProvider
|
||||
|
||||
// groupHeaders are the headers to check (case-insensitively) for group membership. All values of all headers will be added.
|
||||
groupHeaders StringSliceProvider
|
||||
|
||||
// extraHeaderPrefixes are the head prefixes to check (case-insensitively) for filling in
|
||||
// the user.Info.Extra. All values of all matching headers will be added.
|
||||
extraHeaderPrefixes StringSliceProvider
|
||||
}
|
||||
|
||||
func New(nameHeaders, uidHeaders, groupHeaders, extraHeaderPrefixes []string) (authenticator.Request, error) {
|
||||
trimmedNameHeaders, err := trimHeaders(nameHeaders...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
trimmedUIDHeaders, err := trimHeaders(uidHeaders...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
trimmedGroupHeaders, err := trimHeaders(groupHeaders...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
trimmedExtraHeaderPrefixes, err := trimHeaders(extraHeaderPrefixes...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewDynamic(
|
||||
StaticStringSlice(trimmedNameHeaders),
|
||||
StaticStringSlice(trimmedUIDHeaders),
|
||||
StaticStringSlice(trimmedGroupHeaders),
|
||||
StaticStringSlice(trimmedExtraHeaderPrefixes),
|
||||
), nil
|
||||
}
|
||||
|
||||
func NewDynamic(nameHeaders, uidHeaders, groupHeaders, extraHeaderPrefixes StringSliceProvider) authenticator.Request {
|
||||
return &requestHeaderAuthRequestHandler{
|
||||
nameHeaders: nameHeaders,
|
||||
uidHeaders: uidHeaders,
|
||||
groupHeaders: groupHeaders,
|
||||
extraHeaderPrefixes: extraHeaderPrefixes,
|
||||
}
|
||||
}
|
||||
|
||||
func trimHeaders(headerNames ...string) ([]string, error) {
|
||||
ret := []string{}
|
||||
for _, headerName := range headerNames {
|
||||
trimmedHeader := strings.TrimSpace(headerName)
|
||||
if len(trimmedHeader) == 0 {
|
||||
return nil, fmt.Errorf("empty header %q", headerName)
|
||||
}
|
||||
ret = append(ret, trimmedHeader)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func NewDynamicVerifyOptionsSecure(verifyOptionFn x509request.VerifyOptionFunc, proxyClientNames, nameHeaders, uidHeaders, groupHeaders, extraHeaderPrefixes StringSliceProvider) authenticator.Request {
|
||||
headerAuthenticator := NewDynamic(nameHeaders, uidHeaders, groupHeaders, extraHeaderPrefixes)
|
||||
|
||||
return x509request.NewDynamicCAVerifier(verifyOptionFn, headerAuthenticator, proxyClientNames)
|
||||
}
|
||||
|
||||
func (a *requestHeaderAuthRequestHandler) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {
|
||||
name := headerValue(req.Header, a.nameHeaders.Value())
|
||||
if len(name) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
uid := headerValue(req.Header, a.uidHeaders.Value())
|
||||
groups := allHeaderValues(req.Header, a.groupHeaders.Value())
|
||||
extra := newExtra(req.Header, a.extraHeaderPrefixes.Value())
|
||||
|
||||
// clear headers used for authentication
|
||||
ClearAuthenticationHeaders(req.Header, a.nameHeaders, a.uidHeaders, a.groupHeaders, a.extraHeaderPrefixes)
|
||||
|
||||
return &authenticator.Response{
|
||||
User: &user.DefaultInfo{
|
||||
Name: name,
|
||||
UID: uid,
|
||||
Groups: groups,
|
||||
Extra: extra,
|
||||
},
|
||||
}, true, nil
|
||||
}
|
||||
|
||||
func ClearAuthenticationHeaders(h http.Header, nameHeaders, uidHeaders, groupHeaders, extraHeaderPrefixes StringSliceProvider) {
|
||||
for _, headerName := range nameHeaders.Value() {
|
||||
h.Del(headerName)
|
||||
}
|
||||
for _, headerName := range uidHeaders.Value() {
|
||||
h.Del(headerName)
|
||||
}
|
||||
for _, headerName := range groupHeaders.Value() {
|
||||
h.Del(headerName)
|
||||
}
|
||||
for _, prefix := range extraHeaderPrefixes.Value() {
|
||||
for k := range h {
|
||||
if hasPrefixIgnoreCase(k, prefix) {
|
||||
delete(h, k) // we have the raw key so avoid relying on canonicalization
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func hasPrefixIgnoreCase(s, prefix string) bool {
|
||||
return len(s) >= len(prefix) && strings.EqualFold(s[:len(prefix)], prefix)
|
||||
}
|
||||
|
||||
func headerValue(h http.Header, headerNames []string) string {
|
||||
for _, headerName := range headerNames {
|
||||
headerValue := h.Get(headerName)
|
||||
if len(headerValue) > 0 {
|
||||
return headerValue
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func allHeaderValues(h http.Header, headerNames []string) []string {
|
||||
ret := []string{}
|
||||
for _, headerName := range headerNames {
|
||||
headerKey := http.CanonicalHeaderKey(headerName)
|
||||
values, ok := h[headerKey]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, headerValue := range values {
|
||||
if len(headerValue) > 0 {
|
||||
ret = append(ret, headerValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func unescapeExtraKey(encodedKey string) string {
|
||||
key, err := url.PathUnescape(encodedKey) // Decode %-encoded bytes.
|
||||
if err != nil {
|
||||
return encodedKey // Always record extra strings, even if malformed/unencoded.
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func newExtra(h http.Header, headerPrefixes []string) map[string][]string {
|
||||
ret := map[string][]string{}
|
||||
|
||||
// we have to iterate over prefixes first in order to have proper ordering inside the value slices
|
||||
for _, prefix := range headerPrefixes {
|
||||
for headerName, vv := range h {
|
||||
if !hasPrefixIgnoreCase(headerName, prefix) {
|
||||
continue
|
||||
}
|
||||
|
||||
extraKey := unescapeExtraKey(strings.ToLower(headerName[len(prefix):]))
|
||||
ret[extraKey] = append(ret[extraKey], vv...)
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
@ -1,356 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package headerrequest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
coreinformers "k8s.io/client-go/informers/core/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
corev1listers "k8s.io/client-go/listers/core/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
authenticationRoleName = "extension-apiserver-authentication-reader"
|
||||
)
|
||||
|
||||
// RequestHeaderAuthRequestProvider a provider that knows how to dynamically fill parts of RequestHeaderConfig struct
|
||||
type RequestHeaderAuthRequestProvider interface {
|
||||
UsernameHeaders() []string
|
||||
UIDHeaders() []string
|
||||
GroupHeaders() []string
|
||||
ExtraHeaderPrefixes() []string
|
||||
AllowedClientNames() []string
|
||||
}
|
||||
|
||||
var _ RequestHeaderAuthRequestProvider = &RequestHeaderAuthRequestController{}
|
||||
|
||||
type requestHeaderBundle struct {
|
||||
UsernameHeaders []string
|
||||
UIDHeaders []string
|
||||
GroupHeaders []string
|
||||
ExtraHeaderPrefixes []string
|
||||
AllowedClientNames []string
|
||||
}
|
||||
|
||||
// RequestHeaderAuthRequestController a controller that exposes a set of methods for dynamically filling parts of RequestHeaderConfig struct.
|
||||
// The methods are sourced from the config map which is being monitored by this controller.
|
||||
// The controller is primed from the server at the construction time for components that don't want to dynamically react to changes
|
||||
// in the config map.
|
||||
type RequestHeaderAuthRequestController struct {
|
||||
name string
|
||||
|
||||
configmapName string
|
||||
configmapNamespace string
|
||||
|
||||
client kubernetes.Interface
|
||||
configmapLister corev1listers.ConfigMapNamespaceLister
|
||||
configmapInformer cache.SharedIndexInformer
|
||||
configmapInformerSynced cache.InformerSynced
|
||||
|
||||
queue workqueue.TypedRateLimitingInterface[string]
|
||||
|
||||
// exportedRequestHeaderBundle is a requestHeaderBundle that contains the last read, non-zero length content of the configmap
|
||||
exportedRequestHeaderBundle atomic.Value
|
||||
|
||||
usernameHeadersKey string
|
||||
uidHeadersKey string
|
||||
groupHeadersKey string
|
||||
extraHeaderPrefixesKey string
|
||||
allowedClientNamesKey string
|
||||
}
|
||||
|
||||
// NewRequestHeaderAuthRequestController creates a new controller that implements RequestHeaderAuthRequestController
|
||||
func NewRequestHeaderAuthRequestController(
|
||||
cmName string,
|
||||
cmNamespace string,
|
||||
client kubernetes.Interface,
|
||||
usernameHeadersKey, uidHeadersKey, groupHeadersKey, extraHeaderPrefixesKey, allowedClientNamesKey string) *RequestHeaderAuthRequestController {
|
||||
c := &RequestHeaderAuthRequestController{
|
||||
name: "RequestHeaderAuthRequestController",
|
||||
|
||||
client: client,
|
||||
|
||||
configmapName: cmName,
|
||||
configmapNamespace: cmNamespace,
|
||||
|
||||
usernameHeadersKey: usernameHeadersKey,
|
||||
uidHeadersKey: uidHeadersKey,
|
||||
groupHeadersKey: groupHeadersKey,
|
||||
extraHeaderPrefixesKey: extraHeaderPrefixesKey,
|
||||
allowedClientNamesKey: allowedClientNamesKey,
|
||||
|
||||
queue: workqueue.NewTypedRateLimitingQueueWithConfig(
|
||||
workqueue.DefaultTypedControllerRateLimiter[string](),
|
||||
workqueue.TypedRateLimitingQueueConfig[string]{Name: "RequestHeaderAuthRequestController"},
|
||||
),
|
||||
}
|
||||
|
||||
// we construct our own informer because we need such a small subset of the information available. Just one namespace.
|
||||
c.configmapInformer = coreinformers.NewFilteredConfigMapInformer(client, c.configmapNamespace, 12*time.Hour, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, func(listOptions *metav1.ListOptions) {
|
||||
listOptions.FieldSelector = fields.OneTermEqualSelector("metadata.name", c.configmapName).String()
|
||||
})
|
||||
|
||||
c.configmapInformer.AddEventHandler(cache.FilteringResourceEventHandler{
|
||||
FilterFunc: func(obj interface{}) bool {
|
||||
if cast, ok := obj.(*corev1.ConfigMap); ok {
|
||||
return cast.Name == c.configmapName && cast.Namespace == c.configmapNamespace
|
||||
}
|
||||
if tombstone, ok := obj.(cache.DeletedFinalStateUnknown); ok {
|
||||
if cast, ok := tombstone.Obj.(*corev1.ConfigMap); ok {
|
||||
return cast.Name == c.configmapName && cast.Namespace == c.configmapNamespace
|
||||
}
|
||||
}
|
||||
return true // always return true just in case. The checks are fairly cheap
|
||||
},
|
||||
Handler: cache.ResourceEventHandlerFuncs{
|
||||
// we have a filter, so any time we're called, we may as well queue. We only ever check one configmap
|
||||
// so we don't have to be choosy about our key.
|
||||
AddFunc: func(obj interface{}) {
|
||||
c.queue.Add(c.keyFn())
|
||||
},
|
||||
UpdateFunc: func(oldObj, newObj interface{}) {
|
||||
c.queue.Add(c.keyFn())
|
||||
},
|
||||
DeleteFunc: func(obj interface{}) {
|
||||
c.queue.Add(c.keyFn())
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
c.configmapLister = corev1listers.NewConfigMapLister(c.configmapInformer.GetIndexer()).ConfigMaps(c.configmapNamespace)
|
||||
c.configmapInformerSynced = c.configmapInformer.HasSynced
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *RequestHeaderAuthRequestController) UsernameHeaders() []string {
|
||||
return c.loadRequestHeaderFor(c.usernameHeadersKey)
|
||||
}
|
||||
|
||||
func (c *RequestHeaderAuthRequestController) UIDHeaders() []string {
|
||||
return c.loadRequestHeaderFor(c.uidHeadersKey)
|
||||
}
|
||||
|
||||
func (c *RequestHeaderAuthRequestController) GroupHeaders() []string {
|
||||
return c.loadRequestHeaderFor(c.groupHeadersKey)
|
||||
}
|
||||
|
||||
func (c *RequestHeaderAuthRequestController) ExtraHeaderPrefixes() []string {
|
||||
return c.loadRequestHeaderFor(c.extraHeaderPrefixesKey)
|
||||
}
|
||||
|
||||
func (c *RequestHeaderAuthRequestController) AllowedClientNames() []string {
|
||||
return c.loadRequestHeaderFor(c.allowedClientNamesKey)
|
||||
}
|
||||
|
||||
// Run starts RequestHeaderAuthRequestController controller and blocks until stopCh is closed.
|
||||
func (c *RequestHeaderAuthRequestController) Run(ctx context.Context, workers int) {
|
||||
defer utilruntime.HandleCrash()
|
||||
defer c.queue.ShutDown()
|
||||
|
||||
klog.Infof("Starting %s", c.name)
|
||||
defer klog.Infof("Shutting down %s", c.name)
|
||||
|
||||
go c.configmapInformer.Run(ctx.Done())
|
||||
|
||||
// wait for caches to fill before starting your work
|
||||
if !cache.WaitForNamedCacheSync(c.name, ctx.Done(), c.configmapInformerSynced) {
|
||||
return
|
||||
}
|
||||
|
||||
// doesn't matter what workers say, only start one.
|
||||
go wait.Until(c.runWorker, time.Second, ctx.Done())
|
||||
|
||||
<-ctx.Done()
|
||||
}
|
||||
|
||||
// // RunOnce runs a single sync loop
|
||||
func (c *RequestHeaderAuthRequestController) RunOnce(ctx context.Context) error {
|
||||
configMap, err := c.client.CoreV1().ConfigMaps(c.configmapNamespace).Get(ctx, c.configmapName, metav1.GetOptions{})
|
||||
switch {
|
||||
case errors.IsNotFound(err):
|
||||
// ignore, authConfigMap is nil now
|
||||
return nil
|
||||
case errors.IsForbidden(err):
|
||||
klog.Warningf("Unable to get configmap/%s in %s. Usually fixed by "+
|
||||
"'kubectl create rolebinding -n %s ROLEBINDING_NAME --role=%s --serviceaccount=YOUR_NS:YOUR_SA'",
|
||||
c.configmapName, c.configmapNamespace, c.configmapNamespace, authenticationRoleName)
|
||||
return err
|
||||
case err != nil:
|
||||
return err
|
||||
}
|
||||
return c.syncConfigMap(configMap)
|
||||
}
|
||||
|
||||
func (c *RequestHeaderAuthRequestController) runWorker() {
|
||||
for c.processNextWorkItem() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RequestHeaderAuthRequestController) processNextWorkItem() bool {
|
||||
dsKey, quit := c.queue.Get()
|
||||
if quit {
|
||||
return false
|
||||
}
|
||||
defer c.queue.Done(dsKey)
|
||||
|
||||
err := c.sync()
|
||||
if err == nil {
|
||||
c.queue.Forget(dsKey)
|
||||
return true
|
||||
}
|
||||
|
||||
utilruntime.HandleError(fmt.Errorf("%v failed with : %v", dsKey, err))
|
||||
c.queue.AddRateLimited(dsKey)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// sync reads the config and propagates the changes to exportedRequestHeaderBundle
|
||||
// which is exposed by the set of methods that are used to fill RequestHeaderConfig struct
|
||||
func (c *RequestHeaderAuthRequestController) sync() error {
|
||||
configMap, err := c.configmapLister.Get(c.configmapName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.syncConfigMap(configMap)
|
||||
}
|
||||
|
||||
func (c *RequestHeaderAuthRequestController) syncConfigMap(configMap *corev1.ConfigMap) error {
|
||||
hasChanged, newRequestHeaderBundle, err := c.hasRequestHeaderBundleChanged(configMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hasChanged {
|
||||
c.exportedRequestHeaderBundle.Store(newRequestHeaderBundle)
|
||||
klog.V(2).Infof("Loaded a new request header values for %v", c.name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *RequestHeaderAuthRequestController) hasRequestHeaderBundleChanged(cm *corev1.ConfigMap) (bool, *requestHeaderBundle, error) {
|
||||
currentHeadersBundle, err := c.getRequestHeaderBundleFromConfigMap(cm)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
rawHeaderBundle := c.exportedRequestHeaderBundle.Load()
|
||||
if rawHeaderBundle == nil {
|
||||
return true, currentHeadersBundle, nil
|
||||
}
|
||||
|
||||
// check to see if we have a change. If the values are the same, do nothing.
|
||||
loadedHeadersBundle, ok := rawHeaderBundle.(*requestHeaderBundle)
|
||||
if !ok {
|
||||
return true, currentHeadersBundle, nil
|
||||
}
|
||||
|
||||
if !equality.Semantic.DeepEqual(loadedHeadersBundle, currentHeadersBundle) {
|
||||
return true, currentHeadersBundle, nil
|
||||
}
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
func (c *RequestHeaderAuthRequestController) getRequestHeaderBundleFromConfigMap(cm *corev1.ConfigMap) (*requestHeaderBundle, error) {
|
||||
usernameHeaderCurrentValue, err := deserializeStrings(cm.Data[c.usernameHeadersKey])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uidHeaderCurrentValue, err := deserializeStrings(cm.Data[c.uidHeadersKey])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
groupHeadersCurrentValue, err := deserializeStrings(cm.Data[c.groupHeadersKey])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extraHeaderPrefixesCurrentValue, err := deserializeStrings(cm.Data[c.extraHeaderPrefixesKey])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
}
|
||||
|
||||
allowedClientNamesCurrentValue, err := deserializeStrings(cm.Data[c.allowedClientNamesKey])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &requestHeaderBundle{
|
||||
UsernameHeaders: usernameHeaderCurrentValue,
|
||||
UIDHeaders: uidHeaderCurrentValue,
|
||||
GroupHeaders: groupHeadersCurrentValue,
|
||||
ExtraHeaderPrefixes: extraHeaderPrefixesCurrentValue,
|
||||
AllowedClientNames: allowedClientNamesCurrentValue,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *RequestHeaderAuthRequestController) loadRequestHeaderFor(key string) []string {
|
||||
rawHeaderBundle := c.exportedRequestHeaderBundle.Load()
|
||||
if rawHeaderBundle == nil {
|
||||
return nil // this can happen if we've been unable load data from the apiserver for some reason
|
||||
}
|
||||
headerBundle := rawHeaderBundle.(*requestHeaderBundle)
|
||||
|
||||
switch key {
|
||||
case c.usernameHeadersKey:
|
||||
return headerBundle.UsernameHeaders
|
||||
case c.uidHeadersKey:
|
||||
return headerBundle.UIDHeaders
|
||||
case c.groupHeadersKey:
|
||||
return headerBundle.GroupHeaders
|
||||
case c.extraHeaderPrefixesKey:
|
||||
return headerBundle.ExtraHeaderPrefixes
|
||||
case c.allowedClientNamesKey:
|
||||
return headerBundle.AllowedClientNames
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RequestHeaderAuthRequestController) keyFn() string {
|
||||
// this format matches DeletionHandlingMetaNamespaceKeyFunc for our single key
|
||||
return c.configmapNamespace + "/" + c.configmapName
|
||||
}
|
||||
|
||||
func deserializeStrings(in string) ([]string, error) {
|
||||
if len(in) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var ret []string
|
||||
if err := json.Unmarshal([]byte(in), &ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
71
e2e/vendor/k8s.io/apiserver/pkg/authentication/request/union/union.go
generated
vendored
71
e2e/vendor/k8s.io/apiserver/pkg/authentication/request/union/union.go
generated
vendored
@ -1,71 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package union
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
)
|
||||
|
||||
// unionAuthRequestHandler authenticates requests using a chain of authenticator.Requests
|
||||
type unionAuthRequestHandler struct {
|
||||
// Handlers is a chain of request authenticators to delegate to
|
||||
Handlers []authenticator.Request
|
||||
// FailOnError determines whether an error returns short-circuits the chain
|
||||
FailOnError bool
|
||||
}
|
||||
|
||||
// New returns a request authenticator that validates credentials using a chain of authenticator.Request objects.
|
||||
// The entire chain is tried until one succeeds. If all fail, an aggregate error is returned.
|
||||
func New(authRequestHandlers ...authenticator.Request) authenticator.Request {
|
||||
if len(authRequestHandlers) == 1 {
|
||||
return authRequestHandlers[0]
|
||||
}
|
||||
return &unionAuthRequestHandler{Handlers: authRequestHandlers, FailOnError: false}
|
||||
}
|
||||
|
||||
// NewFailOnError returns a request authenticator that validates credentials using a chain of authenticator.Request objects.
|
||||
// The first error short-circuits the chain.
|
||||
func NewFailOnError(authRequestHandlers ...authenticator.Request) authenticator.Request {
|
||||
if len(authRequestHandlers) == 1 {
|
||||
return authRequestHandlers[0]
|
||||
}
|
||||
return &unionAuthRequestHandler{Handlers: authRequestHandlers, FailOnError: true}
|
||||
}
|
||||
|
||||
// AuthenticateRequest authenticates the request using a chain of authenticator.Request objects.
|
||||
func (authHandler *unionAuthRequestHandler) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {
|
||||
var errlist []error
|
||||
for _, currAuthRequestHandler := range authHandler.Handlers {
|
||||
resp, ok, err := currAuthRequestHandler.AuthenticateRequest(req)
|
||||
if err != nil {
|
||||
if authHandler.FailOnError {
|
||||
return resp, ok, err
|
||||
}
|
||||
errlist = append(errlist, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if ok {
|
||||
return resp, ok, err
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false, utilerrors.NewAggregate(errlist)
|
||||
}
|
108
e2e/vendor/k8s.io/apiserver/pkg/authentication/request/websocket/protocol.go
generated
vendored
108
e2e/vendor/k8s.io/apiserver/pkg/authentication/request/websocket/protocol.go
generated
vendored
@ -1,108 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/httpstream/wsstream"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
)
|
||||
|
||||
const bearerProtocolPrefix = "base64url.bearer.authorization.k8s.io."
|
||||
|
||||
var protocolHeader = textproto.CanonicalMIMEHeaderKey("Sec-WebSocket-Protocol")
|
||||
|
||||
var errInvalidToken = errors.New("invalid bearer token")
|
||||
|
||||
// ProtocolAuthenticator allows a websocket connection to provide a bearer token as a subprotocol
|
||||
// in the format "base64url.bearer.authorization.<base64url-without-padding(bearer-token)>"
|
||||
type ProtocolAuthenticator struct {
|
||||
// auth is the token authenticator to use to validate the token
|
||||
auth authenticator.Token
|
||||
}
|
||||
|
||||
func NewProtocolAuthenticator(auth authenticator.Token) *ProtocolAuthenticator {
|
||||
return &ProtocolAuthenticator{auth}
|
||||
}
|
||||
|
||||
func (a *ProtocolAuthenticator) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {
|
||||
// Only accept websocket connections
|
||||
if !wsstream.IsWebSocketRequest(req) {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
token := ""
|
||||
sawTokenProtocol := false
|
||||
filteredProtocols := []string{}
|
||||
for _, protocolHeader := range req.Header[protocolHeader] {
|
||||
for _, protocol := range strings.Split(protocolHeader, ",") {
|
||||
protocol = strings.TrimSpace(protocol)
|
||||
|
||||
if !strings.HasPrefix(protocol, bearerProtocolPrefix) {
|
||||
filteredProtocols = append(filteredProtocols, protocol)
|
||||
continue
|
||||
}
|
||||
|
||||
if sawTokenProtocol {
|
||||
return nil, false, errors.New("multiple base64.bearer.authorization tokens specified")
|
||||
}
|
||||
sawTokenProtocol = true
|
||||
|
||||
encodedToken := strings.TrimPrefix(protocol, bearerProtocolPrefix)
|
||||
decodedToken, err := base64.RawURLEncoding.DecodeString(encodedToken)
|
||||
if err != nil {
|
||||
return nil, false, errors.New("invalid base64.bearer.authorization token encoding")
|
||||
}
|
||||
if !utf8.Valid(decodedToken) {
|
||||
return nil, false, errors.New("invalid base64.bearer.authorization token")
|
||||
}
|
||||
token = string(decodedToken)
|
||||
}
|
||||
}
|
||||
|
||||
// Must pass at least one other subprotocol so that we can remove the one containing the bearer token,
|
||||
// and there is at least one to echo back to the client
|
||||
if len(token) > 0 && len(filteredProtocols) == 0 {
|
||||
return nil, false, errors.New("missing additional subprotocol")
|
||||
}
|
||||
|
||||
if len(token) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
resp, ok, err := a.auth.AuthenticateToken(req.Context(), token)
|
||||
|
||||
// on success, remove the protocol with the token
|
||||
if ok {
|
||||
// https://tools.ietf.org/html/rfc6455#section-11.3.4 indicates the Sec-WebSocket-Protocol header may appear multiple times
|
||||
// in a request, and is logically the same as a single Sec-WebSocket-Protocol header field that contains all values
|
||||
req.Header.Set(protocolHeader, strings.Join(filteredProtocols, ","))
|
||||
}
|
||||
|
||||
// If the token authenticator didn't error, provide a default error
|
||||
if !ok && err == nil {
|
||||
err = errInvalidToken
|
||||
}
|
||||
|
||||
return resp, ok, err
|
||||
}
|
8
e2e/vendor/k8s.io/apiserver/pkg/authentication/request/x509/OWNERS
generated
vendored
8
e2e/vendor/k8s.io/apiserver/pkg/authentication/request/x509/OWNERS
generated
vendored
@ -1,8 +0,0 @@
|
||||
# See the OWNERS docs at https://go.k8s.io/owners
|
||||
|
||||
approvers:
|
||||
- sig-auth-certificates-approvers
|
||||
reviewers:
|
||||
- sig-auth-certificates-reviewers
|
||||
labels:
|
||||
- sig/auth
|
19
e2e/vendor/k8s.io/apiserver/pkg/authentication/request/x509/doc.go
generated
vendored
19
e2e/vendor/k8s.io/apiserver/pkg/authentication/request/x509/doc.go
generated
vendored
@ -1,19 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package x509 provides a request authenticator that validates and
|
||||
// extracts user information from client certificates
|
||||
package x509
|
71
e2e/vendor/k8s.io/apiserver/pkg/authentication/request/x509/verify_options.go
generated
vendored
71
e2e/vendor/k8s.io/apiserver/pkg/authentication/request/x509/verify_options.go
generated
vendored
@ -1,71 +0,0 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package x509
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/client-go/util/cert"
|
||||
)
|
||||
|
||||
// StaticVerifierFn is a VerifyOptionFunc that always returns the same value. This allows verify options that cannot change.
|
||||
func StaticVerifierFn(opts x509.VerifyOptions) VerifyOptionFunc {
|
||||
return func() (x509.VerifyOptions, bool) {
|
||||
return opts, true
|
||||
}
|
||||
}
|
||||
|
||||
// NewStaticVerifierFromFile creates a new verification func from a file. It reads the content and then fails.
|
||||
// It will return a nil function if you pass an empty CA file.
|
||||
func NewStaticVerifierFromFile(clientCA string) (VerifyOptionFunc, error) {
|
||||
if len(clientCA) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Wrap with an x509 verifier
|
||||
var err error
|
||||
opts := DefaultVerifyOptions()
|
||||
opts.Roots, err = cert.NewPool(clientCA)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error loading certs from %s: %v", clientCA, err)
|
||||
}
|
||||
|
||||
return StaticVerifierFn(opts), nil
|
||||
}
|
||||
|
||||
// StringSliceProvider is a way to get a string slice value. It is heavily used for authentication headers among other places.
|
||||
type StringSliceProvider interface {
|
||||
// Value returns the current string slice. Callers should never mutate the returned value.
|
||||
Value() []string
|
||||
}
|
||||
|
||||
// StringSliceProviderFunc is a function that matches the StringSliceProvider interface
|
||||
type StringSliceProviderFunc func() []string
|
||||
|
||||
// Value returns the current string slice. Callers should never mutate the returned value.
|
||||
func (d StringSliceProviderFunc) Value() []string {
|
||||
return d()
|
||||
}
|
||||
|
||||
// StaticStringSlice a StringSliceProvider that returns a fixed value
|
||||
type StaticStringSlice []string
|
||||
|
||||
// Value returns the current string slice. Callers should never mutate the returned value.
|
||||
func (s StaticStringSlice) Value() []string {
|
||||
return s
|
||||
}
|
332
e2e/vendor/k8s.io/apiserver/pkg/authentication/request/x509/x509.go
generated
vendored
332
e2e/vendor/k8s.io/apiserver/pkg/authentication/request/x509/x509.go
generated
vendored
@ -1,332 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package x509
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
asn1util "k8s.io/apimachinery/pkg/apis/asn1"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/component-base/metrics"
|
||||
"k8s.io/component-base/metrics/legacyregistry"
|
||||
)
|
||||
|
||||
/*
|
||||
* By default, the following metric is defined as falling under
|
||||
* ALPHA stability level https://github.com/kubernetes/enhancements/blob/master/keps/sig-instrumentation/1209-metrics-stability/kubernetes-control-plane-metrics-stability.md#stability-classes)
|
||||
*
|
||||
* Promoting the stability level of the metric is a responsibility of the component owner, since it
|
||||
* involves explicitly acknowledging support for the metric across multiple releases, in accordance with
|
||||
* the metric stability policy.
|
||||
*/
|
||||
var clientCertificateExpirationHistogram = metrics.NewHistogram(
|
||||
&metrics.HistogramOpts{
|
||||
Namespace: "apiserver",
|
||||
Subsystem: "client",
|
||||
Name: "certificate_expiration_seconds",
|
||||
Help: "Distribution of the remaining lifetime on the certificate used to authenticate a request.",
|
||||
Buckets: []float64{
|
||||
0,
|
||||
1800, // 30 minutes
|
||||
3600, // 1 hour
|
||||
7200, // 2 hours
|
||||
21600, // 6 hours
|
||||
43200, // 12 hours
|
||||
86400, // 1 day
|
||||
172800, // 2 days
|
||||
345600, // 4 days
|
||||
604800, // 1 week
|
||||
2592000, // 1 month
|
||||
7776000, // 3 months
|
||||
15552000, // 6 months
|
||||
31104000, // 1 year
|
||||
},
|
||||
StabilityLevel: metrics.ALPHA,
|
||||
},
|
||||
)
|
||||
|
||||
func init() {
|
||||
legacyregistry.MustRegister(clientCertificateExpirationHistogram)
|
||||
}
|
||||
|
||||
// UserConversion defines an interface for extracting user info from a client certificate chain
|
||||
type UserConversion interface {
|
||||
User(chain []*x509.Certificate) (*authenticator.Response, bool, error)
|
||||
}
|
||||
|
||||
// UserConversionFunc is a function that implements the UserConversion interface.
|
||||
type UserConversionFunc func(chain []*x509.Certificate) (*authenticator.Response, bool, error)
|
||||
|
||||
// User implements x509.UserConversion
|
||||
func (f UserConversionFunc) User(chain []*x509.Certificate) (*authenticator.Response, bool, error) {
|
||||
return f(chain)
|
||||
}
|
||||
|
||||
func columnSeparatedHex(d []byte) string {
|
||||
h := strings.ToUpper(hex.EncodeToString(d))
|
||||
var sb strings.Builder
|
||||
for i, r := range h {
|
||||
sb.WriteRune(r)
|
||||
if i%2 == 1 && i != len(h)-1 {
|
||||
sb.WriteRune(':')
|
||||
}
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func certificateIdentifier(c *x509.Certificate) string {
|
||||
return fmt.Sprintf(
|
||||
"SN=%d, SKID=%s, AKID=%s",
|
||||
c.SerialNumber,
|
||||
columnSeparatedHex(c.SubjectKeyId),
|
||||
columnSeparatedHex(c.AuthorityKeyId),
|
||||
)
|
||||
}
|
||||
|
||||
// VerifyOptionFunc is function which provides a shallow copy of the VerifyOptions to the authenticator. This allows
|
||||
// for cases where the options (particularly the CAs) can change. If the bool is false, then the returned VerifyOptions
|
||||
// are ignored and the authenticator will express "no opinion". This allows a clear signal for cases where a CertPool
|
||||
// is eventually expected, but not currently present.
|
||||
type VerifyOptionFunc func() (x509.VerifyOptions, bool)
|
||||
|
||||
// Authenticator implements request.Authenticator by extracting user info from verified client certificates
|
||||
type Authenticator struct {
|
||||
verifyOptionsFn VerifyOptionFunc
|
||||
user UserConversion
|
||||
}
|
||||
|
||||
// New returns a request.Authenticator that verifies client certificates using the provided
|
||||
// VerifyOptions, and converts valid certificate chains into user.Info using the provided UserConversion
|
||||
func New(opts x509.VerifyOptions, user UserConversion) *Authenticator {
|
||||
return NewDynamic(StaticVerifierFn(opts), user)
|
||||
}
|
||||
|
||||
// NewDynamic returns a request.Authenticator that verifies client certificates using the provided
|
||||
// VerifyOptionFunc (which may be dynamic), and converts valid certificate chains into user.Info using the provided UserConversion
|
||||
func NewDynamic(verifyOptionsFn VerifyOptionFunc, user UserConversion) *Authenticator {
|
||||
return &Authenticator{verifyOptionsFn, user}
|
||||
}
|
||||
|
||||
// AuthenticateRequest authenticates the request using presented client certificates
|
||||
func (a *Authenticator) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {
|
||||
if req.TLS == nil || len(req.TLS.PeerCertificates) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
// Use intermediates, if provided
|
||||
optsCopy, ok := a.verifyOptionsFn()
|
||||
// if there are intentionally no verify options, then we cannot authenticate this request
|
||||
if !ok {
|
||||
return nil, false, nil
|
||||
}
|
||||
if optsCopy.Intermediates == nil && len(req.TLS.PeerCertificates) > 1 {
|
||||
optsCopy.Intermediates = x509.NewCertPool()
|
||||
for _, intermediate := range req.TLS.PeerCertificates[1:] {
|
||||
optsCopy.Intermediates.AddCert(intermediate)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
kubernetes mutual (2-way) x509 between client and apiserver:
|
||||
|
||||
1. apiserver sending its apiserver certificate along with its publickey to client
|
||||
2. client verifies the apiserver certificate sent against its cluster certificate authority data
|
||||
3. client sending its client certificate along with its public key to the apiserver
|
||||
>4. apiserver verifies the client certificate sent against its cluster certificate authority data
|
||||
|
||||
description:
|
||||
here, with this function,
|
||||
client certificate and pub key sent during the handshake process
|
||||
are verified by apiserver against its cluster certificate authority data
|
||||
|
||||
normal args related to this stage:
|
||||
--client-ca-file string If set, any request presenting a client certificate signed by
|
||||
one of the authorities in the client-ca-file is authenticated with an identity
|
||||
corresponding to the CommonName of the client certificate.
|
||||
|
||||
(retrievable from "kube-apiserver --help" command)
|
||||
(suggested by @deads2k)
|
||||
|
||||
see also:
|
||||
- for the step 1, see: staging/src/k8s.io/apiserver/pkg/server/options/serving.go
|
||||
- for the step 2, see: staging/src/k8s.io/client-go/transport/transport.go
|
||||
- for the step 3, see: staging/src/k8s.io/client-go/transport/transport.go
|
||||
*/
|
||||
|
||||
remaining := req.TLS.PeerCertificates[0].NotAfter.Sub(time.Now())
|
||||
clientCertificateExpirationHistogram.WithContext(req.Context()).Observe(remaining.Seconds())
|
||||
chains, err := req.TLS.PeerCertificates[0].Verify(optsCopy)
|
||||
if err != nil {
|
||||
return nil, false, fmt.Errorf(
|
||||
"verifying certificate %s failed: %w",
|
||||
certificateIdentifier(req.TLS.PeerCertificates[0]),
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
var errlist []error
|
||||
for _, chain := range chains {
|
||||
user, ok, err := a.user.User(chain)
|
||||
if err != nil {
|
||||
errlist = append(errlist, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if ok {
|
||||
return user, ok, err
|
||||
}
|
||||
}
|
||||
return nil, false, utilerrors.NewAggregate(errlist)
|
||||
}
|
||||
|
||||
// Verifier implements request.Authenticator by verifying a client cert on the request, then delegating to the wrapped auth
|
||||
type Verifier struct {
|
||||
verifyOptionsFn VerifyOptionFunc
|
||||
auth authenticator.Request
|
||||
|
||||
// allowedCommonNames contains the common names which a verified certificate is allowed to have.
|
||||
// If empty, all verified certificates are allowed.
|
||||
allowedCommonNames StringSliceProvider
|
||||
}
|
||||
|
||||
// NewVerifier create a request.Authenticator by verifying a client cert on the request, then delegating to the wrapped auth
|
||||
func NewVerifier(opts x509.VerifyOptions, auth authenticator.Request, allowedCommonNames sets.String) authenticator.Request {
|
||||
return NewDynamicCAVerifier(StaticVerifierFn(opts), auth, StaticStringSlice(allowedCommonNames.List()))
|
||||
}
|
||||
|
||||
// NewDynamicCAVerifier create a request.Authenticator by verifying a client cert on the request, then delegating to the wrapped auth
|
||||
func NewDynamicCAVerifier(verifyOptionsFn VerifyOptionFunc, auth authenticator.Request, allowedCommonNames StringSliceProvider) authenticator.Request {
|
||||
return &Verifier{verifyOptionsFn, auth, allowedCommonNames}
|
||||
}
|
||||
|
||||
// AuthenticateRequest verifies the presented client certificate, then delegates to the wrapped auth
|
||||
func (a *Verifier) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {
|
||||
if req.TLS == nil || len(req.TLS.PeerCertificates) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
// Use intermediates, if provided
|
||||
optsCopy, ok := a.verifyOptionsFn()
|
||||
// if there are intentionally no verify options, then we cannot authenticate this request
|
||||
if !ok {
|
||||
return nil, false, nil
|
||||
}
|
||||
if optsCopy.Intermediates == nil && len(req.TLS.PeerCertificates) > 1 {
|
||||
optsCopy.Intermediates = x509.NewCertPool()
|
||||
for _, intermediate := range req.TLS.PeerCertificates[1:] {
|
||||
optsCopy.Intermediates.AddCert(intermediate)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := req.TLS.PeerCertificates[0].Verify(optsCopy); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if err := a.verifySubject(req.TLS.PeerCertificates[0].Subject); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return a.auth.AuthenticateRequest(req)
|
||||
}
|
||||
|
||||
func (a *Verifier) verifySubject(subject pkix.Name) error {
|
||||
// No CN restrictions
|
||||
if len(a.allowedCommonNames.Value()) == 0 {
|
||||
return nil
|
||||
}
|
||||
// Enforce CN restrictions
|
||||
for _, allowedCommonName := range a.allowedCommonNames.Value() {
|
||||
if allowedCommonName == subject.CommonName {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("x509: subject with cn=%s is not in the allowed list", subject.CommonName)
|
||||
}
|
||||
|
||||
// DefaultVerifyOptions returns VerifyOptions that use the system root certificates, current time,
|
||||
// and requires certificates to be valid for client auth (x509.ExtKeyUsageClientAuth)
|
||||
func DefaultVerifyOptions() x509.VerifyOptions {
|
||||
return x509.VerifyOptions{
|
||||
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
}
|
||||
}
|
||||
|
||||
// CommonNameUserConversion builds user info from a certificate chain using the subject's CommonName
|
||||
var CommonNameUserConversion = UserConversionFunc(func(chain []*x509.Certificate) (*authenticator.Response, bool, error) {
|
||||
if len(chain[0].Subject.CommonName) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
fp := sha256.Sum256(chain[0].Raw)
|
||||
id := "X509SHA256=" + hex.EncodeToString(fp[:])
|
||||
|
||||
uid, err := parseUIDFromCert(chain[0])
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return &authenticator.Response{
|
||||
User: &user.DefaultInfo{
|
||||
Name: chain[0].Subject.CommonName,
|
||||
UID: uid,
|
||||
Groups: chain[0].Subject.Organization,
|
||||
Extra: map[string][]string{
|
||||
user.CredentialIDKey: {id},
|
||||
},
|
||||
},
|
||||
}, true, nil
|
||||
})
|
||||
|
||||
var uidOID = asn1util.X509UID()
|
||||
|
||||
func parseUIDFromCert(cert *x509.Certificate) (string, error) {
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.AllowParsingUserUIDFromCertAuth) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
uids := []string{}
|
||||
for _, name := range cert.Subject.Names {
|
||||
if !name.Type.Equal(uidOID) {
|
||||
continue
|
||||
}
|
||||
uid, ok := name.Value.(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("unable to parse UID into a string")
|
||||
}
|
||||
uids = append(uids, uid)
|
||||
}
|
||||
if len(uids) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
if len(uids) != 1 {
|
||||
return "", fmt.Errorf("expected 1 UID, but found multiple: %v", uids)
|
||||
}
|
||||
if uids[0] == "" {
|
||||
return "", errors.New("UID cannot be an empty string")
|
||||
}
|
||||
return uids[0], nil
|
||||
}
|
49
e2e/vendor/k8s.io/apiserver/pkg/authentication/token/cache/cache_simple.go
generated
vendored
49
e2e/vendor/k8s.io/apiserver/pkg/authentication/token/cache/cache_simple.go
generated
vendored
@ -1,49 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
utilcache "k8s.io/apimachinery/pkg/util/cache"
|
||||
"k8s.io/utils/clock"
|
||||
)
|
||||
|
||||
type simpleCache struct {
|
||||
cache *utilcache.Expiring
|
||||
}
|
||||
|
||||
func newSimpleCache(clock clock.Clock) cache {
|
||||
return &simpleCache{cache: utilcache.NewExpiringWithClock(clock)}
|
||||
}
|
||||
|
||||
func (c *simpleCache) get(key string) (*cacheRecord, bool) {
|
||||
record, ok := c.cache.Get(key)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
value, ok := record.(*cacheRecord)
|
||||
return value, ok
|
||||
}
|
||||
|
||||
func (c *simpleCache) set(key string, value *cacheRecord, ttl time.Duration) {
|
||||
c.cache.Set(key, value, ttl)
|
||||
}
|
||||
|
||||
func (c *simpleCache) remove(key string) {
|
||||
c.cache.Delete(key)
|
||||
}
|
60
e2e/vendor/k8s.io/apiserver/pkg/authentication/token/cache/cache_striped.go
generated
vendored
60
e2e/vendor/k8s.io/apiserver/pkg/authentication/token/cache/cache_striped.go
generated
vendored
@ -1,60 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"hash/fnv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// split cache lookups across N striped caches
|
||||
type stripedCache struct {
|
||||
stripeCount uint32
|
||||
hashFunc func(string) uint32
|
||||
caches []cache
|
||||
}
|
||||
|
||||
type hashFunc func(string) uint32
|
||||
type newCacheFunc func() cache
|
||||
|
||||
func newStripedCache(stripeCount int, hash hashFunc, newCacheFunc newCacheFunc) cache {
|
||||
caches := []cache{}
|
||||
for i := 0; i < stripeCount; i++ {
|
||||
caches = append(caches, newCacheFunc())
|
||||
}
|
||||
return &stripedCache{
|
||||
stripeCount: uint32(stripeCount),
|
||||
hashFunc: hash,
|
||||
caches: caches,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *stripedCache) get(key string) (*cacheRecord, bool) {
|
||||
return c.caches[c.hashFunc(key)%c.stripeCount].get(key)
|
||||
}
|
||||
func (c *stripedCache) set(key string, value *cacheRecord, ttl time.Duration) {
|
||||
c.caches[c.hashFunc(key)%c.stripeCount].set(key, value, ttl)
|
||||
}
|
||||
func (c *stripedCache) remove(key string) {
|
||||
c.caches[c.hashFunc(key)%c.stripeCount].remove(key)
|
||||
}
|
||||
|
||||
func fnvHashFunc(key string) uint32 {
|
||||
f := fnv.New32()
|
||||
f.Write([]byte(key))
|
||||
return f.Sum32()
|
||||
}
|
318
e2e/vendor/k8s.io/apiserver/pkg/authentication/token/cache/cached_token_authenticator.go
generated
vendored
318
e2e/vendor/k8s.io/apiserver/pkg/authentication/token/cache/cached_token_authenticator.go
generated
vendored
@ -1,318 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"hash"
|
||||
"io"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sync/singleflight"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
auditinternal "k8s.io/apiserver/pkg/apis/audit"
|
||||
"k8s.io/apiserver/pkg/audit"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/warning"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/utils/clock"
|
||||
)
|
||||
|
||||
var errAuthnCrash = apierrors.NewInternalError(errors.New("authentication failed unexpectedly"))
|
||||
|
||||
const sharedLookupTimeout = 30 * time.Second
|
||||
|
||||
// cacheRecord holds the three return values of the authenticator.Token AuthenticateToken method
|
||||
type cacheRecord struct {
|
||||
resp *authenticator.Response
|
||||
ok bool
|
||||
err error
|
||||
|
||||
// this cache assumes token authn has no side-effects or temporal dependence.
|
||||
// neither of these are true for audit annotations set via AddAuditAnnotation.
|
||||
//
|
||||
// for audit annotations, the assumption is that for some period of time (cache TTL),
|
||||
// all requests with the same API audiences and the same bearer token result in the
|
||||
// same annotations. This may not be true if the authenticator sets an annotation
|
||||
// based on the current time, but that may be okay since cache TTLs are generally
|
||||
// small (seconds).
|
||||
annotations map[string]string
|
||||
warnings []*cacheWarning
|
||||
}
|
||||
|
||||
type cacheWarning struct {
|
||||
agent string
|
||||
text string
|
||||
}
|
||||
|
||||
type cachedTokenAuthenticator struct {
|
||||
authenticator authenticator.Token
|
||||
|
||||
cacheErrs bool
|
||||
successTTL time.Duration
|
||||
failureTTL time.Duration
|
||||
|
||||
cache cache
|
||||
group singleflight.Group
|
||||
|
||||
// hashPool is a per authenticator pool of hash.Hash (to avoid allocations from building the Hash)
|
||||
// HMAC with SHA-256 and a random key is used to prevent precomputation and length extension attacks
|
||||
// It also mitigates hash map DOS attacks via collisions (the inputs are supplied by untrusted users)
|
||||
hashPool *sync.Pool
|
||||
}
|
||||
|
||||
type cache interface {
|
||||
// given a key, return the record, and whether or not it existed
|
||||
get(key string) (value *cacheRecord, exists bool)
|
||||
// caches the record for the key
|
||||
set(key string, value *cacheRecord, ttl time.Duration)
|
||||
// removes the record for the key
|
||||
remove(key string)
|
||||
}
|
||||
|
||||
// New returns a token authenticator that caches the results of the specified authenticator. A ttl of 0 bypasses the cache.
|
||||
func New(authenticator authenticator.Token, cacheErrs bool, successTTL, failureTTL time.Duration) authenticator.Token {
|
||||
return newWithClock(authenticator, cacheErrs, successTTL, failureTTL, clock.RealClock{})
|
||||
}
|
||||
|
||||
func newWithClock(authenticator authenticator.Token, cacheErrs bool, successTTL, failureTTL time.Duration, clock clock.Clock) authenticator.Token {
|
||||
randomCacheKey := make([]byte, 32)
|
||||
if _, err := rand.Read(randomCacheKey); err != nil {
|
||||
panic(err) // rand should never fail
|
||||
}
|
||||
|
||||
return &cachedTokenAuthenticator{
|
||||
authenticator: authenticator,
|
||||
cacheErrs: cacheErrs,
|
||||
successTTL: successTTL,
|
||||
failureTTL: failureTTL,
|
||||
// Cache performance degrades noticeably when the number of
|
||||
// tokens in operation exceeds the size of the cache. It is
|
||||
// cheap to make the cache big in the second dimension below,
|
||||
// the memory is only consumed when that many tokens are being
|
||||
// used. Currently we advertise support 5k nodes and 10k
|
||||
// namespaces; a 32k entry cache is therefore a 2x safety
|
||||
// margin.
|
||||
cache: newStripedCache(32, fnvHashFunc, func() cache { return newSimpleCache(clock) }),
|
||||
|
||||
hashPool: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return hmac.New(sha256.New, randomCacheKey)
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// AuthenticateToken implements authenticator.Token
|
||||
func (a *cachedTokenAuthenticator) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) {
|
||||
record := a.doAuthenticateToken(ctx, token)
|
||||
if !record.ok || record.err != nil {
|
||||
return nil, false, record.err
|
||||
}
|
||||
for key, value := range record.annotations {
|
||||
audit.AddAuditAnnotation(ctx, key, value)
|
||||
}
|
||||
for _, w := range record.warnings {
|
||||
warning.AddWarning(ctx, w.agent, w.text)
|
||||
}
|
||||
return record.resp, true, nil
|
||||
}
|
||||
|
||||
func (a *cachedTokenAuthenticator) doAuthenticateToken(ctx context.Context, token string) *cacheRecord {
|
||||
doneAuthenticating := stats.authenticating(ctx)
|
||||
|
||||
auds, audsOk := authenticator.AudiencesFrom(ctx)
|
||||
|
||||
key := keyFunc(a.hashPool, auds, token)
|
||||
if record, ok := a.cache.get(key); ok {
|
||||
// Record cache hit
|
||||
doneAuthenticating(true)
|
||||
return record
|
||||
}
|
||||
|
||||
// Record cache miss
|
||||
doneBlocking := stats.blocking(ctx)
|
||||
defer doneBlocking()
|
||||
defer doneAuthenticating(false)
|
||||
|
||||
c := a.group.DoChan(key, func() (val interface{}, _ error) {
|
||||
// always use one place to read and write the output of AuthenticateToken
|
||||
record := &cacheRecord{}
|
||||
|
||||
doneFetching := stats.fetching(ctx)
|
||||
// We're leaving the request handling stack so we need to handle crashes
|
||||
// ourselves. Log a stack trace and return a 500 if something panics.
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// make sure to always return a record
|
||||
record.err = errAuthnCrash
|
||||
val = record
|
||||
|
||||
// Same as stdlib http server code. Manually allocate stack
|
||||
// trace buffer size to prevent excessively large logs
|
||||
const size = 64 << 10
|
||||
buf := make([]byte, size)
|
||||
buf = buf[:runtime.Stack(buf, false)]
|
||||
klog.Errorf("%v\n%s", r, buf)
|
||||
}
|
||||
doneFetching(record.err == nil)
|
||||
}()
|
||||
|
||||
// Check again for a cached record. We may have raced with a fetch.
|
||||
if record, ok := a.cache.get(key); ok {
|
||||
return record, nil
|
||||
}
|
||||
|
||||
// Detach the context because the lookup may be shared by multiple callers,
|
||||
// however propagate the audience.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), sharedLookupTimeout)
|
||||
defer cancel()
|
||||
|
||||
if audsOk {
|
||||
ctx = authenticator.WithAudiences(ctx, auds)
|
||||
}
|
||||
recorder := &recorder{}
|
||||
ctx = warning.WithWarningRecorder(ctx, recorder)
|
||||
|
||||
ctx = audit.WithAuditContext(ctx)
|
||||
ac := audit.AuditContextFrom(ctx)
|
||||
// since this is shared work between multiple requests, we have no way of knowing if any
|
||||
// particular request supports audit annotations. thus we always attempt to record them.
|
||||
ac.Event.Level = auditinternal.LevelMetadata
|
||||
|
||||
record.resp, record.ok, record.err = a.authenticator.AuthenticateToken(ctx, token)
|
||||
record.annotations = ac.Event.Annotations
|
||||
record.warnings = recorder.extractWarnings()
|
||||
|
||||
if !a.cacheErrs && record.err != nil {
|
||||
return record, nil
|
||||
}
|
||||
|
||||
switch {
|
||||
case record.ok && a.successTTL > 0:
|
||||
a.cache.set(key, record, a.successTTL)
|
||||
case !record.ok && a.failureTTL > 0:
|
||||
a.cache.set(key, record, a.failureTTL)
|
||||
}
|
||||
|
||||
return record, nil
|
||||
})
|
||||
|
||||
select {
|
||||
case result := <-c:
|
||||
// we always set Val and never set Err
|
||||
return result.Val.(*cacheRecord)
|
||||
case <-ctx.Done():
|
||||
// fake a record on context cancel
|
||||
return &cacheRecord{err: ctx.Err()}
|
||||
}
|
||||
}
|
||||
|
||||
// keyFunc generates a string key by hashing the inputs.
|
||||
// This lowers the memory requirement of the cache and keeps tokens out of memory.
|
||||
func keyFunc(hashPool *sync.Pool, auds []string, token string) string {
|
||||
h := hashPool.Get().(hash.Hash)
|
||||
|
||||
h.Reset()
|
||||
|
||||
// try to force stack allocation
|
||||
var a [4]byte
|
||||
b := a[:]
|
||||
|
||||
writeLengthPrefixedString(h, b, token)
|
||||
// encode the length of audiences to avoid ambiguities
|
||||
writeLength(h, b, len(auds))
|
||||
for _, aud := range auds {
|
||||
writeLengthPrefixedString(h, b, aud)
|
||||
}
|
||||
|
||||
key := toString(h.Sum(nil)) // skip base64 encoding to save an allocation
|
||||
|
||||
hashPool.Put(h)
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
// writeLengthPrefixedString writes s with a length prefix to prevent ambiguities, i.e. "xy" + "z" == "x" + "yz"
|
||||
// the length of b is assumed to be 4 (b is mutated by this function to store the length of s)
|
||||
func writeLengthPrefixedString(w io.Writer, b []byte, s string) {
|
||||
writeLength(w, b, len(s))
|
||||
if _, err := w.Write(toBytes(s)); err != nil {
|
||||
panic(err) // Write() on hash never fails
|
||||
}
|
||||
}
|
||||
|
||||
// writeLength encodes length into b and then writes it via the given writer
|
||||
// the length of b is assumed to be 4
|
||||
func writeLength(w io.Writer, b []byte, length int) {
|
||||
binary.BigEndian.PutUint32(b, uint32(length))
|
||||
if _, err := w.Write(b); err != nil {
|
||||
panic(err) // Write() on hash never fails
|
||||
}
|
||||
}
|
||||
|
||||
// toBytes performs unholy acts to avoid allocations
|
||||
func toBytes(s string) []byte {
|
||||
// unsafe.StringData is unspecified for the empty string, so we provide a strict interpretation
|
||||
if len(s) == 0 {
|
||||
return nil
|
||||
}
|
||||
// Copied from go 1.20.1 os.File.WriteString
|
||||
// https://github.com/golang/go/blob/202a1a57064127c3f19d96df57b9f9586145e21c/src/os/file.go#L246
|
||||
return unsafe.Slice(unsafe.StringData(s), len(s))
|
||||
}
|
||||
|
||||
// toString performs unholy acts to avoid allocations
|
||||
func toString(b []byte) string {
|
||||
// unsafe.SliceData relies on cap whereas we want to rely on len
|
||||
if len(b) == 0 {
|
||||
return ""
|
||||
}
|
||||
// Copied from go 1.20.1 strings.Builder.String
|
||||
// https://github.com/golang/go/blob/202a1a57064127c3f19d96df57b9f9586145e21c/src/strings/builder.go#L48
|
||||
return unsafe.String(unsafe.SliceData(b), len(b))
|
||||
}
|
||||
|
||||
// simple recorder that only appends warning
|
||||
type recorder struct {
|
||||
mu sync.Mutex
|
||||
warnings []*cacheWarning
|
||||
}
|
||||
|
||||
// AddWarning adds a warning to recorder.
|
||||
func (r *recorder) AddWarning(agent, text string) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.warnings = append(r.warnings, &cacheWarning{agent: agent, text: text})
|
||||
}
|
||||
|
||||
func (r *recorder) extractWarnings() []*cacheWarning {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
warnings := r.warnings
|
||||
r.warnings = nil
|
||||
return warnings
|
||||
}
|
126
e2e/vendor/k8s.io/apiserver/pkg/authentication/token/cache/stats.go
generated
vendored
126
e2e/vendor/k8s.io/apiserver/pkg/authentication/token/cache/stats.go
generated
vendored
@ -1,126 +0,0 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"k8s.io/component-base/metrics"
|
||||
"k8s.io/component-base/metrics/legacyregistry"
|
||||
)
|
||||
|
||||
var (
|
||||
requestLatency = metrics.NewHistogramVec(
|
||||
&metrics.HistogramOpts{
|
||||
Namespace: "authentication",
|
||||
Subsystem: "token_cache",
|
||||
Name: "request_duration_seconds",
|
||||
StabilityLevel: metrics.ALPHA,
|
||||
},
|
||||
[]string{"status"},
|
||||
)
|
||||
requestCount = metrics.NewCounterVec(
|
||||
&metrics.CounterOpts{
|
||||
Namespace: "authentication",
|
||||
Subsystem: "token_cache",
|
||||
Name: "request_total",
|
||||
StabilityLevel: metrics.ALPHA,
|
||||
},
|
||||
[]string{"status"},
|
||||
)
|
||||
fetchCount = metrics.NewCounterVec(
|
||||
&metrics.CounterOpts{
|
||||
Namespace: "authentication",
|
||||
Subsystem: "token_cache",
|
||||
Name: "fetch_total",
|
||||
StabilityLevel: metrics.ALPHA,
|
||||
},
|
||||
[]string{"status"},
|
||||
)
|
||||
activeFetchCount = metrics.NewGaugeVec(
|
||||
&metrics.GaugeOpts{
|
||||
Namespace: "authentication",
|
||||
Subsystem: "token_cache",
|
||||
Name: "active_fetch_count",
|
||||
StabilityLevel: metrics.ALPHA,
|
||||
},
|
||||
[]string{"status"},
|
||||
)
|
||||
)
|
||||
|
||||
func init() {
|
||||
legacyregistry.MustRegister(
|
||||
requestLatency,
|
||||
requestCount,
|
||||
fetchCount,
|
||||
activeFetchCount,
|
||||
)
|
||||
}
|
||||
|
||||
const (
|
||||
hitTag = "hit"
|
||||
missTag = "miss"
|
||||
|
||||
fetchFailedTag = "error"
|
||||
fetchOkTag = "ok"
|
||||
|
||||
fetchInFlightTag = "in_flight"
|
||||
fetchBlockedTag = "blocked"
|
||||
)
|
||||
|
||||
type statsCollector struct{}
|
||||
|
||||
var stats = statsCollector{}
|
||||
|
||||
func (statsCollector) authenticating(ctx context.Context) func(hit bool) {
|
||||
start := time.Now()
|
||||
return func(hit bool) {
|
||||
var tag string
|
||||
if hit {
|
||||
tag = hitTag
|
||||
} else {
|
||||
tag = missTag
|
||||
}
|
||||
|
||||
latency := time.Since(start)
|
||||
|
||||
requestCount.WithContext(ctx).WithLabelValues(tag).Inc()
|
||||
requestLatency.WithContext(ctx).WithLabelValues(tag).Observe(float64(latency.Milliseconds()) / 1000)
|
||||
}
|
||||
}
|
||||
|
||||
func (statsCollector) blocking(ctx context.Context) func() {
|
||||
activeFetchCount.WithContext(ctx).WithLabelValues(fetchBlockedTag).Inc()
|
||||
return activeFetchCount.WithContext(ctx).WithLabelValues(fetchBlockedTag).Dec
|
||||
}
|
||||
|
||||
func (statsCollector) fetching(ctx context.Context) func(ok bool) {
|
||||
activeFetchCount.WithContext(ctx).WithLabelValues(fetchInFlightTag).Inc()
|
||||
return func(ok bool) {
|
||||
var tag string
|
||||
if ok {
|
||||
tag = fetchOkTag
|
||||
} else {
|
||||
tag = fetchFailedTag
|
||||
}
|
||||
|
||||
fetchCount.WithContext(ctx).WithLabelValues(tag).Inc()
|
||||
|
||||
activeFetchCount.WithContext(ctx).WithLabelValues(fetchInFlightTag).Dec()
|
||||
}
|
||||
}
|
99
e2e/vendor/k8s.io/apiserver/pkg/authentication/token/tokenfile/tokenfile.go
generated
vendored
99
e2e/vendor/k8s.io/apiserver/pkg/authentication/token/tokenfile/tokenfile.go
generated
vendored
@ -1,99 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package tokenfile
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
type TokenAuthenticator struct {
|
||||
tokens map[string]*user.DefaultInfo
|
||||
}
|
||||
|
||||
// New returns a TokenAuthenticator for a single token
|
||||
func New(tokens map[string]*user.DefaultInfo) *TokenAuthenticator {
|
||||
return &TokenAuthenticator{
|
||||
tokens: tokens,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCSV returns a TokenAuthenticator, populated from a CSV file.
|
||||
// The CSV file must contain records in the format "token,username,useruid"
|
||||
func NewCSV(path string) (*TokenAuthenticator, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
recordNum := 0
|
||||
tokens := make(map[string]*user.DefaultInfo)
|
||||
reader := csv.NewReader(file)
|
||||
reader.FieldsPerRecord = -1
|
||||
for {
|
||||
record, err := reader.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(record) < 3 {
|
||||
return nil, fmt.Errorf("token file '%s' must have at least 3 columns (token, user name, user uid), found %d", path, len(record))
|
||||
}
|
||||
|
||||
recordNum++
|
||||
if record[0] == "" {
|
||||
klog.Warningf("empty token has been found in token file '%s', record number '%d'", path, recordNum)
|
||||
continue
|
||||
}
|
||||
|
||||
obj := &user.DefaultInfo{
|
||||
Name: record[1],
|
||||
UID: record[2],
|
||||
}
|
||||
if _, exist := tokens[record[0]]; exist {
|
||||
klog.Warningf("duplicate token has been found in token file '%s', record number '%d'", path, recordNum)
|
||||
}
|
||||
tokens[record[0]] = obj
|
||||
|
||||
if len(record) >= 4 {
|
||||
obj.Groups = strings.Split(record[3], ",")
|
||||
}
|
||||
}
|
||||
|
||||
return &TokenAuthenticator{
|
||||
tokens: tokens,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *TokenAuthenticator) AuthenticateToken(ctx context.Context, value string) (*authenticator.Response, bool, error) {
|
||||
user, ok := a.tokens[value]
|
||||
if !ok {
|
||||
return nil, false, nil
|
||||
}
|
||||
return &authenticator.Response{User: user}, true, nil
|
||||
}
|
Reference in New Issue
Block a user