diff --git a/docs/admin/kube-apiserver.md b/docs/admin/kube-apiserver.md index 669c15bd9fa..38c9eab5e12 100644 --- a/docs/admin/kube-apiserver.md +++ b/docs/admin/kube-apiserver.md @@ -60,6 +60,8 @@ kube-apiserver --authentication-token-webhook-config-file="": File with webhook configuration for token authentication in kubeconfig format. The API server will query the remote service to determine authentication for bearer tokens. --authorization-mode="AlwaysAllow": Ordered list of plug-ins to do authorization on secure port. Comma-delimited list of: AlwaysAllow,AlwaysDeny,ABAC,Webhook --authorization-policy-file="": File with authorization policy in csv format, used with --authorization-mode=ABAC, on the secure port. + --authorization-webhook-cache-authorized-ttl=5m0s: The duration to cache 'authorized' responses from the webhook authorizer. Default is 5m. + --authorization-webhook-cache-unauthorized-ttl=30s: The duration to cache 'unauthorized' responses from the webhook authorizer. Default is 30s. --authorization-webhook-config-file="": File with webhook configuration in kubeconfig format, used with --authorization-mode=Webhook. The API server will query the remote service to determine access on the API server's secure port. --basic-auth-file="": If set, the file that will be used to admit requests to the secure port of the API server via http basic authentication. --bind-address=0.0.0.0: The IP address on which to listen for the --secure-port port. The associated interface(s) must be reachable by the rest of the cluster, and by CLI/web clients. If blank, all interfaces will be used (0.0.0.0). @@ -121,7 +123,7 @@ kube-apiserver --watch-cache-sizes=[]: List of watch cache sizes for every resource (pods, nodes, etc.), comma separated. The individual override format: resource#size, where size is a number. It takes effect when watch-cache is enabled. ``` -###### Auto generated by spf13/cobra on 17-May-2016 +###### Auto generated by spf13/cobra on 18-May-2016 diff --git a/hack/verify-flags/known-flags.txt b/hack/verify-flags/known-flags.txt index 2ba0f6d180c..633f20a86e1 100644 --- a/hack/verify-flags/known-flags.txt +++ b/hack/verify-flags/known-flags.txt @@ -23,6 +23,8 @@ authentication-token-webhook-config-file authorization-mode authorization-policy-file authorization-webhook-config-file +authorization-webhook-cache-authorized-ttl +authorization-webhook-cache-unauthorized-ttl babysit-daemons basic-auth-file bench-pods diff --git a/pkg/apiserver/authz.go b/pkg/apiserver/authz.go index 88e8f6284e5..78be39a6764 100644 --- a/pkg/apiserver/authz.go +++ b/pkg/apiserver/authz.go @@ -19,6 +19,7 @@ package apiserver import ( "errors" "fmt" + "time" "k8s.io/kubernetes/pkg/auth/authorizer" "k8s.io/kubernetes/pkg/auth/authorizer/abac" @@ -77,6 +78,10 @@ type AuthorizationConfig struct { // Kubeconfig file for Webhook authorization plugin. WebhookConfigFile string + // TTL for caching of authorized responses from the webhook server. + WebhookCacheAuthorizedTTL time.Duration + // TTL for caching of unauthorized responses from the webhook server. + WebhookCacheUnauthorizedTTL time.Duration } // NewAuthorizerFromAuthorizationConfig returns the right sort of union of multiple authorizer.Authorizer objects @@ -114,7 +119,9 @@ func NewAuthorizerFromAuthorizationConfig(authorizationModes []string, config Au if config.WebhookConfigFile == "" { return nil, errors.New("Webhook's configuration file not passed") } - webhookAuthorizer, err := webhook.New(config.WebhookConfigFile) + webhookAuthorizer, err := webhook.New(config.WebhookConfigFile, + config.WebhookCacheAuthorizedTTL, + config.WebhookCacheUnauthorizedTTL) if err != nil { return nil, err } diff --git a/pkg/genericapiserver/options/server_run_options.go b/pkg/genericapiserver/options/server_run_options.go index ce4a68d9ba8..64226143c12 100644 --- a/pkg/genericapiserver/options/server_run_options.go +++ b/pkg/genericapiserver/options/server_run_options.go @@ -20,6 +20,7 @@ import ( "net" "strconv" "strings" + "time" "k8s.io/kubernetes/pkg/admission" "k8s.io/kubernetes/pkg/api" @@ -103,10 +104,14 @@ type ServerRunOptions struct { func NewServerRunOptions() *ServerRunOptions { return &ServerRunOptions{ - APIGroupPrefix: "/apis", - APIPrefix: "/api", - AdmissionControl: "AlwaysAdmit", - AuthorizationMode: "AlwaysAllow", + APIGroupPrefix: "/apis", + APIPrefix: "/api", + AdmissionControl: "AlwaysAdmit", + AuthorizationMode: "AlwaysAllow", + AuthorizationConfig: apiserver.AuthorizationConfig{ + WebhookCacheAuthorizedTTL: 5 * time.Minute, + WebhookCacheUnauthorizedTTL: 30 * time.Second, + }, BindAddress: net.ParseIP("0.0.0.0"), CertDirectory: "/var/run/kubernetes", DefaultStorageMediaType: "application/json", @@ -220,6 +225,8 @@ func (s *ServerRunOptions) AddFlags(fs *pflag.FlagSet) { fs.StringVar(&s.AuthorizationConfig.PolicyFile, "authorization-policy-file", s.AuthorizationConfig.PolicyFile, "File with authorization policy in csv format, used with --authorization-mode=ABAC, on the secure port.") fs.StringVar(&s.AuthorizationConfig.WebhookConfigFile, "authorization-webhook-config-file", s.AuthorizationConfig.WebhookConfigFile, "File with webhook configuration in kubeconfig format, used with --authorization-mode=Webhook. The API server will query the remote service to determine access on the API server's secure port.") + fs.DurationVar(&s.AuthorizationConfig.WebhookCacheAuthorizedTTL, "authorization-webhook-cache-authorized-ttl", s.AuthorizationConfig.WebhookCacheAuthorizedTTL, "The duration to cache 'authorized' responses from the webhook authorizer. Default is 5m.") + fs.DurationVar(&s.AuthorizationConfig.WebhookCacheUnauthorizedTTL, "authorization-webhook-cache-unauthorized-ttl", s.AuthorizationConfig.WebhookCacheUnauthorizedTTL, "The duration to cache 'unauthorized' responses from the webhook authorizer. Default is 30s.") fs.StringVar(&s.BasicAuthFile, "basic-auth-file", s.BasicAuthFile, "If set, the file that will be used to admit requests to the secure port of the API server via http basic authentication.") diff --git a/plugin/pkg/auth/authorizer/webhook/webhook.go b/plugin/pkg/auth/authorizer/webhook/webhook.go index 3d662f33527..eeaf2af1d37 100644 --- a/plugin/pkg/auth/authorizer/webhook/webhook.go +++ b/plugin/pkg/auth/authorizer/webhook/webhook.go @@ -18,11 +18,14 @@ limitations under the License. package webhook import ( + "encoding/json" "errors" + "time" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/apis/authorization/v1beta1" "k8s.io/kubernetes/pkg/auth/authorizer" + "k8s.io/kubernetes/pkg/util/cache" "k8s.io/kubernetes/plugin/pkg/webhook" _ "k8s.io/kubernetes/pkg/apis/authorization/install" @@ -37,6 +40,9 @@ var _ authorizer.Authorizer = (*WebhookAuthorizer)(nil) type WebhookAuthorizer struct { *webhook.GenericWebhook + responseCache *cache.LRUExpireCache + authorizedTTL time.Duration + unauthorizedTTL time.Duration } // New creates a new WebhookAuthorizer from the provided kubeconfig file. @@ -59,12 +65,12 @@ type WebhookAuthorizer struct { // // For additional HTTP configuration, refer to the kubeconfig documentation // http://kubernetes.io/v1.1/docs/user-guide/kubeconfig-file.html. -func New(kubeConfigFile string) (*WebhookAuthorizer, error) { +func New(kubeConfigFile string, authorizedTTL, unauthorizedTTL time.Duration) (*WebhookAuthorizer, error) { gw, err := webhook.NewGenericWebhook(kubeConfigFile, groupVersions) if err != nil { return nil, err } - return &WebhookAuthorizer{gw}, nil + return &WebhookAuthorizer{gw, cache.NewLRUExpireCache(1024), authorizedTTL, unauthorizedTTL}, nil } // Authorize makes a REST request to the remote service describing the attempted action as a JSON @@ -134,13 +140,27 @@ func (w *WebhookAuthorizer) Authorize(attr authorizer.Attributes) (err error) { Verb: attr.GetVerb(), } } - result := w.RestClient.Post().Body(r).Do() - if err := result.Error(); err != nil { + key, err := json.Marshal(r.Spec) + if err != nil { return err } - - if err := result.Into(r); err != nil { - return err + if entry, ok := w.responseCache.Get(string(key)); ok { + r.Status = entry.(v1beta1.SubjectAccessReviewStatus) + } else { + result := w.RestClient.Post().Body(r).Do() + if err := result.Error(); err != nil { + return err + } + if err := result.Into(r); err != nil { + return err + } + go func() { + if r.Status.Allowed { + w.responseCache.Add(string(key), r.Status, w.authorizedTTL) + } else { + w.responseCache.Add(string(key), r.Status, w.unauthorizedTTL) + } + }() } if r.Status.Allowed { return nil diff --git a/plugin/pkg/auth/authorizer/webhook/webhook_test.go b/plugin/pkg/auth/authorizer/webhook/webhook_test.go index 5629fa2ba75..deb332960f4 100644 --- a/plugin/pkg/auth/authorizer/webhook/webhook_test.go +++ b/plugin/pkg/auth/authorizer/webhook/webhook_test.go @@ -182,7 +182,7 @@ current-context: default return fmt.Errorf("failed to execute test template: %v", err) } // Create a new authorizer - _, err = New(p) + _, err = New(p, 0, 0) return err }() if err != nil && !tt.wantErr { @@ -283,7 +283,7 @@ func newAuthorizer(callbackURL string, clientCert, clientKey, ca []byte) (*Webho if err := json.NewEncoder(tempfile).Encode(config); err != nil { return nil, err } - return New(p) + return New(p, 0, 0) } func TestTLSConfig(t *testing.T) {