vendor dependencies

This commit is contained in:
Sergiusz Urbaniak 2019-04-24 11:06:03 +02:00
parent 604208ef4f
commit 72abf135d6
1156 changed files with 78178 additions and 105799 deletions

View file

@ -0,0 +1,90 @@
/*
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,
}
}

View file

@ -0,0 +1,63 @@
/*
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
}

View file

@ -17,52 +17,64 @@ 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
// information about the current user and true if successful, false if not successful,
// or an error if the token could not be checked.
// 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(token string) (user.Info, bool, error)
AuthenticateToken(ctx context.Context, token string) (*Response, bool, error)
}
// Request attempts to extract authentication information from a request and returns
// information about the current user and true if successful, false if not successful,
// or an error if the request could not be checked.
// 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) (user.Info, bool, error)
AuthenticateRequest(req *http.Request) (*Response, bool, error)
}
// Password checks a username and password against a backing authentication store and
// returns information about the user and true if successful, false if not successful,
// or an error if the username and password could not be checked
// Password checks a username and password against a backing authentication
// store and returns a Response or an error if the password could not be
// checked.
type Password interface {
AuthenticatePassword(user, password string) (user.Info, bool, error)
AuthenticatePassword(ctx context.Context, user, password string) (*Response, bool, error)
}
// TokenFunc is a function that implements the Token interface.
type TokenFunc func(token string) (user.Info, bool, error)
type TokenFunc func(ctx context.Context, token string) (*Response, bool, error)
// AuthenticateToken implements authenticator.Token.
func (f TokenFunc) AuthenticateToken(token string) (user.Info, bool, error) {
return f(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) (user.Info, bool, error)
type RequestFunc func(req *http.Request) (*Response, bool, error)
// AuthenticateRequest implements authenticator.Request.
func (f RequestFunc) AuthenticateRequest(req *http.Request) (user.Info, bool, error) {
func (f RequestFunc) AuthenticateRequest(req *http.Request) (*Response, bool, error) {
return f(req)
}
// PasswordFunc is a function that implements the Password interface.
type PasswordFunc func(user, password string) (user.Info, bool, error)
type PasswordFunc func(ctx context.Context, user, password string) (*Response, bool, error)
// AuthenticatePassword implements authenticator.Password.
func (f PasswordFunc) AuthenticatePassword(user, password string) (user.Info, bool, error) {
return f(user, password)
func (f PasswordFunc) AuthenticatePassword(ctx context.Context, user, password string) (*Response, bool, error) {
return f(ctx, user, password)
}
// 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
}

View file

@ -31,6 +31,7 @@ import (
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"
webhooktoken "k8s.io/apiserver/plugin/pkg/authenticator/token/webhook"
authenticationclient "k8s.io/client-go/kubernetes/typed/authentication/v1beta1"
"k8s.io/client-go/util/cert"
@ -41,6 +42,7 @@ import (
type DelegatingAuthenticatorConfig struct {
Anonymous bool
// TokenAccessReviewClient is a client to do token review. It can be nil. Then every token is ignored.
TokenAccessReviewClient authenticationclient.TokenReviewInterface
// CacheTTL is the length of time that a token authentication answer will be cached.
@ -49,6 +51,8 @@ type DelegatingAuthenticatorConfig struct {
// ClientCAFile is the CA bundle file used to authenticate client certificates
ClientCAFile string
APIAudiences authenticator.Audiences
RequestHeaderConfig *RequestHeaderConfig
}
@ -84,11 +88,12 @@ func (c DelegatingAuthenticatorConfig) New() (authenticator.Request, *spec.Secur
}
if c.TokenAccessReviewClient != nil {
tokenAuth, err := webhooktoken.NewFromInterface(c.TokenAccessReviewClient, c.CacheTTL)
tokenAuth, err := webhooktoken.NewFromInterface(c.TokenAccessReviewClient, c.APIAudiences)
if err != nil {
return nil, nil, err
}
authenticators = append(authenticators, bearertoken.New(tokenAuth), websocket.NewProtocolAuthenticator(tokenAuth))
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{

View file

@ -36,25 +36,26 @@ func NewAuthenticatedGroupAdder(auth authenticator.Request) authenticator.Reques
return &AuthenticatedGroupAdder{auth}
}
func (g *AuthenticatedGroupAdder) AuthenticateRequest(req *http.Request) (user.Info, bool, error) {
u, ok, err := g.Authenticator.AuthenticateRequest(req)
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 u.GetName() == user.Anonymous {
return u, true, nil
if r.User.GetName() == user.Anonymous {
return r, true, nil
}
for _, group := range u.GetGroups() {
for _, group := range r.User.GetGroups() {
if group == user.AllAuthenticated || group == user.AllUnauthenticated {
return u, true, nil
return r, true, nil
}
}
return &user.DefaultInfo{
Name: u.GetName(),
UID: u.GetUID(),
Groups: append(u.GetGroups(), user.AllAuthenticated),
Extra: u.GetExtra(),
}, true, nil
r.User = &user.DefaultInfo{
Name: r.User.GetName(),
UID: r.User.GetUID(),
Groups: append(r.User.GetGroups(), user.AllAuthenticated),
Extra: r.User.GetExtra(),
}
return r, true, nil
}

View file

@ -36,15 +36,16 @@ func NewGroupAdder(auth authenticator.Request, groups []string) authenticator.Re
return &GroupAdder{auth, groups}
}
func (g *GroupAdder) AuthenticateRequest(req *http.Request) (user.Info, bool, error) {
u, ok, err := g.Authenticator.AuthenticateRequest(req)
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
}
return &user.DefaultInfo{
Name: u.GetName(),
UID: u.GetUID(),
Groups: append(u.GetGroups(), g.Groups...),
Extra: u.GetExtra(),
}, true, nil
r.User = &user.DefaultInfo{
Name: r.User.GetName(),
UID: r.User.GetUID(),
Groups: append(r.User.GetGroups(), g.Groups...),
Extra: r.User.GetExtra(),
}
return r, true, nil
}

View file

@ -17,6 +17,8 @@ limitations under the License.
package group
import (
"context"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/user"
)
@ -34,15 +36,16 @@ func NewTokenGroupAdder(auth authenticator.Token, groups []string) authenticator
return &TokenGroupAdder{auth, groups}
}
func (g *TokenGroupAdder) AuthenticateToken(token string) (user.Info, bool, error) {
u, ok, err := g.Authenticator.AuthenticateToken(token)
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
}
return &user.DefaultInfo{
Name: u.GetName(),
UID: u.GetUID(),
Groups: append(u.GetGroups(), g.Groups...),
Extra: u.GetExtra(),
}, true, nil
r.User = &user.DefaultInfo{
Name: r.User.GetName(),
UID: r.User.GetUID(),
Groups: append(r.User.GetGroups(), g.Groups...),
Extra: r.User.GetExtra(),
}
return r, true, nil
}

View file

@ -30,7 +30,14 @@ const (
)
func NewAuthenticator() authenticator.Request {
return authenticator.RequestFunc(func(req *http.Request) (user.Info, bool, error) {
return &user.DefaultInfo{Name: anonymousUser, Groups: []string{unauthenticatedGroup}}, true, nil
return authenticator.RequestFunc(func(req *http.Request) (*authenticator.Response, bool, error) {
auds, _ := authenticator.AudiencesFrom(req.Context())
return &authenticator.Response{
User: &user.DefaultInfo{
Name: anonymousUser,
Groups: []string{unauthenticatedGroup},
},
Audiences: auds,
}, true, nil
})
}

View file

@ -22,7 +22,6 @@ import (
"strings"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/user"
)
type Authenticator struct {
@ -35,7 +34,7 @@ func New(auth authenticator.Token) *Authenticator {
var invalidToken = errors.New("invalid bearer token")
func (a *Authenticator) AuthenticateRequest(req *http.Request) (user.Info, bool, error) {
func (a *Authenticator) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {
auth := strings.TrimSpace(req.Header.Get("Authorization"))
if auth == "" {
return nil, false, nil
@ -52,7 +51,7 @@ func (a *Authenticator) AuthenticateRequest(req *http.Request) (user.Info, bool,
return nil, false, nil
}
user, ok, err := a.auth.AuthenticateToken(token)
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 {
@ -64,5 +63,5 @@ func (a *Authenticator) AuthenticateRequest(req *http.Request) (user.Info, bool,
err = invalidToken
}
return user, ok, err
return resp, ok, err
}

View file

@ -105,7 +105,7 @@ func NewSecure(clientCA string, proxyClientNames []string, nameHeaders []string,
return x509request.NewVerifier(opts, headerAuthenticator, sets.NewString(proxyClientNames...)), nil
}
func (a *requestHeaderAuthRequestHandler) AuthenticateRequest(req *http.Request) (user.Info, bool, error) {
func (a *requestHeaderAuthRequestHandler) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {
name := headerValue(req.Header, a.nameHeaders)
if len(name) == 0 {
return nil, false, nil
@ -126,10 +126,12 @@ func (a *requestHeaderAuthRequestHandler) AuthenticateRequest(req *http.Request)
}
}
return &user.DefaultInfo{
Name: name,
Groups: groups,
Extra: extra,
return &authenticator.Response{
User: &user.DefaultInfo{
Name: name,
Groups: groups,
Extra: extra,
},
}, true, nil
}

View file

@ -21,7 +21,6 @@ import (
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/user"
)
// unionAuthRequestHandler authenticates requests using a chain of authenticator.Requests
@ -51,20 +50,20 @@ func NewFailOnError(authRequestHandlers ...authenticator.Request) authenticator.
}
// AuthenticateRequest authenticates the request using a chain of authenticator.Request objects.
func (authHandler *unionAuthRequestHandler) AuthenticateRequest(req *http.Request) (user.Info, bool, error) {
func (authHandler *unionAuthRequestHandler) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {
var errlist []error
for _, currAuthRequestHandler := range authHandler.Handlers {
info, ok, err := currAuthRequestHandler.AuthenticateRequest(req)
resp, ok, err := currAuthRequestHandler.AuthenticateRequest(req)
if err != nil {
if authHandler.FailOnError {
return info, ok, err
return resp, ok, err
}
errlist = append(errlist, err)
continue
}
if ok {
return info, ok, err
return resp, ok, err
}
}

View file

@ -25,7 +25,6 @@ import (
"unicode/utf8"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/util/wsstream"
)
@ -46,7 +45,7 @@ func NewProtocolAuthenticator(auth authenticator.Token) *ProtocolAuthenticator {
return &ProtocolAuthenticator{auth}
}
func (a *ProtocolAuthenticator) AuthenticateRequest(req *http.Request) (user.Info, bool, error) {
func (a *ProtocolAuthenticator) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {
// Only accept websocket connections
if !wsstream.IsWebSocketRequest(req) {
return nil, false, nil
@ -91,7 +90,7 @@ func (a *ProtocolAuthenticator) AuthenticateRequest(req *http.Request) (user.Inf
return nil, false, nil
}
user, ok, err := a.auth.AuthenticateToken(token)
resp, ok, err := a.auth.AuthenticateToken(req.Context(), token)
// on success, remove the protocol with the token
if ok {
@ -105,5 +104,5 @@ func (a *ProtocolAuthenticator) AuthenticateRequest(req *http.Request) (user.Inf
err = errInvalidToken
}
return user, ok, err
return resp, ok, err
}

View file

@ -0,0 +1,9 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- sig-auth-certificates-approvers
reviewers:
- sig-auth-certificates-reviewers
labels:
- sig/auth

View file

@ -19,12 +19,10 @@ package x509
import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"fmt"
"net/http"
"time"
"github.com/golang/glog"
"github.com/prometheus/client_golang/prometheus"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
@ -41,6 +39,9 @@ var clientCertificateExpirationHistogram = prometheus.NewHistogram(
Help: "Distribution of the remaining lifetime on the certificate used to authenticate a request.",
Buckets: []float64{
0,
(30 * time.Minute).Seconds(),
(1 * time.Hour).Seconds(),
(2 * time.Hour).Seconds(),
(6 * time.Hour).Seconds(),
(12 * time.Hour).Seconds(),
(24 * time.Hour).Seconds(),
@ -61,14 +62,14 @@ func init() {
// UserConversion defines an interface for extracting user info from a client certificate chain
type UserConversion interface {
User(chain []*x509.Certificate) (user.Info, bool, error)
User(chain []*x509.Certificate) (*authenticator.Response, bool, error)
}
// UserConversionFunc is a function that implements the UserConversion interface.
type UserConversionFunc func(chain []*x509.Certificate) (user.Info, bool, error)
type UserConversionFunc func(chain []*x509.Certificate) (*authenticator.Response, bool, error)
// User implements x509.UserConversion
func (f UserConversionFunc) User(chain []*x509.Certificate) (user.Info, bool, error) {
func (f UserConversionFunc) User(chain []*x509.Certificate) (*authenticator.Response, bool, error) {
return f(chain)
}
@ -85,7 +86,7 @@ func New(opts x509.VerifyOptions, user UserConversion) *Authenticator {
}
// AuthenticateRequest authenticates the request using presented client certificates
func (a *Authenticator) AuthenticateRequest(req *http.Request) (user.Info, bool, error) {
func (a *Authenticator) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {
if req.TLS == nil || len(req.TLS.PeerCertificates) == 0 {
return nil, false, nil
}
@ -137,7 +138,7 @@ func NewVerifier(opts x509.VerifyOptions, auth authenticator.Request, allowedCom
}
// AuthenticateRequest verifies the presented client certificate, then delegates to the wrapped auth
func (a *Verifier) AuthenticateRequest(req *http.Request) (user.Info, bool, error) {
func (a *Verifier) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {
if req.TLS == nil || len(req.TLS.PeerCertificates) == 0 {
return nil, false, nil
}
@ -169,8 +170,7 @@ func (a *Verifier) verifySubject(subject pkix.Name) error {
if a.allowedCommonNames.Has(subject.CommonName) {
return nil
}
glog.Warningf("x509: subject with cn=%s is not in the allowed list: %v", subject.CommonName, a.allowedCommonNames.List())
return fmt.Errorf("x509: subject with cn=%s is not allowed", subject.CommonName)
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,
@ -182,34 +182,14 @@ func DefaultVerifyOptions() x509.VerifyOptions {
}
// CommonNameUserConversion builds user info from a certificate chain using the subject's CommonName
var CommonNameUserConversion = UserConversionFunc(func(chain []*x509.Certificate) (user.Info, bool, error) {
var CommonNameUserConversion = UserConversionFunc(func(chain []*x509.Certificate) (*authenticator.Response, bool, error) {
if len(chain[0].Subject.CommonName) == 0 {
return nil, false, nil
}
return &user.DefaultInfo{
Name: chain[0].Subject.CommonName,
Groups: chain[0].Subject.Organization,
return &authenticator.Response{
User: &user.DefaultInfo{
Name: chain[0].Subject.CommonName,
Groups: chain[0].Subject.Organization,
},
}, true, nil
})
// DNSNameUserConversion builds user info from a certificate chain using the first DNSName on the certificate
var DNSNameUserConversion = UserConversionFunc(func(chain []*x509.Certificate) (user.Info, bool, error) {
if len(chain[0].DNSNames) == 0 {
return nil, false, nil
}
return &user.DefaultInfo{Name: chain[0].DNSNames[0]}, true, nil
})
// EmailAddressUserConversion builds user info from a certificate chain using the first EmailAddress on the certificate
var EmailAddressUserConversion = UserConversionFunc(func(chain []*x509.Certificate) (user.Info, bool, error) {
var emailAddressOID asn1.ObjectIdentifier = []int{1, 2, 840, 113549, 1, 9, 1}
if len(chain[0].EmailAddresses) == 0 {
for _, name := range chain[0].Subject.Names {
if name.Type.Equal(emailAddressOID) {
return &user.DefaultInfo{Name: name.Value.(string)}, true, nil
}
}
return nil, false, nil
}
return &user.DefaultInfo{Name: chain[0].EmailAddresses[0]}, true, nil
})

View file

@ -0,0 +1,49 @@
/*
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"
lrucache "k8s.io/apimachinery/pkg/util/cache"
"k8s.io/apimachinery/pkg/util/clock"
)
type simpleCache struct {
lru *lrucache.LRUExpireCache
}
func newSimpleCache(size int, clock clock.Clock) cache {
return &simpleCache{lru: lrucache.NewLRUExpireCacheWithClock(size, clock)}
}
func (c *simpleCache) get(key string) (*cacheRecord, bool) {
record, ok := c.lru.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.lru.Add(key, value, ttl)
}
func (c *simpleCache) remove(key string) {
c.lru.Remove(key)
}

View file

@ -0,0 +1,60 @@
/*
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()
}

View file

@ -0,0 +1,95 @@
/*
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"
"fmt"
"time"
utilclock "k8s.io/apimachinery/pkg/util/clock"
"k8s.io/apiserver/pkg/authentication/authenticator"
)
// cacheRecord holds the three return values of the authenticator.Token AuthenticateToken method
type cacheRecord struct {
resp *authenticator.Response
ok bool
err error
}
type cachedTokenAuthenticator struct {
authenticator authenticator.Token
cacheErrs bool
successTTL time.Duration
failureTTL time.Duration
cache cache
}
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, utilclock.RealClock{})
}
func newWithClock(authenticator authenticator.Token, cacheErrs bool, successTTL, failureTTL time.Duration, clock utilclock.Clock) authenticator.Token {
return &cachedTokenAuthenticator{
authenticator: authenticator,
cacheErrs: cacheErrs,
successTTL: successTTL,
failureTTL: failureTTL,
cache: newStripedCache(32, fnvHashFunc, func() cache { return newSimpleCache(128, clock) }),
}
}
// AuthenticateToken implements authenticator.Token
func (a *cachedTokenAuthenticator) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) {
auds, _ := authenticator.AudiencesFrom(ctx)
key := keyFunc(auds, token)
if record, ok := a.cache.get(key); ok {
return record.resp, record.ok, record.err
}
resp, ok, err := a.authenticator.AuthenticateToken(ctx, token)
if !a.cacheErrs && err != nil {
return resp, ok, err
}
switch {
case ok && a.successTTL > 0:
a.cache.set(key, &cacheRecord{resp: resp, ok: ok, err: err}, a.successTTL)
case !ok && a.failureTTL > 0:
a.cache.set(key, &cacheRecord{resp: resp, ok: ok, err: err}, a.failureTTL)
}
return resp, ok, err
}
func keyFunc(auds []string, token string) string {
return fmt.Sprintf("%#v|%v", auds, token)
}

View file

@ -17,14 +17,16 @@ limitations under the License.
package tokenfile
import (
"context"
"encoding/csv"
"fmt"
"io"
"os"
"strings"
"github.com/golang/glog"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/klog"
)
type TokenAuthenticator struct {
@ -65,7 +67,7 @@ func NewCSV(path string) (*TokenAuthenticator, error) {
recordNum++
if record[0] == "" {
glog.Warningf("empty token has been found in token file '%s', record number '%d'", path, recordNum)
klog.Warningf("empty token has been found in token file '%s', record number '%d'", path, recordNum)
continue
}
@ -74,7 +76,7 @@ func NewCSV(path string) (*TokenAuthenticator, error) {
UID: record[2],
}
if _, exist := tokens[record[0]]; exist {
glog.Warningf("duplicate token has been found in token file '%s', record number '%d'", path, recordNum)
klog.Warningf("duplicate token has been found in token file '%s', record number '%d'", path, recordNum)
}
tokens[record[0]] = obj
@ -88,10 +90,10 @@ func NewCSV(path string) (*TokenAuthenticator, error) {
}, nil
}
func (a *TokenAuthenticator) AuthenticateToken(value string) (user.Info, bool, error) {
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 user, true, nil
return &authenticator.Response{User: user}, true, nil
}

View file

@ -16,4 +16,4 @@ limitations under the License.
// Package user contains utilities for dealing with simple user exchange in the auth
// packages. The user.Info interface defines an interface for exchanging that info.
package user
package user // import "k8s.io/apiserver/pkg/authentication/user"