Skip to content

Commit

Permalink
Add expiration LRU cache for webhook token authenticator.
Browse files Browse the repository at this point in the history
  • Loading branch information
cjcullen committed May 18, 2016
1 parent 066e70f commit 57f96a9
Show file tree
Hide file tree
Showing 9 changed files with 38 additions and 18 deletions.
3 changes: 3 additions & 0 deletions cmd/kube-apiserver/app/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type APIServer struct {
ServiceAccountKeyFile string
ServiceAccountLookup bool
WebhookTokenAuthnConfigFile string
WebhookTokenAuthnCacheTTL time.Duration
}

// NewAPIServer creates a new APIServer object with default parameters
Expand All @@ -52,6 +53,7 @@ func NewAPIServer() *APIServer {
EnableHttps: true,
HTTPTimeout: time.Duration(5) * time.Second,
},
WebhookTokenAuthnCacheTTL: 2 * time.Minute,
}
return &s
}
Expand All @@ -66,6 +68,7 @@ func (s *APIServer) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&s.ServiceAccountKeyFile, "service-account-key-file", s.ServiceAccountKeyFile, "File containing PEM-encoded x509 RSA private or public key, used to verify ServiceAccount tokens. If unspecified, --tls-private-key-file is used.")
fs.BoolVar(&s.ServiceAccountLookup, "service-account-lookup", s.ServiceAccountLookup, "If true, validate ServiceAccount tokens exist in etcd as part of authentication.")
fs.StringVar(&s.WebhookTokenAuthnConfigFile, "authentication-token-webhook-config-file", s.WebhookTokenAuthnConfigFile, "File with webhook configuration for token authentication in kubeconfig format. The API server will query the remote service to determine authentication for bearer tokens.")
fs.DurationVar(&s.WebhookTokenAuthnCacheTTL, "authentication-token-webhook-cache-ttl", s.WebhookTokenAuthnCacheTTL, "The duration to cache responses from the webhook token authenticator. Default is 2m")
fs.BoolVar(&s.AllowPrivileged, "allow-privileged", s.AllowPrivileged, "If true, allow privileged containers.")
fs.StringVar(&s.SSHUser, "ssh-user", s.SSHUser, "If non-empty, use secure SSH proxy to the nodes, using this user name")
fs.StringVar(&s.SSHKeyfile, "ssh-keyfile", s.SSHKeyfile, "If non-empty, use secure SSH proxy to the nodes, using this user keyfile")
Expand Down
1 change: 1 addition & 0 deletions cmd/kube-apiserver/app/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ func Run(s *options.APIServer) error {
ServiceAccountTokenGetter: serviceAccountGetter,
KeystoneURL: s.KeystoneURL,
WebhookTokenAuthnConfigFile: s.WebhookTokenAuthnConfigFile,
WebhookTokenAuthnCacheTTL: s.WebhookTokenAuthnCacheTTL,
})

if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion docs/admin/kube-apiserver.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ kube-apiserver
--advertise-address=<nil>: The IP address on which to advertise the apiserver to members of the cluster. This address must be reachable by the rest of the cluster. If blank, the --bind-address will be used. If --bind-address is unspecified, the host's default interface will be used.
--allow-privileged[=false]: If true, allow privileged containers.
--apiserver-count=1: The number of apiservers running in the cluster
--authentication-token-webhook-cache-ttl=2m0s: The duration to cache responses from the webhook token authenticator. Default is 2m
--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.
Expand Down Expand Up @@ -120,7 +121,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 10-May-2016
###### Auto generated by spf13/cobra on 17-May-2016


<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
Expand Down
1 change: 1 addition & 0 deletions hack/verify-flags/known-flags.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ api-token
api-version
apiserver-count
auth-path
authentication-token-webhook-cache-ttl
authentication-token-webhook-config-file
authorization-mode
authorization-policy-file
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/authentication.k8s.io/v1beta1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
)

// TokenReview attempts to authenticate a token to a known user.
// Note: TokenReview requests may be cached by the webhook token authenticator
// plugin in the kube-apiserver.
type TokenReview struct {
unversioned.TypeMeta `json:",inline"`

Expand Down
8 changes: 5 additions & 3 deletions pkg/apiserver/authenticator/authn.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package authenticator

import (
"crypto/rsa"
"time"

"k8s.io/kubernetes/pkg/auth/authenticator"
"k8s.io/kubernetes/pkg/auth/authenticator/bearertoken"
Expand Down Expand Up @@ -47,6 +48,7 @@ type AuthenticatorConfig struct {
ServiceAccountTokenGetter serviceaccount.ServiceAccountTokenGetter
KeystoneURL string
WebhookTokenAuthnConfigFile string
WebhookTokenAuthnCacheTTL time.Duration
}

// New returns an authenticator.Request or an error that supports the standard
Expand Down Expand Up @@ -103,7 +105,7 @@ func New(config AuthenticatorConfig) (authenticator.Request, error) {
}

if len(config.WebhookTokenAuthnConfigFile) > 0 {
webhookTokenAuth, err := newWebhookTokenAuthenticator(config.WebhookTokenAuthnConfigFile)
webhookTokenAuth, err := newWebhookTokenAuthenticator(config.WebhookTokenAuthnConfigFile, config.WebhookTokenAuthnCacheTTL)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -198,8 +200,8 @@ func newAuthenticatorFromKeystoneURL(keystoneURL string) (authenticator.Request,
return basicauth.New(keystoneAuthenticator), nil
}

func newWebhookTokenAuthenticator(webhookConfigFile string) (authenticator.Request, error) {
webhookTokenAuthenticator, err := webhook.New(webhookConfigFile)
func newWebhookTokenAuthenticator(webhookConfigFile string, ttl time.Duration) (authenticator.Request, error) {
webhookTokenAuthenticator, err := webhook.New(webhookConfigFile, ttl)
if err != nil {
return nil, err
}
Expand Down
33 changes: 21 additions & 12 deletions plugin/pkg/auth/authenticator/token/webhook/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ limitations under the License.
package webhook

import (
"time"

"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/authentication.k8s.io/v1beta1"
"k8s.io/kubernetes/pkg/auth/authenticator"
"k8s.io/kubernetes/pkg/auth/user"
"k8s.io/kubernetes/pkg/util/cache"
"k8s.io/kubernetes/plugin/pkg/webhook"

_ "k8s.io/kubernetes/pkg/apis/authentication.k8s.io/install"
Expand All @@ -36,30 +39,36 @@ var _ authenticator.Token = (*WebhookTokenAuthenticator)(nil)

type WebhookTokenAuthenticator struct {
*webhook.GenericWebhook
responseCache *cache.LRUExpireCache
ttl time.Duration
}

// New creates a new WebhookTokenAuthenticator from the provided kubeconfig file.
func New(kubeConfigFile string) (*WebhookTokenAuthenticator, error) {
func New(kubeConfigFile string, ttl time.Duration) (*WebhookTokenAuthenticator, error) {
gw, err := webhook.NewGenericWebhook(kubeConfigFile, groupVersions)
if err != nil {
return nil, err
}
return &WebhookTokenAuthenticator{gw}, nil
return &WebhookTokenAuthenticator{gw, cache.NewLRUExpireCache(1024), ttl}, nil
}

// AuthenticateToken
// AuthenticateToken implements the authenticator.Token interface.
func (w *WebhookTokenAuthenticator) AuthenticateToken(token string) (user.Info, bool, error) {
r := &v1beta1.TokenReview{
Spec: v1beta1.TokenReviewSpec{
Token: token,
},
}
result := w.RestClient.Post().Body(r).Do()
if err := result.Error(); err != nil {
return nil, false, err
Spec: v1beta1.TokenReviewSpec{Token: token},
}
if err := result.Into(r); err != nil {
return nil, false, err
if entry, ok := w.responseCache.Get(r.Spec); ok {
r.Status = entry.(v1beta1.TokenReviewStatus)
} else {
result := w.RestClient.Post().Body(r).Do()
if err := result.Error(); err != nil {
return nil, false, err
}
spec := r.Spec
if err := result.Into(r); err != nil {
return nil, false, err
}
go w.responseCache.Add(spec, r.Status, w.ttl)
}
if !r.Status.Authenticated {
return nil, false, nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ func newTokenAuthenticator(serverURL string, clientCert, clientKey, ca []byte) (
if err := json.NewEncoder(tempfile).Encode(config); err != nil {
return nil, err
}
return New(p)
return New(p, 0)
}

func TestTLSConfig(t *testing.T) {
Expand Down
3 changes: 2 additions & 1 deletion test/integration/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
"strconv"
"strings"
"testing"
"time"

"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
Expand Down Expand Up @@ -85,7 +86,7 @@ func getTestWebhookTokenAuth(serverURL string) (authenticator.Request, error) {
if err := json.NewEncoder(kubecfgFile).Encode(config); err != nil {
return nil, err
}
webhookTokenAuth, err := webhook.New(kubecfgFile.Name())
webhookTokenAuth, err := webhook.New(kubecfgFile.Name(), 2*time.Minute)
if err != nil {
return nil, err
}
Expand Down

0 comments on commit 57f96a9

Please sign in to comment.