diff --git a/cmd/kube-apiserver/app/options/options.go b/cmd/kube-apiserver/app/options/options.go index 49e5c1d7609..8a412bb130a 100644 --- a/cmd/kube-apiserver/app/options/options.go +++ b/cmd/kube-apiserver/app/options/options.go @@ -36,6 +36,7 @@ type ServerRunOptions struct { SecureServing *genericoptions.SecureServingOptions InsecureServing *genericoptions.ServingOptions Authentication *genericoptions.BuiltInAuthenticationOptions + Authorization *genericoptions.BuiltInAuthorizationOptions AllowPrivileged bool EventTTL time.Duration @@ -53,6 +54,7 @@ func NewServerRunOptions() *ServerRunOptions { SecureServing: genericoptions.NewSecureServingOptions(), InsecureServing: genericoptions.NewInsecureServingOptions(), Authentication: genericoptions.NewBuiltInAuthenticationOptions().WithAll(), + Authorization: genericoptions.NewBuiltInAuthorizationOptions(), EventTTL: 1 * time.Hour, KubeletConfig: kubeletclient.KubeletClientConfig{ @@ -81,6 +83,7 @@ func (s *ServerRunOptions) AddFlags(fs *pflag.FlagSet) { s.InsecureServing.AddFlags(fs) s.InsecureServing.AddDeprecatedFlags(fs) s.Authentication.AddFlags(fs) + s.Authorization.AddFlags(fs) // Note: the weird ""+ in below lines seems to be the only way to get gofmt to // arrange these text blocks sensibly. Grrr. diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index 331926e33d5..5f033ebf281 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -94,6 +94,7 @@ func Run(s *options.ServerRunOptions) error { ApplySecureServingOptions(s.SecureServing). ApplyInsecureServingOptions(s.InsecureServing). ApplyAuthenticationOptions(s.Authentication). + ApplyRBACSuperUser(s.Authorization.RBACSuperUser). Complete() // set default values based on the known values serviceIPRange, apiServerServiceIP, err := genericapiserver.DefaultServiceIPRange(s.GenericServerRunOptions.ServiceClusterIPRange) @@ -245,16 +246,8 @@ func Run(s *options.ServerRunOptions) error { } sharedInformers := informers.NewSharedInformerFactory(nil, client, 10*time.Minute) - authorizationConfig := authorizer.AuthorizationConfig{ - PolicyFile: s.GenericServerRunOptions.AuthorizationPolicyFile, - WebhookConfigFile: s.GenericServerRunOptions.AuthorizationWebhookConfigFile, - WebhookCacheAuthorizedTTL: s.GenericServerRunOptions.AuthorizationWebhookCacheAuthorizedTTL, - WebhookCacheUnauthorizedTTL: s.GenericServerRunOptions.AuthorizationWebhookCacheUnauthorizedTTL, - RBACSuperUser: s.GenericServerRunOptions.AuthorizationRBACSuperUser, - InformerFactory: sharedInformers, - } - authorizationModeNames := strings.Split(s.GenericServerRunOptions.AuthorizationMode, ",") - apiAuthorizer, err := authorizer.NewAuthorizerFromAuthorizationConfig(authorizationModeNames, authorizationConfig) + authorizerconfig := s.Authorization.ToAuthorizationConfig(sharedInformers) + apiAuthorizer, err := authorizer.NewAuthorizerFromAuthorizationConfig(authorizerconfig) if err != nil { glog.Fatalf("Invalid Authorization Config: %v", err) } diff --git a/federation/cmd/federation-apiserver/app/options/options.go b/federation/cmd/federation-apiserver/app/options/options.go index 9b51e01f97d..1c5f7a86fe7 100644 --- a/federation/cmd/federation-apiserver/app/options/options.go +++ b/federation/cmd/federation-apiserver/app/options/options.go @@ -32,6 +32,7 @@ type ServerRunOptions struct { SecureServing *genericoptions.SecureServingOptions InsecureServing *genericoptions.ServingOptions Authentication *genericoptions.BuiltInAuthenticationOptions + Authorization *genericoptions.BuiltInAuthorizationOptions EventTTL time.Duration } @@ -44,6 +45,7 @@ func NewServerRunOptions() *ServerRunOptions { SecureServing: genericoptions.NewSecureServingOptions(), InsecureServing: genericoptions.NewInsecureServingOptions(), Authentication: genericoptions.NewBuiltInAuthenticationOptions().WithAll(), + Authorization: genericoptions.NewBuiltInAuthorizationOptions(), EventTTL: 1 * time.Hour, } @@ -58,6 +60,7 @@ func (s *ServerRunOptions) AddFlags(fs *pflag.FlagSet) { s.SecureServing.AddFlags(fs) s.InsecureServing.AddFlags(fs) s.Authentication.AddFlags(fs) + s.Authorization.AddFlags(fs) fs.DurationVar(&s.EventTTL, "event-ttl", s.EventTTL, "Amount of time to retain events. Default is 1h.") diff --git a/federation/cmd/federation-apiserver/app/server.go b/federation/cmd/federation-apiserver/app/server.go index 6a1f2abd191..5663528f251 100644 --- a/federation/cmd/federation-apiserver/app/server.go +++ b/federation/cmd/federation-apiserver/app/server.go @@ -82,6 +82,7 @@ func Run(s *options.ServerRunOptions) error { ApplySecureServingOptions(s.SecureServing). ApplyInsecureServingOptions(s.InsecureServing). ApplyAuthenticationOptions(s.Authentication). + ApplyRBACSuperUser(s.Authorization.RBACSuperUser). Complete() // set default values based on the known values if err := genericConfig.MaybeGenerateServingCerts(); err != nil { @@ -143,16 +144,8 @@ func Run(s *options.ServerRunOptions) error { } sharedInformers := informers.NewSharedInformerFactory(nil, client, 10*time.Minute) - authorizationConfig := authorizer.AuthorizationConfig{ - PolicyFile: s.GenericServerRunOptions.AuthorizationPolicyFile, - WebhookConfigFile: s.GenericServerRunOptions.AuthorizationWebhookConfigFile, - WebhookCacheAuthorizedTTL: s.GenericServerRunOptions.AuthorizationWebhookCacheAuthorizedTTL, - WebhookCacheUnauthorizedTTL: s.GenericServerRunOptions.AuthorizationWebhookCacheUnauthorizedTTL, - RBACSuperUser: s.GenericServerRunOptions.AuthorizationRBACSuperUser, - InformerFactory: sharedInformers, - } - authorizationModeNames := strings.Split(s.GenericServerRunOptions.AuthorizationMode, ",") - apiAuthorizer, err := authorizer.NewAuthorizerFromAuthorizationConfig(authorizationModeNames, authorizationConfig) + authorizerconfig := s.Authorization.ToAuthorizationConfig(sharedInformers) + apiAuthorizer, err := authorizer.NewAuthorizerFromAuthorizationConfig(authorizerconfig) if err != nil { glog.Fatalf("Invalid Authorization Config: %v", err) } diff --git a/pkg/genericapiserver/authorizer/BUILD b/pkg/genericapiserver/authorizer/BUILD index 9784de226ae..78c9a3dc815 100644 --- a/pkg/genericapiserver/authorizer/BUILD +++ b/pkg/genericapiserver/authorizer/BUILD @@ -19,7 +19,6 @@ go_library( "//pkg/auth/authorizer/abac:go_default_library", "//pkg/auth/authorizer/union:go_default_library", "//pkg/controller/informers:go_default_library", - "//pkg/genericapiserver/options: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.go b/pkg/genericapiserver/authorizer/authz.go index 7088b155b19..86fc05f2b00 100644 --- a/pkg/genericapiserver/authorizer/authz.go +++ b/pkg/genericapiserver/authorizer/authz.go @@ -25,11 +25,18 @@ import ( "k8s.io/kubernetes/pkg/auth/authorizer/abac" "k8s.io/kubernetes/pkg/auth/authorizer/union" "k8s.io/kubernetes/pkg/controller/informers" - "k8s.io/kubernetes/pkg/genericapiserver/options" "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 // which always says yes to an authorization request. // It is useful in tests and when using kubernetes in an open manner. @@ -95,6 +102,8 @@ func NewPrivilegedGroups(groups ...string) *privilegedGroupAuthorizer { } type AuthorizationConfig struct { + AuthorizationModes []string + // Options for ModeABAC // Path to an ABAC policy file. @@ -118,28 +127,27 @@ type AuthorizationConfig struct { } // NewAuthorizerFromAuthorizationConfig returns the right sort of union of multiple authorizer.Authorizer objects -// based on the authorizationMode or an error. authorizationMode should be a comma separated values -// of options.AuthorizationModeChoices. -func NewAuthorizerFromAuthorizationConfig(authorizationModes []string, config AuthorizationConfig) (authorizer.Authorizer, error) { +// based on the authorizationMode or an error. +func NewAuthorizerFromAuthorizationConfig(config AuthorizationConfig) (authorizer.Authorizer, error) { - if len(authorizationModes) == 0 { + 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 authorizationModes { + 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 options.ModeAlwaysAllow: + case ModeAlwaysAllow: authorizers = append(authorizers, NewAlwaysAllowAuthorizer()) - case options.ModeAlwaysDeny: + case ModeAlwaysDeny: authorizers = append(authorizers, NewAlwaysDenyAuthorizer()) - case options.ModeABAC: + case ModeABAC: if config.PolicyFile == "" { return nil, errors.New("ABAC's authorization policy file not passed") } @@ -148,7 +156,7 @@ func NewAuthorizerFromAuthorizationConfig(authorizationModes []string, config Au return nil, err } authorizers = append(authorizers, abacAuthorizer) - case options.ModeWebhook: + case ModeWebhook: if config.WebhookConfigFile == "" { return nil, errors.New("Webhook's configuration file not passed") } @@ -159,7 +167,7 @@ func NewAuthorizerFromAuthorizationConfig(authorizationModes []string, config Au return nil, err } authorizers = append(authorizers, webhookAuthorizer) - case options.ModeRBAC: + case ModeRBAC: rbacAuthorizer := rbac.New( config.InformerFactory.Roles().Lister(), config.InformerFactory.RoleBindings().Lister(), @@ -174,13 +182,13 @@ func NewAuthorizerFromAuthorizationConfig(authorizationModes []string, config Au authorizerMap[authorizationMode] = true } - if !authorizerMap[options.ModeABAC] && config.PolicyFile != "" { + if !authorizerMap[ModeABAC] && config.PolicyFile != "" { return nil, errors.New("Cannot specify --authorization-policy-file without mode ABAC") } - if !authorizerMap[options.ModeWebhook] && config.WebhookConfigFile != "" { + if !authorizerMap[ModeWebhook] && config.WebhookConfigFile != "" { return nil, errors.New("Cannot specify --authorization-webhook-config-file without mode Webhook") } - if !authorizerMap[options.ModeRBAC] && config.RBACSuperUser != "" { + if !authorizerMap[ModeRBAC] && config.RBACSuperUser != "" { return nil, errors.New("Cannot specify --authorization-rbac-super-user without mode RBAC") } diff --git a/pkg/genericapiserver/config.go b/pkg/genericapiserver/config.go index 4c84a44ec32..866f3963ff4 100644 --- a/pkg/genericapiserver/config.go +++ b/pkg/genericapiserver/config.go @@ -291,6 +291,11 @@ func (c *Config) ApplyAuthenticationOptions(o *options.BuiltInAuthenticationOpti return c } +func (c *Config) ApplyRBACSuperUser(rbacSuperUser string) *Config { + c.AuthorizerRBACSuperUser = rbacSuperUser + return c +} + // ApplyOptions applies the run options to the method receiver and returns self func (c *Config) ApplyOptions(options *options.ServerRunOptions) *Config { if len(options.AuditLogPath) != 0 { @@ -302,7 +307,6 @@ func (c *Config) ApplyOptions(options *options.ServerRunOptions) *Config { } } - c.AuthorizerRBACSuperUser = options.AuthorizationRBACSuperUser c.CorsAllowedOriginList = options.CorsAllowedOriginList c.EnableGarbageCollection = options.EnableGarbageCollection c.EnableProfiling = options.EnableProfiling diff --git a/pkg/genericapiserver/options/BUILD b/pkg/genericapiserver/options/BUILD index 7bbd1316e56..342643d1d86 100644 --- a/pkg/genericapiserver/options/BUILD +++ b/pkg/genericapiserver/options/BUILD @@ -14,6 +14,7 @@ go_library( name = "go_default_library", srcs = [ "authentication.go", + "authorization.go", "doc.go", "etcd.go", "server_run_options.go", @@ -28,6 +29,8 @@ go_library( "//pkg/client/clientset_generated/release_1_5/typed/authentication/v1beta1:go_default_library", "//pkg/client/restclient:go_default_library", "//pkg/client/unversioned/clientcmd: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", "//pkg/util/config:go_default_library", diff --git a/pkg/genericapiserver/options/authorization.go b/pkg/genericapiserver/options/authorization.go new file mode 100644 index 00000000000..04bdad45306 --- /dev/null +++ b/pkg/genericapiserver/options/authorization.go @@ -0,0 +1,89 @@ +/* +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/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 + RBACSuperUser string +} + +func NewBuiltInAuthorizationOptions() *BuiltInAuthorizationOptions { + return &BuiltInAuthorizationOptions{ + Mode: "AlwaysAllow", + 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.StringVar(&s.RBACSuperUser, "authorization-rbac-super-user", s.RBACSuperUser, ""+ + "If specified, a username which avoids RBAC authorization checks and role binding "+ + "privilege escalation checks, to be used with --authorization-mode=RBAC.") + +} + +func (s *BuiltInAuthorizationOptions) ToAuthorizationConfig(informerFactory informers.SharedInformerFactory) authorizer.AuthorizationConfig { + return authorizer.AuthorizationConfig{ + AuthorizationModes: strings.Split(s.Mode, ","), + PolicyFile: s.PolicyFile, + WebhookConfigFile: s.WebhookConfigFile, + WebhookCacheAuthorizedTTL: s.WebhookCacheAuthorizedTTL, + WebhookCacheUnauthorizedTTL: s.WebhookCacheUnauthorizedTTL, + RBACSuperUser: s.RBACSuperUser, + InformerFactory: informerFactory, + } +} diff --git a/pkg/genericapiserver/options/server_run_options.go b/pkg/genericapiserver/options/server_run_options.go index c123bdde4c9..51b96add8fc 100644 --- a/pkg/genericapiserver/options/server_run_options.go +++ b/pkg/genericapiserver/options/server_run_options.go @@ -20,7 +20,6 @@ import ( "fmt" "net" "strings" - "time" "k8s.io/kubernetes/pkg/admission" "k8s.io/kubernetes/pkg/api" @@ -39,30 +38,12 @@ const ( var DefaultServiceNodePortRange = utilnet.PortRange{Base: 30000, Size: 2768} -const ( - ModeAlwaysAllow string = "AlwaysAllow" - ModeAlwaysDeny string = "AlwaysDeny" - ModeABAC string = "ABAC" - ModeWebhook string = "Webhook" - ModeRBAC string = "RBAC" -) - -var AuthorizationModeChoices = []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeABAC, ModeWebhook, ModeRBAC} - // ServerRunOptions contains the options while running a generic api server. type ServerRunOptions struct { AdmissionControl string AdmissionControlConfigFile string AdvertiseAddress net.IP - // Authorization mode and associated flags. - AuthorizationMode string - AuthorizationPolicyFile string - AuthorizationWebhookConfigFile string - AuthorizationWebhookCacheAuthorizedTTL time.Duration - AuthorizationWebhookCacheUnauthorizedTTL time.Duration - AuthorizationRBACSuperUser string - CloudConfigFile string CloudProvider string CorsAllowedOriginList []string @@ -99,25 +80,22 @@ type ServerRunOptions struct { func NewServerRunOptions() *ServerRunOptions { return &ServerRunOptions{ - AdmissionControl: "AlwaysAdmit", - AuthorizationMode: "AlwaysAllow", - AuthorizationWebhookCacheAuthorizedTTL: 5 * time.Minute, - AuthorizationWebhookCacheUnauthorizedTTL: 30 * time.Second, - DefaultStorageMediaType: "application/json", - DefaultStorageVersions: registered.AllPreferredGroupVersions(), - DeleteCollectionWorkers: 1, - EnableGarbageCollection: true, - EnableProfiling: true, - EnableContentionProfiling: false, - EnableWatchCache: true, - LongRunningRequestRE: DefaultLongRunningRequestRE, - MasterCount: 1, - MasterServiceNamespace: api.NamespaceDefault, - MaxRequestsInFlight: 400, - MinRequestTimeout: 1800, - RuntimeConfig: make(config.ConfigurationMap), - ServiceNodePortRange: DefaultServiceNodePortRange, - StorageVersions: registered.AllPreferredGroupVersions(), + AdmissionControl: "AlwaysAdmit", + DefaultStorageMediaType: "application/json", + DefaultStorageVersions: registered.AllPreferredGroupVersions(), + DeleteCollectionWorkers: 1, + EnableGarbageCollection: true, + EnableProfiling: true, + EnableContentionProfiling: false, + EnableWatchCache: true, + LongRunningRequestRE: DefaultLongRunningRequestRE, + MasterCount: 1, + MasterServiceNamespace: api.NamespaceDefault, + MaxRequestsInFlight: 400, + MinRequestTimeout: 1800, + RuntimeConfig: make(config.ConfigurationMap), + ServiceNodePortRange: DefaultServiceNodePortRange, + StorageVersions: registered.AllPreferredGroupVersions(), } } @@ -209,29 +187,6 @@ func (s *ServerRunOptions) AddUniversalFlags(fs *pflag.FlagSet) { "will be used. If --bind-address is unspecified, the host's default interface will "+ "be used.") - fs.StringVar(&s.AuthorizationMode, "authorization-mode", s.AuthorizationMode, ""+ - "Ordered list of plug-ins to do authorization on secure port. Comma-delimited list of: "+ - strings.Join(AuthorizationModeChoices, ",")+".") - - fs.StringVar(&s.AuthorizationPolicyFile, "authorization-policy-file", s.AuthorizationPolicyFile, ""+ - "File with authorization policy in csv format, used with --authorization-mode=ABAC, on the secure port.") - - fs.StringVar(&s.AuthorizationWebhookConfigFile, "authorization-webhook-config-file", s.AuthorizationWebhookConfigFile, ""+ - "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.AuthorizationWebhookCacheAuthorizedTTL, "authorization-webhook-cache-authorized-ttl", - s.AuthorizationWebhookCacheAuthorizedTTL, - "The duration to cache 'authorized' responses from the webhook authorizer. Default is 5m.") - - fs.DurationVar(&s.AuthorizationWebhookCacheUnauthorizedTTL, - "authorization-webhook-cache-unauthorized-ttl", s.AuthorizationWebhookCacheUnauthorizedTTL, - "The duration to cache 'unauthorized' responses from the webhook authorizer. Default is 30s.") - - fs.StringVar(&s.AuthorizationRBACSuperUser, "authorization-rbac-super-user", s.AuthorizationRBACSuperUser, ""+ - "If specified, a username which avoids RBAC authorization checks and role binding "+ - "privilege escalation checks, to be used with --authorization-mode=RBAC.") - fs.StringVar(&s.CloudProvider, "cloud-provider", s.CloudProvider, "The provider for cloud services. Empty string for no provider.")