22
vendor/k8s.io/apiserver/plugin/pkg/audit/dynamic/dynamic.go
generated
vendored
22
vendor/k8s.io/apiserver/plugin/pkg/audit/dynamic/dynamic.go
generated
vendored
@@ -123,6 +123,7 @@ func NewBackend(c *Config) (audit.Backend, error) {
|
||||
config: c,
|
||||
delegates: atomic.Value{},
|
||||
delegateUpdateMutex: sync.Mutex{},
|
||||
stopped: false,
|
||||
webhookClientManager: cm,
|
||||
recorder: recorder,
|
||||
}
|
||||
@@ -159,6 +160,7 @@ func NewBackend(c *Config) (audit.Backend, error) {
|
||||
type backend struct {
|
||||
// delegateUpdateMutex holds an update lock on the delegates
|
||||
delegateUpdateMutex sync.Mutex
|
||||
stopped bool
|
||||
config *Config
|
||||
delegates atomic.Value
|
||||
webhookClientManager webhook.ClientManager
|
||||
@@ -201,6 +203,11 @@ func (b *backend) Run(stopCh <-chan struct{}) error {
|
||||
// the primary stopChan to the current delegate map.
|
||||
func (b *backend) stopAllDelegates() {
|
||||
b.delegateUpdateMutex.Lock()
|
||||
defer b.delegateUpdateMutex.Unlock()
|
||||
if b.stopped {
|
||||
return
|
||||
}
|
||||
b.stopped = true
|
||||
for _, d := range b.GetDelegates() {
|
||||
close(d.stopChan)
|
||||
}
|
||||
@@ -237,6 +244,11 @@ func (b *backend) setDelegates(delegates syncedDelegates) {
|
||||
func (b *backend) addSink(sink *auditregv1alpha1.AuditSink) {
|
||||
b.delegateUpdateMutex.Lock()
|
||||
defer b.delegateUpdateMutex.Unlock()
|
||||
if b.stopped {
|
||||
msg := fmt.Sprintf("Could not add audit sink %q uid: %s. Update to all delegates is stopped.", sink.Name, sink.UID)
|
||||
klog.Error(msg)
|
||||
return
|
||||
}
|
||||
delegates := b.copyDelegates()
|
||||
if _, ok := delegates[sink.UID]; ok {
|
||||
klog.Errorf("Audit sink %q uid: %s already exists, could not readd", sink.Name, sink.UID)
|
||||
@@ -262,6 +274,11 @@ func (b *backend) addSink(sink *auditregv1alpha1.AuditSink) {
|
||||
func (b *backend) updateSink(oldSink, newSink *auditregv1alpha1.AuditSink) {
|
||||
b.delegateUpdateMutex.Lock()
|
||||
defer b.delegateUpdateMutex.Unlock()
|
||||
if b.stopped {
|
||||
msg := fmt.Sprintf("Could not update old audit sink %q to new audit sink %q. Update to all delegates is stopped.", oldSink.Name, newSink.Name)
|
||||
klog.Error(msg)
|
||||
return
|
||||
}
|
||||
delegates := b.copyDelegates()
|
||||
oldDelegate, ok := delegates[oldSink.UID]
|
||||
if !ok {
|
||||
@@ -300,6 +317,11 @@ func (b *backend) updateSink(oldSink, newSink *auditregv1alpha1.AuditSink) {
|
||||
func (b *backend) deleteSink(sink *auditregv1alpha1.AuditSink) {
|
||||
b.delegateUpdateMutex.Lock()
|
||||
defer b.delegateUpdateMutex.Unlock()
|
||||
if b.stopped {
|
||||
msg := fmt.Sprintf("Could not delete audit sink %q uid: %s. Update to all delegates is stopped.", sink.Name, sink.UID)
|
||||
klog.Warning(msg)
|
||||
return
|
||||
}
|
||||
delegates := b.copyDelegates()
|
||||
delegate, ok := delegates[sink.UID]
|
||||
if !ok {
|
||||
|
||||
14
vendor/k8s.io/apiserver/plugin/pkg/audit/log/backend.go
generated
vendored
14
vendor/k8s.io/apiserver/plugin/pkg/audit/log/backend.go
generated
vendored
@@ -44,18 +44,18 @@ var AllowedFormats = []string{
|
||||
}
|
||||
|
||||
type backend struct {
|
||||
out io.Writer
|
||||
format string
|
||||
groupVersion schema.GroupVersion
|
||||
out io.Writer
|
||||
format string
|
||||
encoder runtime.Encoder
|
||||
}
|
||||
|
||||
var _ audit.Backend = &backend{}
|
||||
|
||||
func NewBackend(out io.Writer, format string, groupVersion schema.GroupVersion) audit.Backend {
|
||||
return &backend{
|
||||
out: out,
|
||||
format: format,
|
||||
groupVersion: groupVersion,
|
||||
out: out,
|
||||
format: format,
|
||||
encoder: audit.Codecs.LegacyCodec(groupVersion),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func (b *backend) logEvent(ev *auditinternal.Event) bool {
|
||||
case FormatLegacy:
|
||||
line = audit.EventString(ev) + "\n"
|
||||
case FormatJson:
|
||||
bs, err := runtime.Encode(audit.Codecs.LegacyCodec(b.groupVersion), ev)
|
||||
bs, err := runtime.Encode(b.encoder, ev)
|
||||
if err != nil {
|
||||
audit.HandlePluginError(PluginName, err, ev)
|
||||
return false
|
||||
|
||||
24
vendor/k8s.io/apiserver/plugin/pkg/audit/webhook/webhook.go
generated
vendored
24
vendor/k8s.io/apiserver/plugin/pkg/audit/webhook/webhook.go
generated
vendored
@@ -18,6 +18,7 @@ limitations under the License.
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@@ -43,9 +44,27 @@ func init() {
|
||||
install.Install(audit.Scheme)
|
||||
}
|
||||
|
||||
// retryOnError enforces the webhook client to retry requests
|
||||
// on error regardless of its nature.
|
||||
// The default implementation considers a very limited set of
|
||||
// 'retriable' errors, assuming correct use of HTTP codes by
|
||||
// external webhooks.
|
||||
// That may easily lead to dropped audit events. In fact, there is
|
||||
// hardly any error that could be a justified reason NOT to retry
|
||||
// sending audit events if there is even a slight chance that the
|
||||
// receiving service gets back to normal at some point.
|
||||
func retryOnError(err error) bool {
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func loadWebhook(configFile string, groupVersion schema.GroupVersion, initialBackoff time.Duration) (*webhook.GenericWebhook, error) {
|
||||
return webhook.NewGenericWebhook(audit.Scheme, audit.Codecs, configFile,
|
||||
w, err := webhook.NewGenericWebhook(audit.Scheme, audit.Codecs, configFile,
|
||||
[]schema.GroupVersion{groupVersion}, initialBackoff)
|
||||
w.ShouldRetry = retryOnError
|
||||
return w, err
|
||||
}
|
||||
|
||||
type backend struct {
|
||||
@@ -60,6 +79,7 @@ func NewDynamicBackend(rc *rest.RESTClient, initialBackoff time.Duration) audit.
|
||||
w: &webhook.GenericWebhook{
|
||||
RestClient: rc,
|
||||
InitialBackoff: initialBackoff,
|
||||
ShouldRetry: retryOnError,
|
||||
},
|
||||
name: fmt.Sprintf("dynamic_%s", PluginName),
|
||||
}
|
||||
@@ -95,7 +115,7 @@ func (b *backend) processEvents(ev ...*auditinternal.Event) error {
|
||||
for _, e := range ev {
|
||||
list.Items = append(list.Items, *e)
|
||||
}
|
||||
return b.w.WithExponentialBackoff(func() rest.Result {
|
||||
return b.w.WithExponentialBackoff(context.Background(), func() rest.Result {
|
||||
trace := utiltrace.New("Call Audit Events webhook",
|
||||
utiltrace.Field{"name", b.name},
|
||||
utiltrace.Field{"event-count", len(list.Items)})
|
||||
|
||||
131
vendor/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook/webhook.go
generated
vendored
131
vendor/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook/webhook.go
generated
vendored
@@ -20,30 +20,32 @@ package webhook
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
authentication "k8s.io/api/authentication/v1beta1"
|
||||
authenticationv1 "k8s.io/api/authentication/v1"
|
||||
authenticationv1beta1 "k8s.io/api/authentication/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/util/webhook"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
authenticationclient "k8s.io/client-go/kubernetes/typed/authentication/v1beta1"
|
||||
authenticationv1client "k8s.io/client-go/kubernetes/typed/authentication/v1"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
var (
|
||||
groupVersions = []schema.GroupVersion{authentication.SchemeGroupVersion}
|
||||
)
|
||||
|
||||
const retryBackoff = 500 * time.Millisecond
|
||||
|
||||
// Ensure WebhookTokenAuthenticator implements the authenticator.Token interface.
|
||||
var _ authenticator.Token = (*WebhookTokenAuthenticator)(nil)
|
||||
|
||||
type tokenReviewer interface {
|
||||
CreateContext(ctx context.Context, review *authenticationv1.TokenReview) (*authenticationv1.TokenReview, error)
|
||||
}
|
||||
|
||||
type WebhookTokenAuthenticator struct {
|
||||
tokenReview authenticationclient.TokenReviewInterface
|
||||
tokenReview tokenReviewer
|
||||
initialBackoff time.Duration
|
||||
implicitAuds authenticator.Audiences
|
||||
}
|
||||
@@ -52,7 +54,7 @@ type WebhookTokenAuthenticator struct {
|
||||
// client. It is recommend to wrap this authenticator with the token cache
|
||||
// authenticator implemented in
|
||||
// k8s.io/apiserver/pkg/authentication/token/cache.
|
||||
func NewFromInterface(tokenReview authenticationclient.TokenReviewInterface, implicitAuds authenticator.Audiences) (*WebhookTokenAuthenticator, error) {
|
||||
func NewFromInterface(tokenReview authenticationv1client.TokenReviewInterface, implicitAuds authenticator.Audiences) (*WebhookTokenAuthenticator, error) {
|
||||
return newWithBackoff(tokenReview, retryBackoff, implicitAuds)
|
||||
}
|
||||
|
||||
@@ -60,8 +62,8 @@ func NewFromInterface(tokenReview authenticationclient.TokenReviewInterface, imp
|
||||
// file. It is recommend to wrap this authenticator with the token cache
|
||||
// authenticator implemented in
|
||||
// k8s.io/apiserver/pkg/authentication/token/cache.
|
||||
func New(kubeConfigFile string, implicitAuds authenticator.Audiences) (*WebhookTokenAuthenticator, error) {
|
||||
tokenReview, err := tokenReviewInterfaceFromKubeconfig(kubeConfigFile)
|
||||
func New(kubeConfigFile string, version string, implicitAuds authenticator.Audiences) (*WebhookTokenAuthenticator, error) {
|
||||
tokenReview, err := tokenReviewInterfaceFromKubeconfig(kubeConfigFile, version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -69,7 +71,7 @@ func New(kubeConfigFile string, implicitAuds authenticator.Audiences) (*WebhookT
|
||||
}
|
||||
|
||||
// newWithBackoff allows tests to skip the sleep.
|
||||
func newWithBackoff(tokenReview authenticationclient.TokenReviewInterface, initialBackoff time.Duration, implicitAuds authenticator.Audiences) (*WebhookTokenAuthenticator, error) {
|
||||
func newWithBackoff(tokenReview tokenReviewer, initialBackoff time.Duration, implicitAuds authenticator.Audiences) (*WebhookTokenAuthenticator, error) {
|
||||
return &WebhookTokenAuthenticator{tokenReview, initialBackoff, implicitAuds}, nil
|
||||
}
|
||||
|
||||
@@ -87,21 +89,21 @@ func (w *WebhookTokenAuthenticator) AuthenticateToken(ctx context.Context, token
|
||||
// intersection in the response.
|
||||
// * otherwise return unauthenticated.
|
||||
wantAuds, checkAuds := authenticator.AudiencesFrom(ctx)
|
||||
r := &authentication.TokenReview{
|
||||
Spec: authentication.TokenReviewSpec{
|
||||
r := &authenticationv1.TokenReview{
|
||||
Spec: authenticationv1.TokenReviewSpec{
|
||||
Token: token,
|
||||
Audiences: wantAuds,
|
||||
},
|
||||
}
|
||||
var (
|
||||
result *authentication.TokenReview
|
||||
result *authenticationv1.TokenReview
|
||||
err error
|
||||
auds authenticator.Audiences
|
||||
)
|
||||
webhook.WithExponentialBackoff(w.initialBackoff, func() error {
|
||||
result, err = w.tokenReview.Create(r)
|
||||
webhook.WithExponentialBackoff(ctx, w.initialBackoff, func() error {
|
||||
result, err = w.tokenReview.CreateContext(ctx, r)
|
||||
return err
|
||||
})
|
||||
}, webhook.DefaultShouldRetry)
|
||||
if err != nil {
|
||||
// An error here indicates bad configuration or an outage. Log for debugging.
|
||||
klog.Errorf("Failed to make webhook authenticator request: %v", err)
|
||||
@@ -150,28 +152,99 @@ func (w *WebhookTokenAuthenticator) AuthenticateToken(ctx context.Context, token
|
||||
// tokenReviewInterfaceFromKubeconfig builds a client from the specified kubeconfig file,
|
||||
// and returns a TokenReviewInterface that uses that client. Note that the client submits TokenReview
|
||||
// requests to the exact path specified in the kubeconfig file, so arbitrary non-API servers can be targeted.
|
||||
func tokenReviewInterfaceFromKubeconfig(kubeConfigFile string) (authenticationclient.TokenReviewInterface, error) {
|
||||
func tokenReviewInterfaceFromKubeconfig(kubeConfigFile string, version string) (tokenReviewer, error) {
|
||||
localScheme := runtime.NewScheme()
|
||||
if err := scheme.AddToScheme(localScheme); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := localScheme.SetVersionPriority(groupVersions...); err != nil {
|
||||
return nil, err
|
||||
|
||||
switch version {
|
||||
case authenticationv1.SchemeGroupVersion.Version:
|
||||
groupVersions := []schema.GroupVersion{authenticationv1.SchemeGroupVersion}
|
||||
if err := localScheme.SetVersionPriority(groupVersions...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, kubeConfigFile, groupVersions, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tokenReviewV1Client{gw}, nil
|
||||
|
||||
case authenticationv1beta1.SchemeGroupVersion.Version:
|
||||
groupVersions := []schema.GroupVersion{authenticationv1beta1.SchemeGroupVersion}
|
||||
if err := localScheme.SetVersionPriority(groupVersions...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, kubeConfigFile, groupVersions, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tokenReviewV1beta1Client{gw}, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf(
|
||||
"unsupported authentication webhook version %q, supported versions are %q, %q",
|
||||
version,
|
||||
authenticationv1.SchemeGroupVersion.Version,
|
||||
authenticationv1beta1.SchemeGroupVersion.Version,
|
||||
)
|
||||
}
|
||||
|
||||
gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, kubeConfigFile, groupVersions, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tokenReviewClient{gw}, nil
|
||||
}
|
||||
|
||||
type tokenReviewClient struct {
|
||||
type tokenReviewV1Client struct {
|
||||
w *webhook.GenericWebhook
|
||||
}
|
||||
|
||||
func (t *tokenReviewClient) Create(tokenReview *authentication.TokenReview) (*authentication.TokenReview, error) {
|
||||
result := &authentication.TokenReview{}
|
||||
err := t.w.RestClient.Post().Body(tokenReview).Do().Into(result)
|
||||
func (t *tokenReviewV1Client) CreateContext(ctx context.Context, review *authenticationv1.TokenReview) (*authenticationv1.TokenReview, error) {
|
||||
result := &authenticationv1.TokenReview{}
|
||||
err := t.w.RestClient.Post().Context(ctx).Body(review).Do().Into(result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
type tokenReviewV1beta1Client struct {
|
||||
w *webhook.GenericWebhook
|
||||
}
|
||||
|
||||
func (t *tokenReviewV1beta1Client) CreateContext(ctx context.Context, review *authenticationv1.TokenReview) (*authenticationv1.TokenReview, error) {
|
||||
v1beta1Review := &authenticationv1beta1.TokenReview{Spec: v1SpecToV1beta1Spec(&review.Spec)}
|
||||
v1beta1Result := &authenticationv1beta1.TokenReview{}
|
||||
err := t.w.RestClient.Post().Context(ctx).Body(v1beta1Review).Do().Into(v1beta1Result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
review.Status = v1beta1StatusToV1Status(&v1beta1Result.Status)
|
||||
return review, nil
|
||||
}
|
||||
|
||||
func v1SpecToV1beta1Spec(in *authenticationv1.TokenReviewSpec) authenticationv1beta1.TokenReviewSpec {
|
||||
return authenticationv1beta1.TokenReviewSpec{
|
||||
Token: in.Token,
|
||||
Audiences: in.Audiences,
|
||||
}
|
||||
}
|
||||
|
||||
func v1beta1StatusToV1Status(in *authenticationv1beta1.TokenReviewStatus) authenticationv1.TokenReviewStatus {
|
||||
return authenticationv1.TokenReviewStatus{
|
||||
Authenticated: in.Authenticated,
|
||||
User: v1beta1UserToV1User(in.User),
|
||||
Audiences: in.Audiences,
|
||||
Error: in.Error,
|
||||
}
|
||||
}
|
||||
|
||||
func v1beta1UserToV1User(u authenticationv1beta1.UserInfo) authenticationv1.UserInfo {
|
||||
var extra map[string]authenticationv1.ExtraValue
|
||||
if u.Extra != nil {
|
||||
extra = make(map[string]authenticationv1.ExtraValue, len(u.Extra))
|
||||
for k, v := range u.Extra {
|
||||
extra[k] = authenticationv1.ExtraValue(v)
|
||||
}
|
||||
}
|
||||
return authenticationv1.UserInfo{
|
||||
Username: u.Username,
|
||||
UID: u.UID,
|
||||
Groups: u.Groups,
|
||||
Extra: extra,
|
||||
}
|
||||
}
|
||||
|
||||
168
vendor/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook.go
generated
vendored
168
vendor/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook.go
generated
vendored
@@ -18,13 +18,15 @@ limitations under the License.
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"k8s.io/klog"
|
||||
|
||||
authorization "k8s.io/api/authorization/v1beta1"
|
||||
authorizationv1 "k8s.io/api/authorization/v1"
|
||||
authorizationv1beta1 "k8s.io/api/authorization/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/cache"
|
||||
@@ -32,11 +34,7 @@ import (
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/apiserver/pkg/util/webhook"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
authorizationclient "k8s.io/client-go/kubernetes/typed/authorization/v1beta1"
|
||||
)
|
||||
|
||||
var (
|
||||
groupVersions = []schema.GroupVersion{authorization.SchemeGroupVersion}
|
||||
authorizationv1client "k8s.io/client-go/kubernetes/typed/authorization/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -48,8 +46,12 @@ const (
|
||||
// Ensure Webhook implements the authorizer.Authorizer interface.
|
||||
var _ authorizer.Authorizer = (*WebhookAuthorizer)(nil)
|
||||
|
||||
type subjectAccessReviewer interface {
|
||||
CreateContext(context.Context, *authorizationv1.SubjectAccessReview) (*authorizationv1.SubjectAccessReview, error)
|
||||
}
|
||||
|
||||
type WebhookAuthorizer struct {
|
||||
subjectAccessReview authorizationclient.SubjectAccessReviewInterface
|
||||
subjectAccessReview subjectAccessReviewer
|
||||
responseCache *cache.LRUExpireCache
|
||||
authorizedTTL time.Duration
|
||||
unauthorizedTTL time.Duration
|
||||
@@ -58,12 +60,11 @@ type WebhookAuthorizer struct {
|
||||
}
|
||||
|
||||
// NewFromInterface creates a WebhookAuthorizer using the given subjectAccessReview client
|
||||
func NewFromInterface(subjectAccessReview authorizationclient.SubjectAccessReviewInterface, authorizedTTL, unauthorizedTTL time.Duration) (*WebhookAuthorizer, error) {
|
||||
func NewFromInterface(subjectAccessReview authorizationv1client.SubjectAccessReviewInterface, authorizedTTL, unauthorizedTTL time.Duration) (*WebhookAuthorizer, error) {
|
||||
return newWithBackoff(subjectAccessReview, authorizedTTL, unauthorizedTTL, retryBackoff)
|
||||
}
|
||||
|
||||
// New creates a new WebhookAuthorizer from the provided kubeconfig file.
|
||||
//
|
||||
// The config's cluster field is used to refer to the remote service, user refers to the returned authorizer.
|
||||
//
|
||||
// # clusters refers to the remote service.
|
||||
@@ -82,8 +83,8 @@ func NewFromInterface(subjectAccessReview authorizationclient.SubjectAccessRevie
|
||||
//
|
||||
// For additional HTTP configuration, refer to the kubeconfig documentation
|
||||
// https://kubernetes.io/docs/user-guide/kubeconfig-file/.
|
||||
func New(kubeConfigFile string, authorizedTTL, unauthorizedTTL time.Duration) (*WebhookAuthorizer, error) {
|
||||
subjectAccessReview, err := subjectAccessReviewInterfaceFromKubeconfig(kubeConfigFile)
|
||||
func New(kubeConfigFile string, version string, authorizedTTL, unauthorizedTTL time.Duration) (*WebhookAuthorizer, error) {
|
||||
subjectAccessReview, err := subjectAccessReviewInterfaceFromKubeconfig(kubeConfigFile, version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -91,7 +92,7 @@ func New(kubeConfigFile string, authorizedTTL, unauthorizedTTL time.Duration) (*
|
||||
}
|
||||
|
||||
// newWithBackoff allows tests to skip the sleep.
|
||||
func newWithBackoff(subjectAccessReview authorizationclient.SubjectAccessReviewInterface, authorizedTTL, unauthorizedTTL, initialBackoff time.Duration) (*WebhookAuthorizer, error) {
|
||||
func newWithBackoff(subjectAccessReview subjectAccessReviewer, authorizedTTL, unauthorizedTTL, initialBackoff time.Duration) (*WebhookAuthorizer, error) {
|
||||
return &WebhookAuthorizer{
|
||||
subjectAccessReview: subjectAccessReview,
|
||||
responseCache: cache.NewLRUExpireCache(1024),
|
||||
@@ -149,10 +150,10 @@ func newWithBackoff(subjectAccessReview authorizationclient.SubjectAccessReviewI
|
||||
// TODO(mikedanese): We should eventually support failing closed when we
|
||||
// encounter an error. We are failing open now to preserve backwards compatible
|
||||
// behavior.
|
||||
func (w *WebhookAuthorizer) Authorize(attr authorizer.Attributes) (decision authorizer.Decision, reason string, err error) {
|
||||
r := &authorization.SubjectAccessReview{}
|
||||
func (w *WebhookAuthorizer) Authorize(ctx context.Context, attr authorizer.Attributes) (decision authorizer.Decision, reason string, err error) {
|
||||
r := &authorizationv1.SubjectAccessReview{}
|
||||
if user := attr.GetUser(); user != nil {
|
||||
r.Spec = authorization.SubjectAccessReviewSpec{
|
||||
r.Spec = authorizationv1.SubjectAccessReviewSpec{
|
||||
User: user.GetName(),
|
||||
UID: user.GetUID(),
|
||||
Groups: user.GetGroups(),
|
||||
@@ -161,7 +162,7 @@ func (w *WebhookAuthorizer) Authorize(attr authorizer.Attributes) (decision auth
|
||||
}
|
||||
|
||||
if attr.IsResourceRequest() {
|
||||
r.Spec.ResourceAttributes = &authorization.ResourceAttributes{
|
||||
r.Spec.ResourceAttributes = &authorizationv1.ResourceAttributes{
|
||||
Namespace: attr.GetNamespace(),
|
||||
Verb: attr.GetVerb(),
|
||||
Group: attr.GetAPIGroup(),
|
||||
@@ -171,7 +172,7 @@ func (w *WebhookAuthorizer) Authorize(attr authorizer.Attributes) (decision auth
|
||||
Name: attr.GetName(),
|
||||
}
|
||||
} else {
|
||||
r.Spec.NonResourceAttributes = &authorization.NonResourceAttributes{
|
||||
r.Spec.NonResourceAttributes = &authorizationv1.NonResourceAttributes{
|
||||
Path: attr.GetPath(),
|
||||
Verb: attr.GetVerb(),
|
||||
}
|
||||
@@ -181,16 +182,16 @@ func (w *WebhookAuthorizer) Authorize(attr authorizer.Attributes) (decision auth
|
||||
return w.decisionOnError, "", err
|
||||
}
|
||||
if entry, ok := w.responseCache.Get(string(key)); ok {
|
||||
r.Status = entry.(authorization.SubjectAccessReviewStatus)
|
||||
r.Status = entry.(authorizationv1.SubjectAccessReviewStatus)
|
||||
} else {
|
||||
var (
|
||||
result *authorization.SubjectAccessReview
|
||||
result *authorizationv1.SubjectAccessReview
|
||||
err error
|
||||
)
|
||||
webhook.WithExponentialBackoff(w.initialBackoff, func() error {
|
||||
result, err = w.subjectAccessReview.Create(r)
|
||||
webhook.WithExponentialBackoff(ctx, w.initialBackoff, func() error {
|
||||
result, err = w.subjectAccessReview.CreateContext(ctx, r)
|
||||
return err
|
||||
})
|
||||
}, webhook.DefaultShouldRetry)
|
||||
if err != nil {
|
||||
// An error here indicates bad configuration or an outage. Log for debugging.
|
||||
klog.Errorf("Failed to make webhook authorizer request: %v", err)
|
||||
@@ -228,13 +229,13 @@ func (w *WebhookAuthorizer) RulesFor(user user.Info, namespace string) ([]author
|
||||
return resourceRules, nonResourceRules, incomplete, fmt.Errorf("webhook authorizer does not support user rule resolution")
|
||||
}
|
||||
|
||||
func convertToSARExtra(extra map[string][]string) map[string]authorization.ExtraValue {
|
||||
func convertToSARExtra(extra map[string][]string) map[string]authorizationv1.ExtraValue {
|
||||
if extra == nil {
|
||||
return nil
|
||||
}
|
||||
ret := map[string]authorization.ExtraValue{}
|
||||
ret := map[string]authorizationv1.ExtraValue{}
|
||||
for k, v := range extra {
|
||||
ret[k] = authorization.ExtraValue(v)
|
||||
ret[k] = authorizationv1.ExtraValue(v)
|
||||
}
|
||||
|
||||
return ret
|
||||
@@ -243,32 +244,69 @@ func convertToSARExtra(extra map[string][]string) map[string]authorization.Extra
|
||||
// subjectAccessReviewInterfaceFromKubeconfig builds a client from the specified kubeconfig file,
|
||||
// and returns a SubjectAccessReviewInterface that uses that client. Note that the client submits SubjectAccessReview
|
||||
// requests to the exact path specified in the kubeconfig file, so arbitrary non-API servers can be targeted.
|
||||
func subjectAccessReviewInterfaceFromKubeconfig(kubeConfigFile string) (authorizationclient.SubjectAccessReviewInterface, error) {
|
||||
func subjectAccessReviewInterfaceFromKubeconfig(kubeConfigFile string, version string) (subjectAccessReviewer, error) {
|
||||
localScheme := runtime.NewScheme()
|
||||
if err := scheme.AddToScheme(localScheme); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := localScheme.SetVersionPriority(groupVersions...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, kubeConfigFile, groupVersions, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
switch version {
|
||||
case authorizationv1.SchemeGroupVersion.Version:
|
||||
groupVersions := []schema.GroupVersion{authorizationv1.SchemeGroupVersion}
|
||||
if err := localScheme.SetVersionPriority(groupVersions...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, kubeConfigFile, groupVersions, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &subjectAccessReviewV1Client{gw}, nil
|
||||
|
||||
case authorizationv1beta1.SchemeGroupVersion.Version:
|
||||
groupVersions := []schema.GroupVersion{authorizationv1beta1.SchemeGroupVersion}
|
||||
if err := localScheme.SetVersionPriority(groupVersions...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, kubeConfigFile, groupVersions, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &subjectAccessReviewV1beta1Client{gw}, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf(
|
||||
"unsupported webhook authorizer version %q, supported versions are %q, %q",
|
||||
version,
|
||||
authorizationv1.SchemeGroupVersion.Version,
|
||||
authorizationv1beta1.SchemeGroupVersion.Version,
|
||||
)
|
||||
}
|
||||
return &subjectAccessReviewClient{gw}, nil
|
||||
}
|
||||
|
||||
type subjectAccessReviewClient struct {
|
||||
type subjectAccessReviewV1Client struct {
|
||||
w *webhook.GenericWebhook
|
||||
}
|
||||
|
||||
func (t *subjectAccessReviewClient) Create(subjectAccessReview *authorization.SubjectAccessReview) (*authorization.SubjectAccessReview, error) {
|
||||
result := &authorization.SubjectAccessReview{}
|
||||
err := t.w.RestClient.Post().Body(subjectAccessReview).Do().Into(result)
|
||||
func (t *subjectAccessReviewV1Client) CreateContext(ctx context.Context, subjectAccessReview *authorizationv1.SubjectAccessReview) (*authorizationv1.SubjectAccessReview, error) {
|
||||
result := &authorizationv1.SubjectAccessReview{}
|
||||
err := t.w.RestClient.Post().Context(ctx).Body(subjectAccessReview).Do().Into(result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
type subjectAccessReviewV1beta1Client struct {
|
||||
w *webhook.GenericWebhook
|
||||
}
|
||||
|
||||
func (t *subjectAccessReviewV1beta1Client) CreateContext(ctx context.Context, subjectAccessReview *authorizationv1.SubjectAccessReview) (*authorizationv1.SubjectAccessReview, error) {
|
||||
v1beta1Review := &authorizationv1beta1.SubjectAccessReview{Spec: v1SpecToV1beta1Spec(&subjectAccessReview.Spec)}
|
||||
v1beta1Result := &authorizationv1beta1.SubjectAccessReview{}
|
||||
err := t.w.RestClient.Post().Context(ctx).Body(v1beta1Review).Do().Into(v1beta1Result)
|
||||
if err == nil {
|
||||
subjectAccessReview.Status = v1beta1StatusToV1Status(&v1beta1Result.Status)
|
||||
}
|
||||
return subjectAccessReview, err
|
||||
}
|
||||
|
||||
// shouldCache determines whether it is safe to cache the given request attributes. If the
|
||||
// requester-controlled attributes are too large, this may be a DoS attempt, so we skip the cache.
|
||||
func shouldCache(attr authorizer.Attributes) bool {
|
||||
@@ -282,3 +320,59 @@ func shouldCache(attr authorizer.Attributes) bool {
|
||||
int64(len(attr.GetPath()))
|
||||
return controlledAttrSize < maxControlledAttrCacheSize
|
||||
}
|
||||
|
||||
func v1beta1StatusToV1Status(in *authorizationv1beta1.SubjectAccessReviewStatus) authorizationv1.SubjectAccessReviewStatus {
|
||||
return authorizationv1.SubjectAccessReviewStatus{
|
||||
Allowed: in.Allowed,
|
||||
Denied: in.Denied,
|
||||
Reason: in.Reason,
|
||||
EvaluationError: in.EvaluationError,
|
||||
}
|
||||
}
|
||||
|
||||
func v1SpecToV1beta1Spec(in *authorizationv1.SubjectAccessReviewSpec) authorizationv1beta1.SubjectAccessReviewSpec {
|
||||
return authorizationv1beta1.SubjectAccessReviewSpec{
|
||||
ResourceAttributes: v1ResourceAttributesToV1beta1ResourceAttributes(in.ResourceAttributes),
|
||||
NonResourceAttributes: v1NonResourceAttributesToV1beta1NonResourceAttributes(in.NonResourceAttributes),
|
||||
User: in.User,
|
||||
Groups: in.Groups,
|
||||
Extra: v1ExtraToV1beta1Extra(in.Extra),
|
||||
UID: in.UID,
|
||||
}
|
||||
}
|
||||
|
||||
func v1ResourceAttributesToV1beta1ResourceAttributes(in *authorizationv1.ResourceAttributes) *authorizationv1beta1.ResourceAttributes {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
return &authorizationv1beta1.ResourceAttributes{
|
||||
Namespace: in.Namespace,
|
||||
Verb: in.Verb,
|
||||
Group: in.Group,
|
||||
Version: in.Version,
|
||||
Resource: in.Resource,
|
||||
Subresource: in.Subresource,
|
||||
Name: in.Name,
|
||||
}
|
||||
}
|
||||
|
||||
func v1NonResourceAttributesToV1beta1NonResourceAttributes(in *authorizationv1.NonResourceAttributes) *authorizationv1beta1.NonResourceAttributes {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
return &authorizationv1beta1.NonResourceAttributes{
|
||||
Path: in.Path,
|
||||
Verb: in.Verb,
|
||||
}
|
||||
}
|
||||
|
||||
func v1ExtraToV1beta1Extra(in map[string]authorizationv1.ExtraValue) map[string]authorizationv1beta1.ExtraValue {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
ret := make(map[string]authorizationv1beta1.ExtraValue, len(in))
|
||||
for k, v := range in {
|
||||
ret[k] = authorizationv1beta1.ExtraValue(v)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user