Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache webhook authorization responses #25838

Merged
merged 1 commit into from
May 22, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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