85
vendor/k8s.io/apiserver/pkg/authentication/token/cache/cached_token_authenticator.go
generated
vendored
85
vendor/k8s.io/apiserver/pkg/authentication/token/cache/cached_token_authenticator.go
generated
vendored
@@ -34,8 +34,11 @@ import (
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
utilclock "k8s.io/apimachinery/pkg/util/clock"
|
||||
auditinternal "k8s.io/apiserver/pkg/apis/audit"
|
||||
"k8s.io/apiserver/pkg/audit"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/klog"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
var errAuthnCrash = apierrors.NewInternalError(errors.New("authentication failed unexpectedly"))
|
||||
@@ -47,6 +50,16 @@ 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
|
||||
}
|
||||
|
||||
type cachedTokenAuthenticator struct {
|
||||
@@ -109,7 +122,18 @@ func newWithClock(authenticator authenticator.Token, cacheErrs bool, successTTL,
|
||||
|
||||
// AuthenticateToken implements authenticator.Token
|
||||
func (a *cachedTokenAuthenticator) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) {
|
||||
doneAuthenticating := stats.authenticating()
|
||||
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)
|
||||
}
|
||||
return record.resp, true, nil
|
||||
}
|
||||
|
||||
func (a *cachedTokenAuthenticator) doAuthenticateToken(ctx context.Context, token string) *cacheRecord {
|
||||
doneAuthenticating := stats.authenticating(ctx)
|
||||
|
||||
auds, audsOk := authenticator.AudiencesFrom(ctx)
|
||||
|
||||
@@ -117,26 +141,27 @@ func (a *cachedTokenAuthenticator) AuthenticateToken(ctx context.Context, token
|
||||
if record, ok := a.cache.get(key); ok {
|
||||
// Record cache hit
|
||||
doneAuthenticating(true)
|
||||
return record.resp, record.ok, record.err
|
||||
return record
|
||||
}
|
||||
|
||||
// Record cache miss
|
||||
doneBlocking := stats.blocking()
|
||||
doneBlocking := stats.blocking(ctx)
|
||||
defer doneBlocking()
|
||||
defer doneAuthenticating(false)
|
||||
|
||||
type lookup struct {
|
||||
resp *authenticator.Response
|
||||
ok bool
|
||||
}
|
||||
c := a.group.DoChan(key, func() (val interface{}, _ error) {
|
||||
// always use one place to read and write the output of AuthenticateToken
|
||||
record := &cacheRecord{}
|
||||
|
||||
c := a.group.DoChan(key, func() (val interface{}, err error) {
|
||||
doneFetching := stats.fetching()
|
||||
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 {
|
||||
err = errAuthnCrash
|
||||
// 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
|
||||
@@ -144,12 +169,12 @@ func (a *cachedTokenAuthenticator) AuthenticateToken(ctx context.Context, token
|
||||
buf = buf[:runtime.Stack(buf, false)]
|
||||
klog.Errorf("%v\n%s", r, buf)
|
||||
}
|
||||
doneFetching(err == nil)
|
||||
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 lookup{record.resp, record.ok}, record.err
|
||||
return record, nil
|
||||
}
|
||||
|
||||
// Detach the context because the lookup may be shared by multiple callers,
|
||||
@@ -161,29 +186,35 @@ func (a *cachedTokenAuthenticator) AuthenticateToken(ctx context.Context, token
|
||||
ctx = authenticator.WithAudiences(ctx, auds)
|
||||
}
|
||||
|
||||
resp, ok, err := a.authenticator.AuthenticateToken(ctx, token)
|
||||
if !a.cacheErrs && err != nil {
|
||||
return nil, err
|
||||
// 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.
|
||||
ev := &auditinternal.Event{Level: auditinternal.LevelMetadata}
|
||||
ctx = request.WithAuditEvent(ctx, ev)
|
||||
|
||||
record.resp, record.ok, record.err = a.authenticator.AuthenticateToken(ctx, token)
|
||||
record.annotations = ev.Annotations
|
||||
|
||||
if !a.cacheErrs && record.err != nil {
|
||||
return record, nil
|
||||
}
|
||||
|
||||
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)
|
||||
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 lookup{resp, ok}, err
|
||||
|
||||
return record, nil
|
||||
})
|
||||
|
||||
select {
|
||||
case result := <-c:
|
||||
if result.Err != nil {
|
||||
return nil, false, result.Err
|
||||
}
|
||||
lookup := result.Val.(lookup)
|
||||
return lookup.resp, lookup.ok, nil
|
||||
// we always set Val and never set Err
|
||||
return result.Val.(*cacheRecord)
|
||||
case <-ctx.Done():
|
||||
return nil, false, ctx.Err()
|
||||
// fake a record on context cancel
|
||||
return &cacheRecord{err: ctx.Err()}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
21
vendor/k8s.io/apiserver/pkg/authentication/token/cache/stats.go
generated
vendored
21
vendor/k8s.io/apiserver/pkg/authentication/token/cache/stats.go
generated
vendored
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"k8s.io/component-base/metrics"
|
||||
@@ -86,7 +87,7 @@ type statsCollector struct{}
|
||||
|
||||
var stats = statsCollector{}
|
||||
|
||||
func (statsCollector) authenticating() func(hit bool) {
|
||||
func (statsCollector) authenticating(ctx context.Context) func(hit bool) {
|
||||
start := time.Now()
|
||||
return func(hit bool) {
|
||||
var tag string
|
||||
@@ -98,18 +99,18 @@ func (statsCollector) authenticating() func(hit bool) {
|
||||
|
||||
latency := time.Since(start)
|
||||
|
||||
requestCount.WithLabelValues(tag).Inc()
|
||||
requestLatency.WithLabelValues(tag).Observe(float64(latency.Milliseconds()) / 1000)
|
||||
requestCount.WithContext(ctx).WithLabelValues(tag).Inc()
|
||||
requestLatency.WithContext(ctx).WithLabelValues(tag).Observe(float64(latency.Milliseconds()) / 1000)
|
||||
}
|
||||
}
|
||||
|
||||
func (statsCollector) blocking() func() {
|
||||
activeFetchCount.WithLabelValues(fetchBlockedTag).Inc()
|
||||
return activeFetchCount.WithLabelValues(fetchBlockedTag).Dec
|
||||
func (statsCollector) blocking(ctx context.Context) func() {
|
||||
activeFetchCount.WithContext(ctx).WithLabelValues(fetchBlockedTag).Inc()
|
||||
return activeFetchCount.WithContext(ctx).WithLabelValues(fetchBlockedTag).Dec
|
||||
}
|
||||
|
||||
func (statsCollector) fetching() func(ok bool) {
|
||||
activeFetchCount.WithLabelValues(fetchInFlightTag).Inc()
|
||||
func (statsCollector) fetching(ctx context.Context) func(ok bool) {
|
||||
activeFetchCount.WithContext(ctx).WithLabelValues(fetchInFlightTag).Inc()
|
||||
return func(ok bool) {
|
||||
var tag string
|
||||
if ok {
|
||||
@@ -118,8 +119,8 @@ func (statsCollector) fetching() func(ok bool) {
|
||||
tag = fetchFailedTag
|
||||
}
|
||||
|
||||
fetchCount.WithLabelValues(tag).Inc()
|
||||
fetchCount.WithContext(ctx).WithLabelValues(tag).Inc()
|
||||
|
||||
activeFetchCount.WithLabelValues(fetchInFlightTag).Dec()
|
||||
activeFetchCount.WithContext(ctx).WithLabelValues(fetchInFlightTag).Dec()
|
||||
}
|
||||
}
|
||||
|
||||
2
vendor/k8s.io/apiserver/pkg/authentication/token/tokenfile/tokenfile.go
generated
vendored
2
vendor/k8s.io/apiserver/pkg/authentication/token/tokenfile/tokenfile.go
generated
vendored
@@ -26,7 +26,7 @@ import (
|
||||
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/klog"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
type TokenAuthenticator struct {
|
||||
|
||||
Reference in New Issue
Block a user