Skip to content

Commit

Permalink
Merge pull request kubernetes#25838 from cjcullen/authzcache
Browse files Browse the repository at this point in the history
Automatic merge from submit-queue

Cache webhook authorization responses

Similar to kubernetes#25694, but for authorization.
  • Loading branch information
k8s-merge-robot committed May 22, 2016
2 parents c17465b + d03dbbc commit 9e8282c
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 15 deletions.
4 changes: 3 additions & 1 deletion docs/admin/kube-apiserver.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -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 -->
Expand Down
2 changes: 2 additions & 0 deletions hack/verify-flags/known-flags.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 8 additions & 1 deletion pkg/apiserver/authz.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package apiserver
import (
"errors"
"fmt"
"time"

"k8s.io/kubernetes/pkg/auth/authorizer"
"k8s.io/kubernetes/pkg/auth/authorizer/abac"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down
15 changes: 11 additions & 4 deletions pkg/genericapiserver/options/server_run_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"net"
"strconv"
"strings"
"time"

"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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.")

Expand Down
34 changes: 27 additions & 7 deletions plugin/pkg/auth/authorizer/webhook/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions plugin/pkg/auth/authorizer/webhook/webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 9e8282c

Please sign in to comment.