Add LRU Expire cache to webhook authorizer.

This commit is contained in:
CJ Cullen 2016-05-14 17:47:26 -07:00
parent 346f965871
commit d03dbbcc14
6 changed files with 53 additions and 15 deletions

View File

@ -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
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->

View File

@ -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

View File

@ -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
}

View File

@ -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.")

View File

@ -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

View File

@ -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) {