diff --git a/pkg/kubeapiserver/authorizer/config.go b/pkg/kubeapiserver/authorizer/config.go index ed3cf6decc1b7..f8f9a06ae33e2 100644 --- a/pkg/kubeapiserver/authorizer/config.go +++ b/pkg/kubeapiserver/authorizer/config.go @@ -116,6 +116,7 @@ func (config Config) New() (authorizer.Authorizer, authorizer.RuleResolver, erro } webhookAuthorizer, err := webhook.New(config.WebhookConfigFile, config.WebhookVersion, + "", config.WebhookCacheAuthorizedTTL, config.WebhookCacheUnauthorizedTTL, *config.WebhookRetryBackoff, diff --git a/staging/src/k8s.io/apiserver/pkg/authorization/authorizerfactory/delegating.go b/staging/src/k8s.io/apiserver/pkg/authorization/authorizerfactory/delegating.go index d1ead25dbb245..0a721fb8d46ad 100644 --- a/staging/src/k8s.io/apiserver/pkg/authorization/authorizerfactory/delegating.go +++ b/staging/src/k8s.io/apiserver/pkg/authorization/authorizerfactory/delegating.go @@ -42,6 +42,9 @@ type DelegatingAuthorizerConfig struct { // 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 + + // The cluster to execute authorization decisions against (optional). + Cluster string } func (c DelegatingAuthorizerConfig) New() (authorizer.Authorizer, error) { @@ -58,5 +61,6 @@ func (c DelegatingAuthorizerConfig) New() (authorizer.Authorizer, error) { RecordRequestTotal: RecordRequestTotal, RecordRequestLatency: RecordRequestLatency, }, + c.Cluster, ) } diff --git a/staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook.go b/staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook.go index b7970139306c7..9ee9af33f38cd 100644 --- a/staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook.go +++ b/staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook.go @@ -67,11 +67,12 @@ type WebhookAuthorizer struct { retryBackoff wait.Backoff decisionOnError authorizer.Decision metrics AuthorizerMetrics + cluster string } // NewFromInterface creates a WebhookAuthorizer using the given subjectAccessReview client -func NewFromInterface(subjectAccessReview authorizationv1client.AuthorizationV1Interface, authorizedTTL, unauthorizedTTL time.Duration, retryBackoff wait.Backoff, metrics AuthorizerMetrics) (*WebhookAuthorizer, error) { - return newWithBackoff(&subjectAccessReviewV1Client{subjectAccessReview.RESTClient()}, authorizedTTL, unauthorizedTTL, retryBackoff, metrics) +func NewFromInterface(subjectAccessReview authorizationv1client.AuthorizationV1Interface, authorizedTTL, unauthorizedTTL time.Duration, retryBackoff wait.Backoff, metrics AuthorizerMetrics, cluster string) (*WebhookAuthorizer, error) { + return newWithBackoff(&subjectAccessReviewV1Client{subjectAccessReview.RESTClient(), cluster}, authorizedTTL, unauthorizedTTL, retryBackoff, metrics, cluster) } // New creates a new WebhookAuthorizer from the provided kubeconfig file. @@ -93,19 +94,19 @@ func NewFromInterface(subjectAccessReview authorizationv1client.AuthorizationV1I // // For additional HTTP configuration, refer to the kubeconfig documentation // https://kubernetes.io/docs/user-guide/kubeconfig-file/. -func New(kubeConfigFile string, version string, authorizedTTL, unauthorizedTTL time.Duration, retryBackoff wait.Backoff, customDial utilnet.DialFunc) (*WebhookAuthorizer, error) { - subjectAccessReview, err := subjectAccessReviewInterfaceFromKubeconfig(kubeConfigFile, version, retryBackoff, customDial) +func New(kubeConfigFile, version, cluster string, authorizedTTL, unauthorizedTTL time.Duration, retryBackoff wait.Backoff, customDial utilnet.DialFunc) (*WebhookAuthorizer, error) { + subjectAccessReview, err := subjectAccessReviewInterfaceFromKubeconfig(kubeConfigFile, version, cluster, retryBackoff, customDial) if err != nil { return nil, err } return newWithBackoff(subjectAccessReview, authorizedTTL, unauthorizedTTL, retryBackoff, AuthorizerMetrics{ RecordRequestTotal: noopMetrics{}.RecordRequestTotal, RecordRequestLatency: noopMetrics{}.RecordRequestLatency, - }) + }, cluster) } // newWithBackoff allows tests to skip the sleep. -func newWithBackoff(subjectAccessReview subjectAccessReviewer, authorizedTTL, unauthorizedTTL time.Duration, retryBackoff wait.Backoff, metrics AuthorizerMetrics) (*WebhookAuthorizer, error) { +func newWithBackoff(subjectAccessReview subjectAccessReviewer, authorizedTTL, unauthorizedTTL time.Duration, retryBackoff wait.Backoff, metrics AuthorizerMetrics, cluster string) (*WebhookAuthorizer, error) { return &WebhookAuthorizer{ subjectAccessReview: subjectAccessReview, responseCache: cache.NewLRUExpireCache(8192), @@ -114,6 +115,7 @@ func newWithBackoff(subjectAccessReview subjectAccessReviewer, authorizedTTL, un retryBackoff: retryBackoff, decisionOnError: authorizer.DecisionNoOpinion, metrics: metrics, + cluster: cluster, }, nil } @@ -272,7 +274,7 @@ func convertToSARExtra(extra map[string][]string) map[string]authorizationv1.Ext // 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, version string, retryBackoff wait.Backoff, customDial utilnet.DialFunc) (subjectAccessReviewer, error) { +func subjectAccessReviewInterfaceFromKubeconfig(kubeConfigFile, version, cluster string, retryBackoff wait.Backoff, customDial utilnet.DialFunc) (subjectAccessReviewer, error) { localScheme := runtime.NewScheme() if err := scheme.AddToScheme(localScheme); err != nil { return nil, err @@ -288,7 +290,7 @@ func subjectAccessReviewInterfaceFromKubeconfig(kubeConfigFile string, version s if err != nil { return nil, err } - return &subjectAccessReviewV1ClientGW{gw.RestClient}, nil + return &subjectAccessReviewV1ClientGW{gw.RestClient, cluster}, nil case authorizationv1beta1.SchemeGroupVersion.Version: groupVersions := []schema.GroupVersion{authorizationv1beta1.SchemeGroupVersion} @@ -299,7 +301,7 @@ func subjectAccessReviewInterfaceFromKubeconfig(kubeConfigFile string, version s if err != nil { return nil, err } - return &subjectAccessReviewV1beta1ClientGW{gw.RestClient}, nil + return &subjectAccessReviewV1beta1ClientGW{gw.RestClient, cluster}, nil default: return nil, fmt.Errorf( @@ -312,13 +314,15 @@ func subjectAccessReviewInterfaceFromKubeconfig(kubeConfigFile string, version s } type subjectAccessReviewV1Client struct { - client rest.Interface + client rest.Interface + cluster string } func (t *subjectAccessReviewV1Client) Create(ctx context.Context, subjectAccessReview *authorizationv1.SubjectAccessReview, opts metav1.CreateOptions) (result *authorizationv1.SubjectAccessReview, statusCode int, err error) { result = &authorizationv1.SubjectAccessReview{} restResult := t.client.Post(). + Cluster(t.cluster). Resource("subjectaccessreviews"). VersionedParams(&opts, scheme.ParameterCodec). Body(subjectAccessReview). @@ -331,14 +335,15 @@ func (t *subjectAccessReviewV1Client) Create(ctx context.Context, subjectAccessR // subjectAccessReviewV1ClientGW used by the generic webhook, doesn't specify GVR. type subjectAccessReviewV1ClientGW struct { - client rest.Interface + client rest.Interface + cluster string } func (t *subjectAccessReviewV1ClientGW) Create(ctx context.Context, subjectAccessReview *authorizationv1.SubjectAccessReview, _ metav1.CreateOptions) (*authorizationv1.SubjectAccessReview, int, error) { var statusCode int result := &authorizationv1.SubjectAccessReview{} - restResult := t.client.Post().Body(subjectAccessReview).Do(ctx) + restResult := t.client.Post().Cluster(t.cluster).Body(subjectAccessReview).Do(ctx) restResult.StatusCode(&statusCode) err := restResult.Into(result) @@ -348,7 +353,8 @@ func (t *subjectAccessReviewV1ClientGW) Create(ctx context.Context, subjectAcces // subjectAccessReviewV1beta1ClientGW used by the generic webhook, doesn't specify GVR. type subjectAccessReviewV1beta1ClientGW struct { - client rest.Interface + client rest.Interface + cluster string } func (t *subjectAccessReviewV1beta1ClientGW) Create(ctx context.Context, subjectAccessReview *authorizationv1.SubjectAccessReview, _ metav1.CreateOptions) (*authorizationv1.SubjectAccessReview, int, error) { @@ -356,7 +362,7 @@ func (t *subjectAccessReviewV1beta1ClientGW) Create(ctx context.Context, subject v1beta1Review := &authorizationv1beta1.SubjectAccessReview{Spec: v1SpecToV1beta1Spec(&subjectAccessReview.Spec)} v1beta1Result := &authorizationv1beta1.SubjectAccessReview{} - restResult := t.client.Post().Body(v1beta1Review).Do(ctx) + restResult := t.client.Post().Cluster(t.cluster).Body(v1beta1Review).Do(ctx) restResult.StatusCode(&statusCode) err := restResult.Into(v1beta1Result) diff --git a/staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook_v1_test.go b/staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook_v1_test.go index 6617bcea967bd..8f39587e9810f 100644 --- a/staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook_v1_test.go +++ b/staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook_v1_test.go @@ -194,11 +194,11 @@ current-context: default return fmt.Errorf("failed to execute test template: %v", err) } // Create a new authorizer - sarClient, err := subjectAccessReviewInterfaceFromKubeconfig(p, "v1", testRetryBackoff, nil) + sarClient, err := subjectAccessReviewInterfaceFromKubeconfig(p, "v1", "", testRetryBackoff, nil) if err != nil { return fmt.Errorf("error building sar client: %v", err) } - _, err = newWithBackoff(sarClient, 0, 0, testRetryBackoff, noopAuthorizerMetrics()) + _, err = newWithBackoff(sarClient, 0, 0, testRetryBackoff, noopAuthorizerMetrics(), "") return err }() if err != nil && !tt.wantErr { @@ -333,11 +333,11 @@ func newV1Authorizer(callbackURL string, clientCert, clientKey, ca []byte, cache if err := json.NewEncoder(tempfile).Encode(config); err != nil { return nil, err } - sarClient, err := subjectAccessReviewInterfaceFromKubeconfig(p, "v1", testRetryBackoff, nil) + sarClient, err := subjectAccessReviewInterfaceFromKubeconfig(p, "v1", "", testRetryBackoff, nil) if err != nil { return nil, fmt.Errorf("error building sar client: %v", err) } - return newWithBackoff(sarClient, cacheTime, cacheTime, testRetryBackoff, metrics) + return newWithBackoff(sarClient, cacheTime, cacheTime, testRetryBackoff, metrics, "") } func TestV1TLSConfig(t *testing.T) { diff --git a/staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook_v1beta1_test.go b/staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook_v1beta1_test.go index 77f90f3e831e9..582c3d12b2bc0 100644 --- a/staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook_v1beta1_test.go +++ b/staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook_v1beta1_test.go @@ -186,11 +186,11 @@ current-context: default return fmt.Errorf("failed to execute test template: %v", err) } // Create a new authorizer - sarClient, err := subjectAccessReviewInterfaceFromKubeconfig(p, "v1beta1", testRetryBackoff, nil) + sarClient, err := subjectAccessReviewInterfaceFromKubeconfig(p, "v1beta1", "", testRetryBackoff, nil) if err != nil { return fmt.Errorf("error building sar client: %v", err) } - _, err = newWithBackoff(sarClient, 0, 0, testRetryBackoff, noopAuthorizerMetrics()) + _, err = newWithBackoff(sarClient, 0, 0, testRetryBackoff, noopAuthorizerMetrics(), "") return err }() if err != nil && !tt.wantErr { @@ -325,11 +325,11 @@ func newV1beta1Authorizer(callbackURL string, clientCert, clientKey, ca []byte, if err := json.NewEncoder(tempfile).Encode(config); err != nil { return nil, err } - sarClient, err := subjectAccessReviewInterfaceFromKubeconfig(p, "v1beta1", testRetryBackoff, nil) + sarClient, err := subjectAccessReviewInterfaceFromKubeconfig(p, "v1beta1", "", testRetryBackoff, nil) if err != nil { return nil, fmt.Errorf("error building sar client: %v", err) } - return newWithBackoff(sarClient, cacheTime, cacheTime, testRetryBackoff, noopAuthorizerMetrics()) + return newWithBackoff(sarClient, cacheTime, cacheTime, testRetryBackoff, noopAuthorizerMetrics(), "") } func TestV1beta1TLSConfig(t *testing.T) {