From a3564c0aa89ac27466d922710af4f4cbeaf9fe6b Mon Sep 17 00:00:00 2001 From: deads2k Date: Wed, 21 Dec 2016 09:53:18 -0500 Subject: [PATCH 1/2] start kubeapiserver package for sharing between kubeapiserver and federation --- cmd/kube-apiserver/app/BUILD | 1 - cmd/kube-apiserver/app/options/BUILD | 1 + cmd/kube-apiserver/app/options/options.go | 5 +- cmd/kube-apiserver/app/server.go | 3 +- federation/cmd/federation-apiserver/app/BUILD | 1 - .../federation-apiserver/app/options/BUILD | 1 + .../app/options/options.go | 5 +- .../cmd/federation-apiserver/app/server.go | 5 +- pkg/genericapiserver/authorizer/BUILD | 4 - pkg/genericapiserver/authorizer/authz_test.go | 80 ----------- pkg/genericapiserver/authorizer/builtin.go | 108 --------------- pkg/genericapiserver/options/BUILD | 1 - pkg/genericapiserver/options/authorization.go | 68 --------- pkg/kubeapiserver/BUILD | 14 ++ pkg/kubeapiserver/authorizer/BUILD | 35 +++++ pkg/kubeapiserver/authorizer/config.go | 131 ++++++++++++++++++ pkg/kubeapiserver/authorizer/config_test.go | 100 +++++++++++++ pkg/kubeapiserver/doc.go | 21 +++ pkg/kubeapiserver/options/BUILD | 19 +++ pkg/kubeapiserver/options/authorization.go | 93 +++++++++++++ 20 files changed, 424 insertions(+), 272 deletions(-) create mode 100644 pkg/kubeapiserver/BUILD create mode 100644 pkg/kubeapiserver/authorizer/BUILD create mode 100644 pkg/kubeapiserver/authorizer/config.go create mode 100644 pkg/kubeapiserver/authorizer/config_test.go create mode 100644 pkg/kubeapiserver/doc.go create mode 100644 pkg/kubeapiserver/options/BUILD create mode 100644 pkg/kubeapiserver/options/authorization.go diff --git a/cmd/kube-apiserver/app/BUILD b/cmd/kube-apiserver/app/BUILD index 7a9c479fe22..a29d567fcd1 100644 --- a/cmd/kube-apiserver/app/BUILD +++ b/cmd/kube-apiserver/app/BUILD @@ -30,7 +30,6 @@ go_library( "//pkg/controller/serviceaccount:go_default_library", "//pkg/generated/openapi:go_default_library", "//pkg/genericapiserver:go_default_library", - "//pkg/genericapiserver/authorizer:go_default_library", "//pkg/genericapiserver/filters:go_default_library", "//pkg/master:go_default_library", "//pkg/registry/cachesize:go_default_library", diff --git a/cmd/kube-apiserver/app/options/BUILD b/cmd/kube-apiserver/app/options/BUILD index 6ee21db5681..d5c3b106c6f 100644 --- a/cmd/kube-apiserver/app/options/BUILD +++ b/cmd/kube-apiserver/app/options/BUILD @@ -19,6 +19,7 @@ go_library( "//pkg/api:go_default_library", "//pkg/api/validation:go_default_library", "//pkg/genericapiserver/options:go_default_library", + "//pkg/kubeapiserver/options:go_default_library", "//pkg/kubelet/client:go_default_library", "//pkg/master/ports:go_default_library", "//pkg/util/net:go_default_library", diff --git a/cmd/kube-apiserver/app/options/options.go b/cmd/kube-apiserver/app/options/options.go index cbfcd221c68..6082f2be6e9 100644 --- a/cmd/kube-apiserver/app/options/options.go +++ b/cmd/kube-apiserver/app/options/options.go @@ -24,6 +24,7 @@ import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/validation" genericoptions "k8s.io/kubernetes/pkg/genericapiserver/options" + kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options" kubeletclient "k8s.io/kubernetes/pkg/kubelet/client" "k8s.io/kubernetes/pkg/master/ports" utilnet "k8s.io/kubernetes/pkg/util/net" @@ -41,7 +42,7 @@ type ServerRunOptions struct { SecureServing *genericoptions.SecureServingOptions InsecureServing *genericoptions.ServingOptions Authentication *genericoptions.BuiltInAuthenticationOptions - Authorization *genericoptions.BuiltInAuthorizationOptions + Authorization *kubeoptions.BuiltInAuthorizationOptions AllowPrivileged bool EventTTL time.Duration @@ -63,7 +64,7 @@ func NewServerRunOptions() *ServerRunOptions { SecureServing: genericoptions.NewSecureServingOptions(), InsecureServing: genericoptions.NewInsecureServingOptions(), Authentication: genericoptions.NewBuiltInAuthenticationOptions().WithAll(), - Authorization: genericoptions.NewBuiltInAuthorizationOptions(), + Authorization: kubeoptions.NewBuiltInAuthorizationOptions(), EventTTL: 1 * time.Hour, MasterCount: 1, diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index 7e1c41a1ee7..6c6137c4d40 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -49,7 +49,6 @@ import ( serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount" generatedopenapi "k8s.io/kubernetes/pkg/generated/openapi" "k8s.io/kubernetes/pkg/genericapiserver" - "k8s.io/kubernetes/pkg/genericapiserver/authorizer" "k8s.io/kubernetes/pkg/genericapiserver/filters" "k8s.io/kubernetes/pkg/master" "k8s.io/kubernetes/pkg/registry/cachesize" @@ -261,7 +260,7 @@ func Run(s *options.ServerRunOptions) error { sharedInformers := informers.NewSharedInformerFactory(nil, client, 10*time.Minute) authorizationConfig := s.Authorization.ToAuthorizationConfig(sharedInformers) - apiAuthorizer, err := authorizer.NewAuthorizerFromAuthorizationConfig(authorizationConfig) + apiAuthorizer, err := authorizationConfig.New() if err != nil { return fmt.Errorf("invalid Authorization Config: %v", err) } diff --git a/federation/cmd/federation-apiserver/app/BUILD b/federation/cmd/federation-apiserver/app/BUILD index 08f4ebc006b..d28d2d7c9fc 100644 --- a/federation/cmd/federation-apiserver/app/BUILD +++ b/federation/cmd/federation-apiserver/app/BUILD @@ -41,7 +41,6 @@ go_library( "//pkg/controller/informers:go_default_library", "//pkg/generated/openapi:go_default_library", "//pkg/genericapiserver:go_default_library", - "//pkg/genericapiserver/authorizer:go_default_library", "//pkg/genericapiserver/filters:go_default_library", "//pkg/registry/batch/job/etcd:go_default_library", "//pkg/registry/cachesize:go_default_library", diff --git a/federation/cmd/federation-apiserver/app/options/BUILD b/federation/cmd/federation-apiserver/app/options/BUILD index cfe6761af25..86df67734e6 100644 --- a/federation/cmd/federation-apiserver/app/options/BUILD +++ b/federation/cmd/federation-apiserver/app/options/BUILD @@ -16,6 +16,7 @@ go_library( tags = ["automanaged"], deps = [ "//pkg/genericapiserver/options:go_default_library", + "//pkg/kubeapiserver/options:go_default_library", "//vendor:github.com/spf13/pflag", ], ) diff --git a/federation/cmd/federation-apiserver/app/options/options.go b/federation/cmd/federation-apiserver/app/options/options.go index 1c5f7a86fe7..f05a551c0ed 100644 --- a/federation/cmd/federation-apiserver/app/options/options.go +++ b/federation/cmd/federation-apiserver/app/options/options.go @@ -21,6 +21,7 @@ import ( "time" genericoptions "k8s.io/kubernetes/pkg/genericapiserver/options" + kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options" "github.com/spf13/pflag" ) @@ -32,7 +33,7 @@ type ServerRunOptions struct { SecureServing *genericoptions.SecureServingOptions InsecureServing *genericoptions.ServingOptions Authentication *genericoptions.BuiltInAuthenticationOptions - Authorization *genericoptions.BuiltInAuthorizationOptions + Authorization *kubeoptions.BuiltInAuthorizationOptions EventTTL time.Duration } @@ -45,7 +46,7 @@ func NewServerRunOptions() *ServerRunOptions { SecureServing: genericoptions.NewSecureServingOptions(), InsecureServing: genericoptions.NewInsecureServingOptions(), Authentication: genericoptions.NewBuiltInAuthenticationOptions().WithAll(), - Authorization: genericoptions.NewBuiltInAuthorizationOptions(), + Authorization: kubeoptions.NewBuiltInAuthorizationOptions(), EventTTL: 1 * time.Hour, } diff --git a/federation/cmd/federation-apiserver/app/server.go b/federation/cmd/federation-apiserver/app/server.go index 4cca4461d99..9c292293ffa 100644 --- a/federation/cmd/federation-apiserver/app/server.go +++ b/federation/cmd/federation-apiserver/app/server.go @@ -37,7 +37,6 @@ import ( "k8s.io/kubernetes/pkg/controller/informers" "k8s.io/kubernetes/pkg/generated/openapi" "k8s.io/kubernetes/pkg/genericapiserver" - "k8s.io/kubernetes/pkg/genericapiserver/authorizer" "k8s.io/kubernetes/pkg/genericapiserver/filters" "k8s.io/kubernetes/pkg/registry/cachesize" "k8s.io/kubernetes/pkg/registry/generic" @@ -151,8 +150,8 @@ func Run(s *options.ServerRunOptions) error { } sharedInformers := informers.NewSharedInformerFactory(nil, client, 10*time.Minute) - authorizerconfig := s.Authorization.ToAuthorizationConfig(sharedInformers) - apiAuthorizer, err := authorizer.NewAuthorizerFromAuthorizationConfig(authorizerconfig) + authorizationConfig := s.Authorization.ToAuthorizationConfig(sharedInformers) + apiAuthorizer, err := authorizationConfig.New() if err != nil { return fmt.Errorf("invalid Authorization Config: %v", err) } diff --git a/pkg/genericapiserver/authorizer/BUILD b/pkg/genericapiserver/authorizer/BUILD index 26ac63965e5..3f9dc29b00d 100644 --- a/pkg/genericapiserver/authorizer/BUILD +++ b/pkg/genericapiserver/authorizer/BUILD @@ -17,11 +17,7 @@ go_library( tags = ["automanaged"], deps = [ "//pkg/auth/authorizer:go_default_library", - "//pkg/auth/authorizer/abac:go_default_library", - "//pkg/auth/authorizer/union:go_default_library", "//pkg/client/clientset_generated/clientset/typed/authorization/v1beta1:go_default_library", - "//pkg/controller/informers:go_default_library", - "//plugin/pkg/auth/authorizer/rbac:go_default_library", "//plugin/pkg/auth/authorizer/webhook:go_default_library", ], ) diff --git a/pkg/genericapiserver/authorizer/authz_test.go b/pkg/genericapiserver/authorizer/authz_test.go index ec53e5be412..11f0ea34c00 100644 --- a/pkg/genericapiserver/authorizer/authz_test.go +++ b/pkg/genericapiserver/authorizer/authz_test.go @@ -41,86 +41,6 @@ func TestNewAlwaysDenyAuthorizer(t *testing.T) { } } -// NewAuthorizerFromAuthorizationConfig has multiple return possibilities. This test -// validates that errors are returned only when proper. -func TestNewAuthorizerFromAuthorizationConfig(t *testing.T) { - - examplePolicyFile := "../../auth/authorizer/abac/example_policy_file.jsonl" - - tests := []struct { - config AuthorizationConfig - wantErr bool - msg string - }{ - { - // Unknown modes should return errors - config: AuthorizationConfig{AuthorizationModes: []string{"DoesNotExist"}}, - wantErr: true, - msg: "using a fake mode should have returned an error", - }, - { - // ModeAlwaysAllow and ModeAlwaysDeny should return without authorizationPolicyFile - // but error if one is given - config: AuthorizationConfig{AuthorizationModes: []string{ModeAlwaysAllow, ModeAlwaysDeny}}, - msg: "returned an error for valid config", - }, - { - // ModeABAC requires a policy file - config: AuthorizationConfig{AuthorizationModes: []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeABAC}}, - wantErr: true, - msg: "specifying ABAC with no policy file should return an error", - }, - { - // ModeABAC should not error if a valid policy path is provided - config: AuthorizationConfig{ - AuthorizationModes: []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeABAC}, - PolicyFile: examplePolicyFile, - }, - msg: "errored while using a valid policy file", - }, - { - - // Authorization Policy file cannot be used without ModeABAC - config: AuthorizationConfig{ - AuthorizationModes: []string{ModeAlwaysAllow, ModeAlwaysDeny}, - PolicyFile: examplePolicyFile, - }, - wantErr: true, - msg: "should have errored when Authorization Policy File is used without ModeABAC", - }, - { - // At least one authorizationMode is necessary - config: AuthorizationConfig{PolicyFile: examplePolicyFile}, - wantErr: true, - msg: "should have errored when no authorization modes are passed", - }, - { - // ModeWebhook requires at minimum a target. - config: AuthorizationConfig{AuthorizationModes: []string{ModeWebhook}}, - wantErr: true, - msg: "should have errored when config was empty with ModeWebhook", - }, - { - // Cannot provide webhook flags without ModeWebhook - config: AuthorizationConfig{ - AuthorizationModes: []string{ModeAlwaysAllow}, - WebhookConfigFile: "authz_webhook_config.yml", - }, - wantErr: true, - msg: "should have errored when Webhook config file is used without ModeWebhook", - }, - } - - for _, tt := range tests { - _, err := NewAuthorizerFromAuthorizationConfig(tt.config) - if tt.wantErr && (err == nil) { - t.Errorf("NewAuthorizerFromAuthorizationConfig %s", tt.msg) - } else if !tt.wantErr && (err != nil) { - t.Errorf("NewAuthorizerFromAuthorizationConfig %s: %v", tt.msg, err) - } - } -} - func TestPrivilegedGroupAuthorizer(t *testing.T) { auth := NewPrivilegedGroups("allow-01", "allow-01") diff --git a/pkg/genericapiserver/authorizer/builtin.go b/pkg/genericapiserver/authorizer/builtin.go index 63c6db61adc..a5ea71f9b6e 100644 --- a/pkg/genericapiserver/authorizer/builtin.go +++ b/pkg/genericapiserver/authorizer/builtin.go @@ -18,23 +18,8 @@ package authorizer import ( "errors" - "fmt" - "time" "k8s.io/kubernetes/pkg/auth/authorizer" - "k8s.io/kubernetes/pkg/auth/authorizer/abac" - "k8s.io/kubernetes/pkg/auth/authorizer/union" - "k8s.io/kubernetes/pkg/controller/informers" - "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac" - "k8s.io/kubernetes/plugin/pkg/auth/authorizer/webhook" -) - -const ( - ModeAlwaysAllow string = "AlwaysAllow" - ModeAlwaysDeny string = "AlwaysDeny" - ModeABAC string = "ABAC" - ModeWebhook string = "Webhook" - ModeRBAC string = "RBAC" ) // alwaysAllowAuthorizer is an implementation of authorizer.Attributes @@ -100,96 +85,3 @@ func NewPrivilegedGroups(groups ...string) *privilegedGroupAuthorizer { groups: groups, } } - -type AuthorizationConfig struct { - AuthorizationModes []string - - // Options for ModeABAC - - // Path to an ABAC policy file. - PolicyFile string - - // Options for ModeWebhook - - // 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 - - // Options for RBAC - - // User which can bootstrap role policies - RBACSuperUser string - - InformerFactory informers.SharedInformerFactory -} - -// NewAuthorizerFromAuthorizationConfig returns the right sort of union of multiple authorizer.Authorizer objects -// based on the authorizationMode or an error. -func NewAuthorizerFromAuthorizationConfig(config AuthorizationConfig) (authorizer.Authorizer, error) { - - if len(config.AuthorizationModes) == 0 { - return nil, errors.New("At least one authorization mode should be passed") - } - - var authorizers []authorizer.Authorizer - authorizerMap := make(map[string]bool) - - for _, authorizationMode := range config.AuthorizationModes { - if authorizerMap[authorizationMode] { - return nil, fmt.Errorf("Authorization mode %s specified more than once", authorizationMode) - } - // Keep cases in sync with constant list above. - switch authorizationMode { - case ModeAlwaysAllow: - authorizers = append(authorizers, NewAlwaysAllowAuthorizer()) - case ModeAlwaysDeny: - authorizers = append(authorizers, NewAlwaysDenyAuthorizer()) - case ModeABAC: - if config.PolicyFile == "" { - return nil, errors.New("ABAC's authorization policy file not passed") - } - abacAuthorizer, err := abac.NewFromFile(config.PolicyFile) - if err != nil { - return nil, err - } - authorizers = append(authorizers, abacAuthorizer) - case ModeWebhook: - if config.WebhookConfigFile == "" { - return nil, errors.New("Webhook's configuration file not passed") - } - webhookAuthorizer, err := webhook.New(config.WebhookConfigFile, - config.WebhookCacheAuthorizedTTL, - config.WebhookCacheUnauthorizedTTL) - if err != nil { - return nil, err - } - authorizers = append(authorizers, webhookAuthorizer) - case ModeRBAC: - rbacAuthorizer := rbac.New( - config.InformerFactory.Roles().Lister(), - config.InformerFactory.RoleBindings().Lister(), - config.InformerFactory.ClusterRoles().Lister(), - config.InformerFactory.ClusterRoleBindings().Lister(), - ) - authorizers = append(authorizers, rbacAuthorizer) - default: - return nil, fmt.Errorf("Unknown authorization mode %s specified", authorizationMode) - } - authorizerMap[authorizationMode] = true - } - - if !authorizerMap[ModeABAC] && config.PolicyFile != "" { - return nil, errors.New("Cannot specify --authorization-policy-file without mode ABAC") - } - if !authorizerMap[ModeWebhook] && config.WebhookConfigFile != "" { - return nil, errors.New("Cannot specify --authorization-webhook-config-file without mode Webhook") - } - if !authorizerMap[ModeRBAC] && config.RBACSuperUser != "" { - return nil, errors.New("Cannot specify --authorization-rbac-super-user without mode RBAC") - } - - return union.New(authorizers...), nil -} diff --git a/pkg/genericapiserver/options/BUILD b/pkg/genericapiserver/options/BUILD index bcb81315a43..f41f222bb86 100644 --- a/pkg/genericapiserver/options/BUILD +++ b/pkg/genericapiserver/options/BUILD @@ -29,7 +29,6 @@ go_library( "//pkg/client/restclient:go_default_library", "//pkg/client/unversioned/clientcmd:go_default_library", "//pkg/cloudprovider:go_default_library", - "//pkg/controller/informers:go_default_library", "//pkg/genericapiserver/authorizer:go_default_library", "//pkg/runtime/schema:go_default_library", "//pkg/storage/storagebackend:go_default_library", diff --git a/pkg/genericapiserver/options/authorization.go b/pkg/genericapiserver/options/authorization.go index f6dfb1d814d..4e544c0e3ee 100644 --- a/pkg/genericapiserver/options/authorization.go +++ b/pkg/genericapiserver/options/authorization.go @@ -17,7 +17,6 @@ limitations under the License. package options import ( - "strings" "time" "github.com/spf13/pflag" @@ -25,76 +24,9 @@ import ( authorizationclient "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/authorization/v1beta1" "k8s.io/kubernetes/pkg/client/restclient" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" - "k8s.io/kubernetes/pkg/controller/informers" "k8s.io/kubernetes/pkg/genericapiserver/authorizer" ) -var AuthorizationModeChoices = []string{authorizer.ModeAlwaysAllow, authorizer.ModeAlwaysDeny, authorizer.ModeABAC, authorizer.ModeWebhook, authorizer.ModeRBAC} - -type BuiltInAuthorizationOptions struct { - Mode string - PolicyFile string - WebhookConfigFile string - WebhookCacheAuthorizedTTL time.Duration - WebhookCacheUnauthorizedTTL time.Duration -} - -func NewBuiltInAuthorizationOptions() *BuiltInAuthorizationOptions { - return &BuiltInAuthorizationOptions{ - Mode: authorizer.ModeAlwaysAllow, - WebhookCacheAuthorizedTTL: 5 * time.Minute, - WebhookCacheUnauthorizedTTL: 30 * time.Second, - } -} - -func (s *BuiltInAuthorizationOptions) Validate() []error { - allErrors := []error{} - return allErrors -} - -func (s *BuiltInAuthorizationOptions) AddFlags(fs *pflag.FlagSet) { - fs.StringVar(&s.Mode, "authorization-mode", s.Mode, ""+ - "Ordered list of plug-ins to do authorization on secure port. Comma-delimited list of: "+ - strings.Join(AuthorizationModeChoices, ",")+".") - - fs.StringVar(&s.PolicyFile, "authorization-policy-file", s.PolicyFile, ""+ - "File with authorization policy in csv format, used with --authorization-mode=ABAC, on the secure port.") - - fs.StringVar(&s.WebhookConfigFile, "authorization-webhook-config-file", s.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.WebhookCacheAuthorizedTTL, "authorization-webhook-cache-authorized-ttl", - s.WebhookCacheAuthorizedTTL, - "The duration to cache 'authorized' responses from the webhook authorizer. Default is 5m.") - - fs.DurationVar(&s.WebhookCacheUnauthorizedTTL, - "authorization-webhook-cache-unauthorized-ttl", s.WebhookCacheUnauthorizedTTL, - "The duration to cache 'unauthorized' responses from the webhook authorizer. Default is 30s.") - - fs.String("authorization-rbac-super-user", "", ""+ - "If specified, a username which avoids RBAC authorization checks and role binding "+ - "privilege escalation checks, to be used with --authorization-mode=RBAC.") - fs.MarkDeprecated("authorization-rbac-super-user", "Removed during alpha to beta. The 'system:masters' group has privileged access.") - -} - -func (s *BuiltInAuthorizationOptions) ToAuthorizationConfig(informerFactory informers.SharedInformerFactory) authorizer.AuthorizationConfig { - modes := []string{} - if len(s.Mode) > 0 { - modes = strings.Split(s.Mode, ",") - } - - return authorizer.AuthorizationConfig{ - AuthorizationModes: modes, - PolicyFile: s.PolicyFile, - WebhookConfigFile: s.WebhookConfigFile, - WebhookCacheAuthorizedTTL: s.WebhookCacheAuthorizedTTL, - WebhookCacheUnauthorizedTTL: s.WebhookCacheUnauthorizedTTL, - InformerFactory: informerFactory, - } -} - // DelegatingAuthorizationOptions provides an easy way for composing API servers to delegate their authorization to // the root kube API server type DelegatingAuthorizationOptions struct { diff --git a/pkg/kubeapiserver/BUILD b/pkg/kubeapiserver/BUILD new file mode 100644 index 00000000000..72e02123044 --- /dev/null +++ b/pkg/kubeapiserver/BUILD @@ -0,0 +1,14 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", +) + +go_library( + name = "go_default_library", + srcs = ["doc.go"], + tags = ["automanaged"], +) diff --git a/pkg/kubeapiserver/authorizer/BUILD b/pkg/kubeapiserver/authorizer/BUILD new file mode 100644 index 00000000000..cb105469a93 --- /dev/null +++ b/pkg/kubeapiserver/authorizer/BUILD @@ -0,0 +1,35 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", + "go_test", +) + +go_test( + name = "go_default_test", + srcs = ["config_test.go"], + data = [ + "//pkg/auth/authorizer/abac:example_policy", + ], + library = "go_default_library", + tags = ["automanaged"], + deps = [], +) + +go_library( + name = "go_default_library", + srcs = ["config.go"], + tags = ["automanaged"], + deps = [ + "//pkg/auth/authorizer:go_default_library", + "//pkg/auth/authorizer/abac:go_default_library", + "//pkg/auth/authorizer/union:go_default_library", + "//pkg/controller/informers:go_default_library", + "//pkg/genericapiserver/authorizer:go_default_library", + "//plugin/pkg/auth/authorizer/rbac:go_default_library", + "//plugin/pkg/auth/authorizer/webhook:go_default_library", + ], +) diff --git a/pkg/kubeapiserver/authorizer/config.go b/pkg/kubeapiserver/authorizer/config.go new file mode 100644 index 00000000000..1ebecdea3b5 --- /dev/null +++ b/pkg/kubeapiserver/authorizer/config.go @@ -0,0 +1,131 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package authorizer + +import ( + "errors" + "fmt" + "time" + + "k8s.io/kubernetes/pkg/auth/authorizer" + "k8s.io/kubernetes/pkg/auth/authorizer/abac" + "k8s.io/kubernetes/pkg/auth/authorizer/union" + "k8s.io/kubernetes/pkg/controller/informers" + genericauthorizer "k8s.io/kubernetes/pkg/genericapiserver/authorizer" + "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac" + "k8s.io/kubernetes/plugin/pkg/auth/authorizer/webhook" +) + +const ( + ModeAlwaysAllow string = "AlwaysAllow" + ModeAlwaysDeny string = "AlwaysDeny" + ModeABAC string = "ABAC" + ModeWebhook string = "Webhook" + ModeRBAC string = "RBAC" +) + +type AuthorizationConfig struct { + AuthorizationModes []string + + // Options for ModeABAC + + // Path to an ABAC policy file. + PolicyFile string + + // Options for ModeWebhook + + // 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 + + // Options for RBAC + + // User which can bootstrap role policies + RBACSuperUser string + + InformerFactory informers.SharedInformerFactory +} + +// New returns the right sort of union of multiple authorizer.Authorizer objects +// based on the authorizationMode or an error. +func (config AuthorizationConfig) New() (authorizer.Authorizer, error) { + if len(config.AuthorizationModes) == 0 { + return nil, errors.New("At least one authorization mode should be passed") + } + + var authorizers []authorizer.Authorizer + authorizerMap := make(map[string]bool) + + for _, authorizationMode := range config.AuthorizationModes { + if authorizerMap[authorizationMode] { + return nil, fmt.Errorf("Authorization mode %s specified more than once", authorizationMode) + } + // Keep cases in sync with constant list above. + switch authorizationMode { + case ModeAlwaysAllow: + authorizers = append(authorizers, genericauthorizer.NewAlwaysAllowAuthorizer()) + case ModeAlwaysDeny: + authorizers = append(authorizers, genericauthorizer.NewAlwaysDenyAuthorizer()) + case ModeABAC: + if config.PolicyFile == "" { + return nil, errors.New("ABAC's authorization policy file not passed") + } + abacAuthorizer, err := abac.NewFromFile(config.PolicyFile) + if err != nil { + return nil, err + } + authorizers = append(authorizers, abacAuthorizer) + case ModeWebhook: + if config.WebhookConfigFile == "" { + return nil, errors.New("Webhook's configuration file not passed") + } + webhookAuthorizer, err := webhook.New(config.WebhookConfigFile, + config.WebhookCacheAuthorizedTTL, + config.WebhookCacheUnauthorizedTTL) + if err != nil { + return nil, err + } + authorizers = append(authorizers, webhookAuthorizer) + case ModeRBAC: + rbacAuthorizer := rbac.New( + config.InformerFactory.Roles().Lister(), + config.InformerFactory.RoleBindings().Lister(), + config.InformerFactory.ClusterRoles().Lister(), + config.InformerFactory.ClusterRoleBindings().Lister(), + ) + authorizers = append(authorizers, rbacAuthorizer) + default: + return nil, fmt.Errorf("Unknown authorization mode %s specified", authorizationMode) + } + authorizerMap[authorizationMode] = true + } + + if !authorizerMap[ModeABAC] && config.PolicyFile != "" { + return nil, errors.New("Cannot specify --authorization-policy-file without mode ABAC") + } + if !authorizerMap[ModeWebhook] && config.WebhookConfigFile != "" { + return nil, errors.New("Cannot specify --authorization-webhook-config-file without mode Webhook") + } + if !authorizerMap[ModeRBAC] && config.RBACSuperUser != "" { + return nil, errors.New("Cannot specify --authorization-rbac-super-user without mode RBAC") + } + + return union.New(authorizers...), nil +} diff --git a/pkg/kubeapiserver/authorizer/config_test.go b/pkg/kubeapiserver/authorizer/config_test.go new file mode 100644 index 00000000000..2353cbce3c8 --- /dev/null +++ b/pkg/kubeapiserver/authorizer/config_test.go @@ -0,0 +1,100 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package authorizer + +import ( + "testing" +) + +// New has multiple return possibilities. This test +// validates that errors are returned only when proper. +func TestNew(t *testing.T) { + examplePolicyFile := "../../auth/authorizer/abac/example_policy_file.jsonl" + + tests := []struct { + config AuthorizationConfig + wantErr bool + msg string + }{ + { + // Unknown modes should return errors + config: AuthorizationConfig{AuthorizationModes: []string{"DoesNotExist"}}, + wantErr: true, + msg: "using a fake mode should have returned an error", + }, + { + // ModeAlwaysAllow and ModeAlwaysDeny should return without authorizationPolicyFile + // but error if one is given + config: AuthorizationConfig{AuthorizationModes: []string{ModeAlwaysAllow, ModeAlwaysDeny}}, + msg: "returned an error for valid config", + }, + { + // ModeABAC requires a policy file + config: AuthorizationConfig{AuthorizationModes: []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeABAC}}, + wantErr: true, + msg: "specifying ABAC with no policy file should return an error", + }, + { + // ModeABAC should not error if a valid policy path is provided + config: AuthorizationConfig{ + AuthorizationModes: []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeABAC}, + PolicyFile: examplePolicyFile, + }, + msg: "errored while using a valid policy file", + }, + { + + // Authorization Policy file cannot be used without ModeABAC + config: AuthorizationConfig{ + AuthorizationModes: []string{ModeAlwaysAllow, ModeAlwaysDeny}, + PolicyFile: examplePolicyFile, + }, + wantErr: true, + msg: "should have errored when Authorization Policy File is used without ModeABAC", + }, + { + // At least one authorizationMode is necessary + config: AuthorizationConfig{PolicyFile: examplePolicyFile}, + wantErr: true, + msg: "should have errored when no authorization modes are passed", + }, + { + // ModeWebhook requires at minimum a target. + config: AuthorizationConfig{AuthorizationModes: []string{ModeWebhook}}, + wantErr: true, + msg: "should have errored when config was empty with ModeWebhook", + }, + { + // Cannot provide webhook flags without ModeWebhook + config: AuthorizationConfig{ + AuthorizationModes: []string{ModeAlwaysAllow}, + WebhookConfigFile: "authz_webhook_config.yml", + }, + wantErr: true, + msg: "should have errored when Webhook config file is used without ModeWebhook", + }, + } + + for _, tt := range tests { + _, err := tt.config.New() + if tt.wantErr && (err == nil) { + t.Errorf("New %s", tt.msg) + } else if !tt.wantErr && (err != nil) { + t.Errorf("New %s: %v", tt.msg, err) + } + } +} diff --git a/pkg/kubeapiserver/doc.go b/pkg/kubeapiserver/doc.go new file mode 100644 index 00000000000..aa18901607b --- /dev/null +++ b/pkg/kubeapiserver/doc.go @@ -0,0 +1,21 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// The kubapiserver package holds code that is common to both the kube-apiserver +// and the federation-apiserver, but isn't part of a generic API server. +// For instance, the non-delegated authorization options are used by those two +// servers, but no generic API server is likely to use them. +package kubeapiserver diff --git a/pkg/kubeapiserver/options/BUILD b/pkg/kubeapiserver/options/BUILD new file mode 100644 index 00000000000..bafc2f7d236 --- /dev/null +++ b/pkg/kubeapiserver/options/BUILD @@ -0,0 +1,19 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", +) + +go_library( + name = "go_default_library", + srcs = ["authorization.go"], + tags = ["automanaged"], + deps = [ + "//pkg/controller/informers:go_default_library", + "//pkg/kubeapiserver/authorizer:go_default_library", + "//vendor:github.com/spf13/pflag", + ], +) diff --git a/pkg/kubeapiserver/options/authorization.go b/pkg/kubeapiserver/options/authorization.go new file mode 100644 index 00000000000..96d378d3dcc --- /dev/null +++ b/pkg/kubeapiserver/options/authorization.go @@ -0,0 +1,93 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package options + +import ( + "strings" + "time" + + "github.com/spf13/pflag" + + "k8s.io/kubernetes/pkg/controller/informers" + "k8s.io/kubernetes/pkg/kubeapiserver/authorizer" +) + +var AuthorizationModeChoices = []string{authorizer.ModeAlwaysAllow, authorizer.ModeAlwaysDeny, authorizer.ModeABAC, authorizer.ModeWebhook, authorizer.ModeRBAC} + +type BuiltInAuthorizationOptions struct { + Mode string + PolicyFile string + WebhookConfigFile string + WebhookCacheAuthorizedTTL time.Duration + WebhookCacheUnauthorizedTTL time.Duration +} + +func NewBuiltInAuthorizationOptions() *BuiltInAuthorizationOptions { + return &BuiltInAuthorizationOptions{ + Mode: authorizer.ModeAlwaysAllow, + WebhookCacheAuthorizedTTL: 5 * time.Minute, + WebhookCacheUnauthorizedTTL: 30 * time.Second, + } +} + +func (s *BuiltInAuthorizationOptions) Validate() []error { + allErrors := []error{} + return allErrors +} + +func (s *BuiltInAuthorizationOptions) AddFlags(fs *pflag.FlagSet) { + fs.StringVar(&s.Mode, "authorization-mode", s.Mode, ""+ + "Ordered list of plug-ins to do authorization on secure port. Comma-delimited list of: "+ + strings.Join(AuthorizationModeChoices, ",")+".") + + fs.StringVar(&s.PolicyFile, "authorization-policy-file", s.PolicyFile, ""+ + "File with authorization policy in csv format, used with --authorization-mode=ABAC, on the secure port.") + + fs.StringVar(&s.WebhookConfigFile, "authorization-webhook-config-file", s.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.WebhookCacheAuthorizedTTL, "authorization-webhook-cache-authorized-ttl", + s.WebhookCacheAuthorizedTTL, + "The duration to cache 'authorized' responses from the webhook authorizer. Default is 5m.") + + fs.DurationVar(&s.WebhookCacheUnauthorizedTTL, + "authorization-webhook-cache-unauthorized-ttl", s.WebhookCacheUnauthorizedTTL, + "The duration to cache 'unauthorized' responses from the webhook authorizer. Default is 30s.") + + fs.String("authorization-rbac-super-user", "", ""+ + "If specified, a username which avoids RBAC authorization checks and role binding "+ + "privilege escalation checks, to be used with --authorization-mode=RBAC.") + fs.MarkDeprecated("authorization-rbac-super-user", "Removed during alpha to beta. The 'system:masters' group has privileged access.") + +} + +func (s *BuiltInAuthorizationOptions) ToAuthorizationConfig(informerFactory informers.SharedInformerFactory) authorizer.AuthorizationConfig { + modes := []string{} + if len(s.Mode) > 0 { + modes = strings.Split(s.Mode, ",") + } + + return authorizer.AuthorizationConfig{ + AuthorizationModes: modes, + PolicyFile: s.PolicyFile, + WebhookConfigFile: s.WebhookConfigFile, + WebhookCacheAuthorizedTTL: s.WebhookCacheAuthorizedTTL, + WebhookCacheUnauthorizedTTL: s.WebhookCacheUnauthorizedTTL, + InformerFactory: informerFactory, + } +} From ab1b77673f152bd0d1481b6342b52a2af1330027 Mon Sep 17 00:00:00 2001 From: deads2k Date: Wed, 21 Dec 2016 12:45:38 -0500 Subject: [PATCH 2/2] decouple genericapiserver from non-generic authenticator --- cmd/kube-apiserver/app/BUILD | 2 +- cmd/kube-apiserver/app/options/options.go | 4 +- cmd/kube-apiserver/app/server.go | 8 +- cmd/kubelet/app/BUILD | 2 +- cmd/kubelet/app/auth.go | 2 +- examples/apiserver/BUILD | 1 + examples/apiserver/apiserver.go | 7 +- federation/cmd/federation-apiserver/app/BUILD | 1 - .../app/options/options.go | 4 +- .../cmd/federation-apiserver/app/server.go | 5 +- pkg/apiserver/authenticator/OWNERS | 10 - pkg/genericapiserver/BUILD | 2 +- pkg/genericapiserver/authenticator/BUILD | 33 ++ .../authenticator/delegating.go | 0 .../authenticator/loopback.go | 29 ++ .../authenticator/requestheader.go | 31 ++ pkg/genericapiserver/config.go | 37 +- pkg/genericapiserver/options/BUILD | 2 +- .../options/authentication.go | 269 +-------------- .../authenticator/BUILD | 7 +- .../authenticator/config.go} | 24 +- pkg/kubeapiserver/options/BUILD | 8 +- pkg/kubeapiserver/options/authentication.go | 318 ++++++++++++++++++ test/integration/framework/BUILD | 2 +- test/integration/framework/master_utils.go | 2 +- 25 files changed, 453 insertions(+), 357 deletions(-) delete mode 100755 pkg/apiserver/authenticator/OWNERS create mode 100644 pkg/genericapiserver/authenticator/BUILD rename pkg/{apiserver => genericapiserver}/authenticator/delegating.go (100%) create mode 100644 pkg/genericapiserver/authenticator/loopback.go create mode 100644 pkg/genericapiserver/authenticator/requestheader.go rename pkg/{apiserver => kubeapiserver}/authenticator/BUILD (89%) rename pkg/{apiserver/authenticator/builtin.go => kubeapiserver/authenticator/config.go} (89%) create mode 100644 pkg/kubeapiserver/options/authentication.go diff --git a/cmd/kube-apiserver/app/BUILD b/cmd/kube-apiserver/app/BUILD index a29d567fcd1..2d9ac3ca58e 100644 --- a/cmd/kube-apiserver/app/BUILD +++ b/cmd/kube-apiserver/app/BUILD @@ -21,7 +21,6 @@ go_library( "//pkg/apis/autoscaling:go_default_library", "//pkg/apis/batch:go_default_library", "//pkg/apis/extensions:go_default_library", - "//pkg/apiserver/authenticator:go_default_library", "//pkg/capabilities:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library", "//pkg/cloudprovider:go_default_library", @@ -31,6 +30,7 @@ go_library( "//pkg/generated/openapi:go_default_library", "//pkg/genericapiserver:go_default_library", "//pkg/genericapiserver/filters:go_default_library", + "//pkg/kubeapiserver/authenticator:go_default_library", "//pkg/master:go_default_library", "//pkg/registry/cachesize:go_default_library", "//pkg/runtime/schema:go_default_library", diff --git a/cmd/kube-apiserver/app/options/options.go b/cmd/kube-apiserver/app/options/options.go index 6082f2be6e9..df9b18281d3 100644 --- a/cmd/kube-apiserver/app/options/options.go +++ b/cmd/kube-apiserver/app/options/options.go @@ -41,7 +41,7 @@ type ServerRunOptions struct { Etcd *genericoptions.EtcdOptions SecureServing *genericoptions.SecureServingOptions InsecureServing *genericoptions.ServingOptions - Authentication *genericoptions.BuiltInAuthenticationOptions + Authentication *kubeoptions.BuiltInAuthenticationOptions Authorization *kubeoptions.BuiltInAuthorizationOptions AllowPrivileged bool @@ -63,7 +63,7 @@ func NewServerRunOptions() *ServerRunOptions { Etcd: genericoptions.NewEtcdOptions(), SecureServing: genericoptions.NewSecureServingOptions(), InsecureServing: genericoptions.NewInsecureServingOptions(), - Authentication: genericoptions.NewBuiltInAuthenticationOptions().WithAll(), + Authentication: kubeoptions.NewBuiltInAuthenticationOptions().WithAll(), Authorization: kubeoptions.NewBuiltInAuthorizationOptions(), EventTTL: 1 * time.Hour, diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index 6c6137c4d40..f52ec29cbcd 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -41,7 +41,6 @@ import ( "k8s.io/kubernetes/pkg/apis/autoscaling" "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/apis/extensions" - "k8s.io/kubernetes/pkg/apiserver/authenticator" "k8s.io/kubernetes/pkg/capabilities" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/cloudprovider" @@ -50,6 +49,7 @@ import ( generatedopenapi "k8s.io/kubernetes/pkg/generated/openapi" "k8s.io/kubernetes/pkg/genericapiserver" "k8s.io/kubernetes/pkg/genericapiserver/filters" + kubeauthenticator "k8s.io/kubernetes/pkg/kubeapiserver/authenticator" "k8s.io/kubernetes/pkg/master" "k8s.io/kubernetes/pkg/registry/cachesize" "k8s.io/kubernetes/pkg/runtime/schema" @@ -107,7 +107,7 @@ func Run(s *options.ServerRunOptions) error { if _, err := genericConfig.ApplySecureServingOptions(s.SecureServing); err != nil { return fmt.Errorf("failed to configure https: %s", err) } - if _, err = genericConfig.ApplyAuthenticationOptions(s.Authentication); err != nil { + if err = s.Authentication.Apply(genericConfig); err != nil { return fmt.Errorf("failed to configure authentication: %s", err) } @@ -217,7 +217,7 @@ func Run(s *options.ServerRunOptions) error { // Default to the private server key for service account token signing if len(s.Authentication.ServiceAccounts.KeyFiles) == 0 && s.SecureServing.ServerCert.CertKey.KeyFile != "" { - if authenticator.IsValidServiceAccountKeyFile(s.SecureServing.ServerCert.CertKey.KeyFile) { + if kubeauthenticator.IsValidServiceAccountKeyFile(s.SecureServing.ServerCert.CertKey.KeyFile) { s.Authentication.ServiceAccounts.KeyFiles = []string{s.SecureServing.ServerCert.CertKey.KeyFile} } else { glog.Warning("No TLS key provided, service account token authentication disabled") @@ -235,7 +235,7 @@ func Run(s *options.ServerRunOptions) error { authenticatorConfig.ServiceAccountTokenGetter = serviceaccountcontroller.NewGetterFromStorageInterface(storageConfig, storageFactory.ResourcePrefix(api.Resource("serviceaccounts")), storageFactory.ResourcePrefix(api.Resource("secrets"))) } - apiAuthenticator, securityDefinitions, err := authenticator.New(authenticatorConfig) + apiAuthenticator, securityDefinitions, err := authenticatorConfig.New() if err != nil { return fmt.Errorf("invalid Authentication Config: %v", err) } diff --git a/cmd/kubelet/app/BUILD b/cmd/kubelet/app/BUILD index a8a30791216..63da956473e 100644 --- a/cmd/kubelet/app/BUILD +++ b/cmd/kubelet/app/BUILD @@ -25,7 +25,6 @@ go_library( "//pkg/apis/componentconfig:go_default_library", "//pkg/apis/componentconfig/v1alpha1:go_default_library", "//pkg/apis/meta/v1:go_default_library", - "//pkg/apiserver/authenticator:go_default_library", "//pkg/auth/authenticator:go_default_library", "//pkg/auth/authorizer:go_default_library", "//pkg/capabilities:go_default_library", @@ -46,6 +45,7 @@ go_library( "//pkg/credentialprovider/aws:go_default_library", "//pkg/credentialprovider/azure:go_default_library", "//pkg/credentialprovider/gcp:go_default_library", + "//pkg/genericapiserver/authenticator:go_default_library", "//pkg/genericapiserver/authorizer:go_default_library", "//pkg/healthz:go_default_library", "//pkg/kubelet:go_default_library", diff --git a/cmd/kubelet/app/auth.go b/cmd/kubelet/app/auth.go index 23a474ad6e9..17954df2f93 100644 --- a/cmd/kubelet/app/auth.go +++ b/cmd/kubelet/app/auth.go @@ -22,12 +22,12 @@ import ( "reflect" "k8s.io/kubernetes/pkg/apis/componentconfig" - apiserverauthenticator "k8s.io/kubernetes/pkg/apiserver/authenticator" "k8s.io/kubernetes/pkg/auth/authenticator" "k8s.io/kubernetes/pkg/auth/authorizer" clientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" authenticationclient "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/authentication/v1beta1" authorizationclient "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/authorization/v1beta1" + apiserverauthenticator "k8s.io/kubernetes/pkg/genericapiserver/authenticator" alwaysallowauthorizer "k8s.io/kubernetes/pkg/genericapiserver/authorizer" apiserverauthorizer "k8s.io/kubernetes/pkg/genericapiserver/authorizer" "k8s.io/kubernetes/pkg/kubelet/server" diff --git a/examples/apiserver/BUILD b/examples/apiserver/BUILD index 002164598eb..6c472fc175d 100644 --- a/examples/apiserver/BUILD +++ b/examples/apiserver/BUILD @@ -21,6 +21,7 @@ go_library( "//pkg/genericapiserver:go_default_library", "//pkg/genericapiserver/authorizer:go_default_library", "//pkg/genericapiserver/options:go_default_library", + "//pkg/kubeapiserver/options:go_default_library", "//pkg/registry/generic:go_default_library", "//pkg/runtime/schema:go_default_library", "//pkg/storage/storagebackend:go_default_library", diff --git a/examples/apiserver/apiserver.go b/examples/apiserver/apiserver.go index 61abfca47e7..28aa112ed7f 100644 --- a/examples/apiserver/apiserver.go +++ b/examples/apiserver/apiserver.go @@ -27,6 +27,7 @@ import ( "k8s.io/kubernetes/pkg/genericapiserver" "k8s.io/kubernetes/pkg/genericapiserver/authorizer" genericoptions "k8s.io/kubernetes/pkg/genericapiserver/options" + kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options" "k8s.io/kubernetes/pkg/registry/generic" "k8s.io/kubernetes/pkg/runtime/schema" "k8s.io/kubernetes/pkg/storage/storagebackend" @@ -60,7 +61,7 @@ type ServerRunOptions struct { Etcd *genericoptions.EtcdOptions SecureServing *genericoptions.SecureServingOptions InsecureServing *genericoptions.ServingOptions - Authentication *genericoptions.BuiltInAuthenticationOptions + Authentication *kubeoptions.BuiltInAuthenticationOptions } func NewServerRunOptions() *ServerRunOptions { @@ -69,7 +70,7 @@ func NewServerRunOptions() *ServerRunOptions { Etcd: genericoptions.NewEtcdOptions(), SecureServing: genericoptions.NewSecureServingOptions(), InsecureServing: genericoptions.NewInsecureServingOptions(), - Authentication: genericoptions.NewBuiltInAuthenticationOptions().WithAll(), + Authentication: kubeoptions.NewBuiltInAuthenticationOptions().WithAll(), } s.InsecureServing.BindPort = InsecurePort s.SecureServing.ServingOptions.BindPort = SecurePort @@ -107,7 +108,7 @@ func (serverOptions *ServerRunOptions) Run(stopCh <-chan struct{}) error { if _, err := config.ApplySecureServingOptions(serverOptions.SecureServing); err != nil { return fmt.Errorf("failed to configure https: %s", err) } - if _, err := config.ApplyAuthenticationOptions(serverOptions.Authentication); err != nil { + if err := serverOptions.Authentication.Apply(config); err != nil { return fmt.Errorf("failed to configure authentication: %s", err) } diff --git a/federation/cmd/federation-apiserver/app/BUILD b/federation/cmd/federation-apiserver/app/BUILD index d28d2d7c9fc..d487bbbb1b5 100644 --- a/federation/cmd/federation-apiserver/app/BUILD +++ b/federation/cmd/federation-apiserver/app/BUILD @@ -35,7 +35,6 @@ go_library( "//pkg/apis/batch/install:go_default_library", "//pkg/apis/extensions:go_default_library", "//pkg/apis/extensions/install:go_default_library", - "//pkg/apiserver/authenticator:go_default_library", "//pkg/client/clientset_generated/internalclientset:go_default_library", "//pkg/cloudprovider/providers:go_default_library", "//pkg/controller/informers:go_default_library", diff --git a/federation/cmd/federation-apiserver/app/options/options.go b/federation/cmd/federation-apiserver/app/options/options.go index f05a551c0ed..ea94c8486db 100644 --- a/federation/cmd/federation-apiserver/app/options/options.go +++ b/federation/cmd/federation-apiserver/app/options/options.go @@ -32,7 +32,7 @@ type ServerRunOptions struct { Etcd *genericoptions.EtcdOptions SecureServing *genericoptions.SecureServingOptions InsecureServing *genericoptions.ServingOptions - Authentication *genericoptions.BuiltInAuthenticationOptions + Authentication *kubeoptions.BuiltInAuthenticationOptions Authorization *kubeoptions.BuiltInAuthorizationOptions EventTTL time.Duration @@ -45,7 +45,7 @@ func NewServerRunOptions() *ServerRunOptions { Etcd: genericoptions.NewEtcdOptions(), SecureServing: genericoptions.NewSecureServingOptions(), InsecureServing: genericoptions.NewInsecureServingOptions(), - Authentication: genericoptions.NewBuiltInAuthenticationOptions().WithAll(), + Authentication: kubeoptions.NewBuiltInAuthenticationOptions().WithAll(), Authorization: kubeoptions.NewBuiltInAuthorizationOptions(), EventTTL: 1 * time.Hour, diff --git a/federation/cmd/federation-apiserver/app/server.go b/federation/cmd/federation-apiserver/app/server.go index 9c292293ffa..473dd0fea43 100644 --- a/federation/cmd/federation-apiserver/app/server.go +++ b/federation/cmd/federation-apiserver/app/server.go @@ -32,7 +32,6 @@ import ( "k8s.io/kubernetes/federation/cmd/federation-apiserver/app/options" "k8s.io/kubernetes/pkg/admission" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/apiserver/authenticator" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/controller/informers" "k8s.io/kubernetes/pkg/generated/openapi" @@ -91,7 +90,7 @@ func Run(s *options.ServerRunOptions) error { if _, err := genericConfig.ApplySecureServingOptions(s.SecureServing); err != nil { return fmt.Errorf("failed to configure https: %s", err) } - if _, err := genericConfig.ApplyAuthenticationOptions(s.Authentication); err != nil { + if err := s.Authentication.Apply(genericConfig); err != nil { return fmt.Errorf("failed to configure authentication: %s", err) } @@ -134,7 +133,7 @@ func Run(s *options.ServerRunOptions) error { storageFactory.SetEtcdLocation(groupResource, servers) } - apiAuthenticator, securityDefinitions, err := authenticator.New(s.Authentication.ToAuthenticationConfig()) + apiAuthenticator, securityDefinitions, err := s.Authentication.ToAuthenticationConfig().New() if err != nil { return fmt.Errorf("invalid Authentication Config: %v", err) } diff --git a/pkg/apiserver/authenticator/OWNERS b/pkg/apiserver/authenticator/OWNERS deleted file mode 100755 index 34b008b26cb..00000000000 --- a/pkg/apiserver/authenticator/OWNERS +++ /dev/null @@ -1,10 +0,0 @@ -reviewers: -- smarterclayton -- deads2k -- liggitt -- dims -- spxtr -- errordeveloper -- resouer -- cjcullen -- ericchiang diff --git a/pkg/genericapiserver/BUILD b/pkg/genericapiserver/BUILD index e5cf0acf179..cc90060c877 100644 --- a/pkg/genericapiserver/BUILD +++ b/pkg/genericapiserver/BUILD @@ -34,7 +34,6 @@ go_library( "//pkg/apimachinery/registered:go_default_library", "//pkg/apis/meta/v1:go_default_library", "//pkg/apiserver:go_default_library", - "//pkg/apiserver/authenticator:go_default_library", "//pkg/apiserver/filters:go_default_library", "//pkg/apiserver/openapi:go_default_library", "//pkg/apiserver/request:go_default_library", @@ -44,6 +43,7 @@ go_library( "//pkg/auth/handlers:go_default_library", "//pkg/auth/user:go_default_library", "//pkg/client/restclient:go_default_library", + "//pkg/genericapiserver/authenticator:go_default_library", "//pkg/genericapiserver/authorizer:go_default_library", "//pkg/genericapiserver/filters:go_default_library", "//pkg/genericapiserver/mux:go_default_library", diff --git a/pkg/genericapiserver/authenticator/BUILD b/pkg/genericapiserver/authenticator/BUILD new file mode 100644 index 00000000000..a40f825fe5c --- /dev/null +++ b/pkg/genericapiserver/authenticator/BUILD @@ -0,0 +1,33 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", +) + +go_library( + name = "go_default_library", + srcs = [ + "delegating.go", + "loopback.go", + "requestheader.go", + ], + tags = ["automanaged"], + deps = [ + "//pkg/auth/authenticator:go_default_library", + "//pkg/auth/authenticator/bearertoken:go_default_library", + "//pkg/auth/group:go_default_library", + "//pkg/auth/user:go_default_library", + "//pkg/client/clientset_generated/clientset/typed/authentication/v1beta1:go_default_library", + "//pkg/util/cert:go_default_library", + "//plugin/pkg/auth/authenticator/request/anonymous:go_default_library", + "//plugin/pkg/auth/authenticator/request/headerrequest:go_default_library", + "//plugin/pkg/auth/authenticator/request/union:go_default_library", + "//plugin/pkg/auth/authenticator/request/x509:go_default_library", + "//plugin/pkg/auth/authenticator/token/tokenfile:go_default_library", + "//plugin/pkg/auth/authenticator/token/webhook:go_default_library", + "//vendor:github.com/go-openapi/spec", + ], +) diff --git a/pkg/apiserver/authenticator/delegating.go b/pkg/genericapiserver/authenticator/delegating.go similarity index 100% rename from pkg/apiserver/authenticator/delegating.go rename to pkg/genericapiserver/authenticator/delegating.go diff --git a/pkg/genericapiserver/authenticator/loopback.go b/pkg/genericapiserver/authenticator/loopback.go new file mode 100644 index 00000000000..8e3536a31c2 --- /dev/null +++ b/pkg/genericapiserver/authenticator/loopback.go @@ -0,0 +1,29 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package authenticator + +import ( + "k8s.io/kubernetes/pkg/auth/authenticator" + "k8s.io/kubernetes/pkg/auth/authenticator/bearertoken" + "k8s.io/kubernetes/pkg/auth/user" + "k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/tokenfile" +) + +// newAuthenticatorFromToken returns an authenticator.Request or an error +func NewAuthenticatorFromTokens(tokens map[string]*user.DefaultInfo) authenticator.Request { + return bearertoken.New(tokenfile.New(tokens)) +} diff --git a/pkg/genericapiserver/authenticator/requestheader.go b/pkg/genericapiserver/authenticator/requestheader.go new file mode 100644 index 00000000000..b719eccc002 --- /dev/null +++ b/pkg/genericapiserver/authenticator/requestheader.go @@ -0,0 +1,31 @@ +/* +Copyright 2014 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package authenticator + +type RequestHeaderConfig struct { + // UsernameHeaders are the headers to check (in order, case-insensitively) for an identity. The first header with a value wins. + UsernameHeaders []string + // GroupHeaders are the headers to check (case-insensitively) for a group names. All values will be used. + GroupHeaders []string + // ExtraHeaderPrefixes are the head prefixes to check (case-insentively) for filling in + // the user.Info.Extra. All values of all matching headers will be added. + ExtraHeaderPrefixes []string + // ClientCA points to CA bundle file which is used verify the identity of the front proxy + ClientCA string + // AllowedClientNames is a list of common names that may be presented by the authenticating front proxy. Empty means: accept any. + AllowedClientNames []string +} diff --git a/pkg/genericapiserver/config.go b/pkg/genericapiserver/config.go index 2d5e097645b..90b2528c22d 100644 --- a/pkg/genericapiserver/config.go +++ b/pkg/genericapiserver/config.go @@ -39,7 +39,6 @@ import ( "k8s.io/kubernetes/pkg/admission" "k8s.io/kubernetes/pkg/api" metav1 "k8s.io/kubernetes/pkg/apis/meta/v1" - apiserverauthenticator "k8s.io/kubernetes/pkg/apiserver/authenticator" apiserverfilters "k8s.io/kubernetes/pkg/apiserver/filters" apiserveropenapi "k8s.io/kubernetes/pkg/apiserver/openapi" apiserverrequest "k8s.io/kubernetes/pkg/apiserver/request" @@ -49,7 +48,8 @@ import ( authhandlers "k8s.io/kubernetes/pkg/auth/handlers" "k8s.io/kubernetes/pkg/auth/user" "k8s.io/kubernetes/pkg/client/restclient" - apiserverauthorizer "k8s.io/kubernetes/pkg/genericapiserver/authorizer" + genericauthenticator "k8s.io/kubernetes/pkg/genericapiserver/authenticator" + genericauthorizer "k8s.io/kubernetes/pkg/genericapiserver/authorizer" genericfilters "k8s.io/kubernetes/pkg/genericapiserver/filters" "k8s.io/kubernetes/pkg/genericapiserver/mux" openapicommon "k8s.io/kubernetes/pkg/genericapiserver/openapi/common" @@ -327,30 +327,7 @@ func (c *Config) ApplyInsecureServingOptions(insecureServing *options.ServingOpt return c } -func (c *Config) ApplyAuthenticationOptions(o *options.BuiltInAuthenticationOptions) (*Config, error) { - if o == nil || o.PasswordFile == nil { - return c, nil - } - - var err error - if o.ClientCert != nil { - c, err = c.applyClientCert(o.ClientCert.ClientCA) - if err != nil { - return nil, fmt.Errorf("unable to load client CA file: %v", err) - } - } - if o.RequestHeader != nil { - c, err = c.applyClientCert(o.RequestHeader.ClientCAFile) - if err != nil { - return nil, fmt.Errorf("unable to load client CA file: %v", err) - } - } - - c.SupportsBasicAuth = len(o.PasswordFile.BasicAuthFile) > 0 - return c, nil -} - -func (c *Config) applyClientCert(clientCAFile string) (*Config, error) { +func (c *Config) ApplyClientCert(clientCAFile string) (*Config, error) { if c.SecureServingInfo != nil { if len(clientCAFile) > 0 { clientCAs, err := certutil.CertsFromFile(clientCAFile) @@ -375,11 +352,11 @@ func (c *Config) ApplyDelegatingAuthenticationOptions(o *options.DelegatingAuthe } var err error - c, err = c.applyClientCert(o.ClientCert.ClientCA) + c, err = c.ApplyClientCert(o.ClientCert.ClientCA) if err != nil { return nil, fmt.Errorf("unable to load client CA file: %v", err) } - c, err = c.applyClientCert(o.RequestHeader.ClientCAFile) + c, err = c.ApplyClientCert(o.RequestHeader.ClientCAFile) if err != nil { return nil, fmt.Errorf("unable to load client CA file: %v", err) } @@ -505,10 +482,10 @@ func (c *Config) Complete() completedConfig { Groups: []string{user.SystemPrivilegedGroup}, } - tokenAuthenticator := apiserverauthenticator.NewAuthenticatorFromTokens(tokens) + tokenAuthenticator := genericauthenticator.NewAuthenticatorFromTokens(tokens) c.Authenticator = authenticatorunion.New(tokenAuthenticator, c.Authenticator) - tokenAuthorizer := apiserverauthorizer.NewPrivilegedGroups(user.SystemPrivilegedGroup) + tokenAuthorizer := genericauthorizer.NewPrivilegedGroups(user.SystemPrivilegedGroup) c.Authorizer = authorizerunion.New(tokenAuthorizer, c.Authorizer) } diff --git a/pkg/genericapiserver/options/BUILD b/pkg/genericapiserver/options/BUILD index f41f222bb86..a2744c53af9 100644 --- a/pkg/genericapiserver/options/BUILD +++ b/pkg/genericapiserver/options/BUILD @@ -23,12 +23,12 @@ go_library( "//pkg/api:go_default_library", "//pkg/api/v1:go_default_library", "//pkg/apimachinery/registered:go_default_library", - "//pkg/apiserver/authenticator:go_default_library", "//pkg/client/clientset_generated/clientset/typed/authentication/v1beta1:go_default_library", "//pkg/client/clientset_generated/clientset/typed/authorization/v1beta1:go_default_library", "//pkg/client/restclient:go_default_library", "//pkg/client/unversioned/clientcmd:go_default_library", "//pkg/cloudprovider:go_default_library", + "//pkg/genericapiserver/authenticator:go_default_library", "//pkg/genericapiserver/authorizer:go_default_library", "//pkg/runtime/schema:go_default_library", "//pkg/storage/storagebackend:go_default_library", diff --git a/pkg/genericapiserver/options/authentication.go b/pkg/genericapiserver/options/authentication.go index c453e33239c..43502d5993c 100644 --- a/pkg/genericapiserver/options/authentication.go +++ b/pkg/genericapiserver/options/authentication.go @@ -21,279 +21,12 @@ import ( "github.com/spf13/pflag" - "k8s.io/kubernetes/pkg/apiserver/authenticator" authenticationclient "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/authentication/v1beta1" "k8s.io/kubernetes/pkg/client/restclient" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" + "k8s.io/kubernetes/pkg/genericapiserver/authenticator" ) -type BuiltInAuthenticationOptions struct { - Anonymous *AnonymousAuthenticationOptions - AnyToken *AnyTokenAuthenticationOptions - ClientCert *ClientCertAuthenticationOptions - Keystone *KeystoneAuthenticationOptions - OIDC *OIDCAuthenticationOptions - PasswordFile *PasswordFileAuthenticationOptions - RequestHeader *RequestHeaderAuthenticationOptions - ServiceAccounts *ServiceAccountAuthenticationOptions - TokenFile *TokenFileAuthenticationOptions - WebHook *WebHookAuthenticationOptions -} - -type AnyTokenAuthenticationOptions struct { - Allow bool -} - -type AnonymousAuthenticationOptions struct { - Allow bool -} - -type KeystoneAuthenticationOptions struct { - URL string - CAFile string -} - -type OIDCAuthenticationOptions struct { - CAFile string - ClientID string - IssuerURL string - UsernameClaim string - GroupsClaim string -} - -type PasswordFileAuthenticationOptions struct { - BasicAuthFile string -} - -type ServiceAccountAuthenticationOptions struct { - KeyFiles []string - Lookup bool -} - -type TokenFileAuthenticationOptions struct { - TokenFile string -} - -type WebHookAuthenticationOptions struct { - ConfigFile string - CacheTTL time.Duration -} - -func NewBuiltInAuthenticationOptions() *BuiltInAuthenticationOptions { - return &BuiltInAuthenticationOptions{} -} - -func (s *BuiltInAuthenticationOptions) WithAll() *BuiltInAuthenticationOptions { - return s. - WithAnyonymous(). - WithAnyToken(). - WithClientCert(). - WithKeystone(). - WithOIDC(). - WithPasswordFile(). - WithRequestHeader(). - WithServiceAccounts(). - WithTokenFile(). - WithWebHook() -} - -func (s *BuiltInAuthenticationOptions) WithAnyonymous() *BuiltInAuthenticationOptions { - s.Anonymous = &AnonymousAuthenticationOptions{Allow: true} - return s -} - -func (s *BuiltInAuthenticationOptions) WithAnyToken() *BuiltInAuthenticationOptions { - s.AnyToken = &AnyTokenAuthenticationOptions{} - return s -} - -func (s *BuiltInAuthenticationOptions) WithClientCert() *BuiltInAuthenticationOptions { - s.ClientCert = &ClientCertAuthenticationOptions{} - return s -} - -func (s *BuiltInAuthenticationOptions) WithKeystone() *BuiltInAuthenticationOptions { - s.Keystone = &KeystoneAuthenticationOptions{} - return s -} - -func (s *BuiltInAuthenticationOptions) WithOIDC() *BuiltInAuthenticationOptions { - s.OIDC = &OIDCAuthenticationOptions{} - return s -} - -func (s *BuiltInAuthenticationOptions) WithPasswordFile() *BuiltInAuthenticationOptions { - s.PasswordFile = &PasswordFileAuthenticationOptions{} - return s -} - -func (s *BuiltInAuthenticationOptions) WithRequestHeader() *BuiltInAuthenticationOptions { - s.RequestHeader = &RequestHeaderAuthenticationOptions{} - return s -} - -func (s *BuiltInAuthenticationOptions) WithServiceAccounts() *BuiltInAuthenticationOptions { - s.ServiceAccounts = &ServiceAccountAuthenticationOptions{} - return s -} - -func (s *BuiltInAuthenticationOptions) WithTokenFile() *BuiltInAuthenticationOptions { - s.TokenFile = &TokenFileAuthenticationOptions{} - return s -} - -func (s *BuiltInAuthenticationOptions) WithWebHook() *BuiltInAuthenticationOptions { - s.WebHook = &WebHookAuthenticationOptions{ - CacheTTL: 2 * time.Minute, - } - return s -} - -func (s *BuiltInAuthenticationOptions) Validate() []error { - allErrors := []error{} - return allErrors -} - -func (s *BuiltInAuthenticationOptions) AddFlags(fs *pflag.FlagSet) { - if s.Anonymous != nil { - fs.BoolVar(&s.Anonymous.Allow, "anonymous-auth", s.Anonymous.Allow, ""+ - "Enables anonymous requests to the secure port of the API server. "+ - "Requests that are not rejected by another authentication method are treated as anonymous requests. "+ - "Anonymous requests have a username of system:anonymous, and a group name of system:unauthenticated.") - } - - if s.AnyToken != nil { - fs.BoolVar(&s.AnyToken.Allow, "insecure-allow-any-token", s.AnyToken.Allow, ""+ - "If set, your server will be INSECURE. Any token will be allowed and user information will be parsed "+ - "from the token as `username/group1,group2`") - - } - - if s.ClientCert != nil { - s.ClientCert.AddFlags(fs) - } - - if s.Keystone != nil { - fs.StringVar(&s.Keystone.URL, "experimental-keystone-url", s.Keystone.URL, - "If passed, activates the keystone authentication plugin.") - - fs.StringVar(&s.Keystone.CAFile, "experimental-keystone-ca-file", s.Keystone.CAFile, ""+ - "If set, the Keystone server's certificate will be verified by one of the authorities "+ - "in the experimental-keystone-ca-file, otherwise the host's root CA set will be used.") - } - - if s.OIDC != nil { - fs.StringVar(&s.OIDC.IssuerURL, "oidc-issuer-url", s.OIDC.IssuerURL, ""+ - "The URL of the OpenID issuer, only HTTPS scheme will be accepted. "+ - "If set, it will be used to verify the OIDC JSON Web Token (JWT).") - - fs.StringVar(&s.OIDC.ClientID, "oidc-client-id", s.OIDC.ClientID, - "The client ID for the OpenID Connect client, must be set if oidc-issuer-url is set.") - - fs.StringVar(&s.OIDC.CAFile, "oidc-ca-file", s.OIDC.CAFile, ""+ - "If set, the OpenID server's certificate will be verified by one of the authorities "+ - "in the oidc-ca-file, otherwise the host's root CA set will be used.") - - fs.StringVar(&s.OIDC.UsernameClaim, "oidc-username-claim", "sub", ""+ - "The OpenID claim to use as the user name. Note that claims other than the default ('sub') "+ - "is not guaranteed to be unique and immutable. This flag is experimental, please see "+ - "the authentication documentation for further details.") - - fs.StringVar(&s.OIDC.GroupsClaim, "oidc-groups-claim", "", ""+ - "If provided, the name of a custom OpenID Connect claim for specifying user groups. "+ - "The claim value is expected to be a string or array of strings. This flag is experimental, "+ - "please see the authentication documentation for further details.") - } - - if s.PasswordFile != nil { - fs.StringVar(&s.PasswordFile.BasicAuthFile, "basic-auth-file", s.PasswordFile.BasicAuthFile, ""+ - "If set, the file that will be used to admit requests to the secure port of the API server "+ - "via http basic authentication.") - } - - if s.RequestHeader != nil { - s.RequestHeader.AddFlags(fs) - } - - if s.ServiceAccounts != nil { - fs.StringArrayVar(&s.ServiceAccounts.KeyFiles, "service-account-key-file", s.ServiceAccounts.KeyFiles, ""+ - "File containing PEM-encoded x509 RSA or ECDSA private or public keys, used to verify "+ - "ServiceAccount tokens. If unspecified, --tls-private-key-file is used. "+ - "The specified file can contain multiple keys, and the flag can be specified multiple times with different files.") - - fs.BoolVar(&s.ServiceAccounts.Lookup, "service-account-lookup", s.ServiceAccounts.Lookup, - "If true, validate ServiceAccount tokens exist in etcd as part of authentication.") - } - - if s.TokenFile != nil { - fs.StringVar(&s.TokenFile.TokenFile, "token-auth-file", s.TokenFile.TokenFile, ""+ - "If set, the file that will be used to secure the secure port of the API server "+ - "via token authentication.") - } - - if s.WebHook != nil { - fs.StringVar(&s.WebHook.ConfigFile, "authentication-token-webhook-config-file", s.WebHook.ConfigFile, ""+ - "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.WebHook.CacheTTL, "authentication-token-webhook-cache-ttl", s.WebHook.CacheTTL, - "The duration to cache responses from the webhook token authenticator. Default is 2m.") - } -} - -func (s *BuiltInAuthenticationOptions) ToAuthenticationConfig() authenticator.AuthenticatorConfig { - ret := authenticator.AuthenticatorConfig{} - - if s.Anonymous != nil { - ret.Anonymous = s.Anonymous.Allow - } - - if s.AnyToken != nil { - ret.AnyToken = s.AnyToken.Allow - } - - if s.ClientCert != nil { - ret.ClientCAFile = s.ClientCert.ClientCA - } - - if s.Keystone != nil { - ret.KeystoneURL = s.Keystone.URL - ret.KeystoneCAFile = s.Keystone.CAFile - } - - if s.OIDC != nil { - ret.OIDCCAFile = s.OIDC.CAFile - ret.OIDCClientID = s.OIDC.ClientID - ret.OIDCGroupsClaim = s.OIDC.GroupsClaim - ret.OIDCIssuerURL = s.OIDC.IssuerURL - ret.OIDCUsernameClaim = s.OIDC.UsernameClaim - } - - if s.PasswordFile != nil { - ret.BasicAuthFile = s.PasswordFile.BasicAuthFile - } - - if s.RequestHeader != nil { - ret.RequestHeaderConfig = s.RequestHeader.ToAuthenticationRequestHeaderConfig() - } - - if s.ServiceAccounts != nil { - ret.ServiceAccountKeyFiles = s.ServiceAccounts.KeyFiles - ret.ServiceAccountLookup = s.ServiceAccounts.Lookup - } - - if s.TokenFile != nil { - ret.TokenAuthFile = s.TokenFile.TokenFile - } - - if s.WebHook != nil { - ret.WebhookTokenAuthnConfigFile = s.WebHook.ConfigFile - ret.WebhookTokenAuthnCacheTTL = s.WebHook.CacheTTL - } - - return ret -} - type RequestHeaderAuthenticationOptions struct { UsernameHeaders []string GroupHeaders []string diff --git a/pkg/apiserver/authenticator/BUILD b/pkg/kubeapiserver/authenticator/BUILD similarity index 89% rename from pkg/apiserver/authenticator/BUILD rename to pkg/kubeapiserver/authenticator/BUILD index 04c3bdb897c..17ecac0e23c 100644 --- a/pkg/apiserver/authenticator/BUILD +++ b/pkg/kubeapiserver/authenticator/BUILD @@ -9,17 +9,14 @@ load( go_library( name = "go_default_library", - srcs = [ - "builtin.go", - "delegating.go", - ], + srcs = ["config.go"], tags = ["automanaged"], deps = [ "//pkg/auth/authenticator:go_default_library", "//pkg/auth/authenticator/bearertoken:go_default_library", "//pkg/auth/group:go_default_library", "//pkg/auth/user:go_default_library", - "//pkg/client/clientset_generated/clientset/typed/authentication/v1beta1:go_default_library", + "//pkg/genericapiserver/authenticator:go_default_library", "//pkg/serviceaccount:go_default_library", "//pkg/util/cert:go_default_library", "//plugin/pkg/auth/authenticator/password/keystone:go_default_library", diff --git a/pkg/apiserver/authenticator/builtin.go b/pkg/kubeapiserver/authenticator/config.go similarity index 89% rename from pkg/apiserver/authenticator/builtin.go rename to pkg/kubeapiserver/authenticator/config.go index f4e88101052..b85c659b0b7 100644 --- a/pkg/apiserver/authenticator/builtin.go +++ b/pkg/kubeapiserver/authenticator/config.go @@ -25,6 +25,7 @@ import ( "k8s.io/kubernetes/pkg/auth/authenticator/bearertoken" "k8s.io/kubernetes/pkg/auth/group" "k8s.io/kubernetes/pkg/auth/user" + genericauthenticator "k8s.io/kubernetes/pkg/genericapiserver/authenticator" "k8s.io/kubernetes/pkg/serviceaccount" certutil "k8s.io/kubernetes/pkg/util/cert" "k8s.io/kubernetes/plugin/pkg/auth/authenticator/password/keystone" @@ -40,20 +41,6 @@ import ( "k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/webhook" ) -type RequestHeaderConfig struct { - // UsernameHeaders are the headers to check (in order, case-insensitively) for an identity. The first header with a value wins. - UsernameHeaders []string - // GroupHeaders are the headers to check (case-insensitively) for a group names. All values will be used. - GroupHeaders []string - // ExtraHeaderPrefixes are the head prefixes to check (case-insentively) for filling in - // the user.Info.Extra. All values of all matching headers will be added. - ExtraHeaderPrefixes []string - // ClientCA points to CA bundle file which is used verify the identity of the front proxy - ClientCA string - // AllowedClientNames is a list of common names that may be presented by the authenticating front proxy. Empty means: accept any. - AllowedClientNames []string -} - type AuthenticatorConfig struct { Anonymous bool AnyToken bool @@ -72,7 +59,7 @@ type AuthenticatorConfig struct { WebhookTokenAuthnConfigFile string WebhookTokenAuthnCacheTTL time.Duration - RequestHeaderConfig *RequestHeaderConfig + RequestHeaderConfig *genericauthenticator.RequestHeaderConfig // TODO, this is the only non-serializable part of the entire config. Factor it out into a clientconfig ServiceAccountTokenGetter serviceaccount.ServiceAccountTokenGetter @@ -80,7 +67,7 @@ type AuthenticatorConfig struct { // New returns an authenticator.Request or an error that supports the standard // Kubernetes authentication mechanisms. -func New(config AuthenticatorConfig) (authenticator.Request, *spec.SecurityDefinitions, error) { +func (config AuthenticatorConfig) New() (authenticator.Request, *spec.SecurityDefinitions, error) { var authenticators []authenticator.Request securityDefinitions := spec.SecurityDefinitions{} hasBasicAuth := false @@ -243,11 +230,6 @@ func newAuthenticatorFromTokenFile(tokenAuthFile string) (authenticator.Request, return bearertoken.New(tokenAuthenticator), nil } -// newAuthenticatorFromToken returns an authenticator.Request or an error -func NewAuthenticatorFromTokens(tokens map[string]*user.DefaultInfo) authenticator.Request { - return bearertoken.New(tokenfile.New(tokens)) -} - // newAuthenticatorFromOIDCIssuerURL returns an authenticator.Request or an error. func newAuthenticatorFromOIDCIssuerURL(issuerURL, clientID, caFile, usernameClaim, groupsClaim string) (authenticator.Request, error) { tokenAuthenticator, err := oidc.New(oidc.OIDCOptions{ diff --git a/pkg/kubeapiserver/options/BUILD b/pkg/kubeapiserver/options/BUILD index bafc2f7d236..ee00c92fabe 100644 --- a/pkg/kubeapiserver/options/BUILD +++ b/pkg/kubeapiserver/options/BUILD @@ -9,10 +9,16 @@ load( go_library( name = "go_default_library", - srcs = ["authorization.go"], + srcs = [ + "authentication.go", + "authorization.go", + ], tags = ["automanaged"], deps = [ "//pkg/controller/informers:go_default_library", + "//pkg/genericapiserver:go_default_library", + "//pkg/genericapiserver/options:go_default_library", + "//pkg/kubeapiserver/authenticator:go_default_library", "//pkg/kubeapiserver/authorizer:go_default_library", "//vendor:github.com/spf13/pflag", ], diff --git a/pkg/kubeapiserver/options/authentication.go b/pkg/kubeapiserver/options/authentication.go new file mode 100644 index 00000000000..d52b6773ac4 --- /dev/null +++ b/pkg/kubeapiserver/options/authentication.go @@ -0,0 +1,318 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package options + +import ( + "fmt" + "time" + + "github.com/spf13/pflag" + + "k8s.io/kubernetes/pkg/genericapiserver" + genericoptions "k8s.io/kubernetes/pkg/genericapiserver/options" + "k8s.io/kubernetes/pkg/kubeapiserver/authenticator" +) + +type BuiltInAuthenticationOptions struct { + Anonymous *AnonymousAuthenticationOptions + AnyToken *AnyTokenAuthenticationOptions + ClientCert *genericoptions.ClientCertAuthenticationOptions + Keystone *KeystoneAuthenticationOptions + OIDC *OIDCAuthenticationOptions + PasswordFile *PasswordFileAuthenticationOptions + RequestHeader *genericoptions.RequestHeaderAuthenticationOptions + ServiceAccounts *ServiceAccountAuthenticationOptions + TokenFile *TokenFileAuthenticationOptions + WebHook *WebHookAuthenticationOptions +} + +type AnyTokenAuthenticationOptions struct { + Allow bool +} + +type AnonymousAuthenticationOptions struct { + Allow bool +} + +type KeystoneAuthenticationOptions struct { + URL string + CAFile string +} + +type OIDCAuthenticationOptions struct { + CAFile string + ClientID string + IssuerURL string + UsernameClaim string + GroupsClaim string +} + +type PasswordFileAuthenticationOptions struct { + BasicAuthFile string +} + +type ServiceAccountAuthenticationOptions struct { + KeyFiles []string + Lookup bool +} + +type TokenFileAuthenticationOptions struct { + TokenFile string +} + +type WebHookAuthenticationOptions struct { + ConfigFile string + CacheTTL time.Duration +} + +func NewBuiltInAuthenticationOptions() *BuiltInAuthenticationOptions { + return &BuiltInAuthenticationOptions{} +} + +func (s *BuiltInAuthenticationOptions) WithAll() *BuiltInAuthenticationOptions { + return s. + WithAnyonymous(). + WithAnyToken(). + WithClientCert(). + WithKeystone(). + WithOIDC(). + WithPasswordFile(). + WithRequestHeader(). + WithServiceAccounts(). + WithTokenFile(). + WithWebHook() +} + +func (s *BuiltInAuthenticationOptions) WithAnyonymous() *BuiltInAuthenticationOptions { + s.Anonymous = &AnonymousAuthenticationOptions{Allow: true} + return s +} + +func (s *BuiltInAuthenticationOptions) WithAnyToken() *BuiltInAuthenticationOptions { + s.AnyToken = &AnyTokenAuthenticationOptions{} + return s +} + +func (s *BuiltInAuthenticationOptions) WithClientCert() *BuiltInAuthenticationOptions { + s.ClientCert = &genericoptions.ClientCertAuthenticationOptions{} + return s +} + +func (s *BuiltInAuthenticationOptions) WithKeystone() *BuiltInAuthenticationOptions { + s.Keystone = &KeystoneAuthenticationOptions{} + return s +} + +func (s *BuiltInAuthenticationOptions) WithOIDC() *BuiltInAuthenticationOptions { + s.OIDC = &OIDCAuthenticationOptions{} + return s +} + +func (s *BuiltInAuthenticationOptions) WithPasswordFile() *BuiltInAuthenticationOptions { + s.PasswordFile = &PasswordFileAuthenticationOptions{} + return s +} + +func (s *BuiltInAuthenticationOptions) WithRequestHeader() *BuiltInAuthenticationOptions { + s.RequestHeader = &genericoptions.RequestHeaderAuthenticationOptions{} + return s +} + +func (s *BuiltInAuthenticationOptions) WithServiceAccounts() *BuiltInAuthenticationOptions { + s.ServiceAccounts = &ServiceAccountAuthenticationOptions{} + return s +} + +func (s *BuiltInAuthenticationOptions) WithTokenFile() *BuiltInAuthenticationOptions { + s.TokenFile = &TokenFileAuthenticationOptions{} + return s +} + +func (s *BuiltInAuthenticationOptions) WithWebHook() *BuiltInAuthenticationOptions { + s.WebHook = &WebHookAuthenticationOptions{ + CacheTTL: 2 * time.Minute, + } + return s +} + +func (s *BuiltInAuthenticationOptions) Validate() []error { + allErrors := []error{} + return allErrors +} + +func (s *BuiltInAuthenticationOptions) AddFlags(fs *pflag.FlagSet) { + if s.Anonymous != nil { + fs.BoolVar(&s.Anonymous.Allow, "anonymous-auth", s.Anonymous.Allow, ""+ + "Enables anonymous requests to the secure port of the API server. "+ + "Requests that are not rejected by another authentication method are treated as anonymous requests. "+ + "Anonymous requests have a username of system:anonymous, and a group name of system:unauthenticated.") + } + + if s.AnyToken != nil { + fs.BoolVar(&s.AnyToken.Allow, "insecure-allow-any-token", s.AnyToken.Allow, ""+ + "If set, your server will be INSECURE. Any token will be allowed and user information will be parsed "+ + "from the token as `username/group1,group2`") + + } + + if s.ClientCert != nil { + s.ClientCert.AddFlags(fs) + } + + if s.Keystone != nil { + fs.StringVar(&s.Keystone.URL, "experimental-keystone-url", s.Keystone.URL, + "If passed, activates the keystone authentication plugin.") + + fs.StringVar(&s.Keystone.CAFile, "experimental-keystone-ca-file", s.Keystone.CAFile, ""+ + "If set, the Keystone server's certificate will be verified by one of the authorities "+ + "in the experimental-keystone-ca-file, otherwise the host's root CA set will be used.") + } + + if s.OIDC != nil { + fs.StringVar(&s.OIDC.IssuerURL, "oidc-issuer-url", s.OIDC.IssuerURL, ""+ + "The URL of the OpenID issuer, only HTTPS scheme will be accepted. "+ + "If set, it will be used to verify the OIDC JSON Web Token (JWT).") + + fs.StringVar(&s.OIDC.ClientID, "oidc-client-id", s.OIDC.ClientID, + "The client ID for the OpenID Connect client, must be set if oidc-issuer-url is set.") + + fs.StringVar(&s.OIDC.CAFile, "oidc-ca-file", s.OIDC.CAFile, ""+ + "If set, the OpenID server's certificate will be verified by one of the authorities "+ + "in the oidc-ca-file, otherwise the host's root CA set will be used.") + + fs.StringVar(&s.OIDC.UsernameClaim, "oidc-username-claim", "sub", ""+ + "The OpenID claim to use as the user name. Note that claims other than the default ('sub') "+ + "is not guaranteed to be unique and immutable. This flag is experimental, please see "+ + "the authentication documentation for further details.") + + fs.StringVar(&s.OIDC.GroupsClaim, "oidc-groups-claim", "", ""+ + "If provided, the name of a custom OpenID Connect claim for specifying user groups. "+ + "The claim value is expected to be a string or array of strings. This flag is experimental, "+ + "please see the authentication documentation for further details.") + } + + if s.PasswordFile != nil { + fs.StringVar(&s.PasswordFile.BasicAuthFile, "basic-auth-file", s.PasswordFile.BasicAuthFile, ""+ + "If set, the file that will be used to admit requests to the secure port of the API server "+ + "via http basic authentication.") + } + + if s.RequestHeader != nil { + s.RequestHeader.AddFlags(fs) + } + + if s.ServiceAccounts != nil { + fs.StringArrayVar(&s.ServiceAccounts.KeyFiles, "service-account-key-file", s.ServiceAccounts.KeyFiles, ""+ + "File containing PEM-encoded x509 RSA or ECDSA private or public keys, used to verify "+ + "ServiceAccount tokens. If unspecified, --tls-private-key-file is used. "+ + "The specified file can contain multiple keys, and the flag can be specified multiple times with different files.") + + fs.BoolVar(&s.ServiceAccounts.Lookup, "service-account-lookup", s.ServiceAccounts.Lookup, + "If true, validate ServiceAccount tokens exist in etcd as part of authentication.") + } + + if s.TokenFile != nil { + fs.StringVar(&s.TokenFile.TokenFile, "token-auth-file", s.TokenFile.TokenFile, ""+ + "If set, the file that will be used to secure the secure port of the API server "+ + "via token authentication.") + } + + if s.WebHook != nil { + fs.StringVar(&s.WebHook.ConfigFile, "authentication-token-webhook-config-file", s.WebHook.ConfigFile, ""+ + "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.WebHook.CacheTTL, "authentication-token-webhook-cache-ttl", s.WebHook.CacheTTL, + "The duration to cache responses from the webhook token authenticator. Default is 2m.") + } +} + +func (s *BuiltInAuthenticationOptions) ToAuthenticationConfig() authenticator.AuthenticatorConfig { + ret := authenticator.AuthenticatorConfig{} + + if s.Anonymous != nil { + ret.Anonymous = s.Anonymous.Allow + } + + if s.AnyToken != nil { + ret.AnyToken = s.AnyToken.Allow + } + + if s.ClientCert != nil { + ret.ClientCAFile = s.ClientCert.ClientCA + } + + if s.Keystone != nil { + ret.KeystoneURL = s.Keystone.URL + ret.KeystoneCAFile = s.Keystone.CAFile + } + + if s.OIDC != nil { + ret.OIDCCAFile = s.OIDC.CAFile + ret.OIDCClientID = s.OIDC.ClientID + ret.OIDCGroupsClaim = s.OIDC.GroupsClaim + ret.OIDCIssuerURL = s.OIDC.IssuerURL + ret.OIDCUsernameClaim = s.OIDC.UsernameClaim + } + + if s.PasswordFile != nil { + ret.BasicAuthFile = s.PasswordFile.BasicAuthFile + } + + if s.RequestHeader != nil { + ret.RequestHeaderConfig = s.RequestHeader.ToAuthenticationRequestHeaderConfig() + } + + if s.ServiceAccounts != nil { + ret.ServiceAccountKeyFiles = s.ServiceAccounts.KeyFiles + ret.ServiceAccountLookup = s.ServiceAccounts.Lookup + } + + if s.TokenFile != nil { + ret.TokenAuthFile = s.TokenFile.TokenFile + } + + if s.WebHook != nil { + ret.WebhookTokenAuthnConfigFile = s.WebHook.ConfigFile + ret.WebhookTokenAuthnCacheTTL = s.WebHook.CacheTTL + } + + return ret +} + +func (o *BuiltInAuthenticationOptions) Apply(c *genericapiserver.Config) error { + if o == nil || o.PasswordFile == nil { + return nil + } + + var err error + if o.ClientCert != nil { + c, err = c.ApplyClientCert(o.ClientCert.ClientCA) + if err != nil { + return fmt.Errorf("unable to load client CA file: %v", err) + } + } + if o.RequestHeader != nil { + c, err = c.ApplyClientCert(o.RequestHeader.ClientCAFile) + if err != nil { + return fmt.Errorf("unable to load client CA file: %v", err) + } + } + + c.SupportsBasicAuth = len(o.PasswordFile.BasicAuthFile) > 0 + return nil +} diff --git a/test/integration/framework/BUILD b/test/integration/framework/BUILD index 9293ecaa83a..2d471e025ed 100644 --- a/test/integration/framework/BUILD +++ b/test/integration/framework/BUILD @@ -30,7 +30,6 @@ go_library( "//pkg/apis/policy/v1alpha1:go_default_library", "//pkg/apis/rbac/v1alpha1:go_default_library", "//pkg/apis/storage/v1beta1:go_default_library", - "//pkg/apiserver/authenticator:go_default_library", "//pkg/auth/authenticator:go_default_library", "//pkg/auth/authorizer:go_default_library", "//pkg/auth/authorizer/union:go_default_library", @@ -44,6 +43,7 @@ go_library( "//pkg/controller/replication:go_default_library", "//pkg/generated/openapi:go_default_library", "//pkg/genericapiserver:go_default_library", + "//pkg/genericapiserver/authenticator:go_default_library", "//pkg/genericapiserver/authorizer:go_default_library", "//pkg/kubectl:go_default_library", "//pkg/kubelet/client:go_default_library", diff --git a/test/integration/framework/master_utils.go b/test/integration/framework/master_utils.go index 8513dea4d54..ab3a69456a6 100644 --- a/test/integration/framework/master_utils.go +++ b/test/integration/framework/master_utils.go @@ -40,7 +40,6 @@ import ( policy "k8s.io/kubernetes/pkg/apis/policy/v1alpha1" rbac "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1" storage "k8s.io/kubernetes/pkg/apis/storage/v1beta1" - "k8s.io/kubernetes/pkg/apiserver/authenticator" authauthenticator "k8s.io/kubernetes/pkg/auth/authenticator" authauthorizer "k8s.io/kubernetes/pkg/auth/authorizer" authorizerunion "k8s.io/kubernetes/pkg/auth/authorizer/union" @@ -54,6 +53,7 @@ import ( replicationcontroller "k8s.io/kubernetes/pkg/controller/replication" "k8s.io/kubernetes/pkg/generated/openapi" "k8s.io/kubernetes/pkg/genericapiserver" + "k8s.io/kubernetes/pkg/genericapiserver/authenticator" "k8s.io/kubernetes/pkg/genericapiserver/authorizer" "k8s.io/kubernetes/pkg/kubectl" kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"