From a3564c0aa89ac27466d922710af4f4cbeaf9fe6b Mon Sep 17 00:00:00 2001 From: deads2k Date: Wed, 21 Dec 2016 09:53:18 -0500 Subject: [PATCH] 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 7a9c479fe2..a29d567fcd 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 6ee21db568..d5c3b106c6 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 cbfcd221c6..6082f2be6e 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 7e1c41a1ee..6c6137c4d4 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 08f4ebc006..d28d2d7c9f 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 cfe6761af2..86df67734e 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 1c5f7a86fe..f05a551c0e 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 4cca4461d9..9c292293ff 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 26ac63965e..3f9dc29b00 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 ec53e5be41..11f0ea34c0 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 63c6db61ad..a5ea71f9b6 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 bcb81315a4..f41f222bb8 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 f6dfb1d814..4e544c0e3e 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 0000000000..72e0212304 --- /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 0000000000..cb105469a9 --- /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 0000000000..1ebecdea3b --- /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 0000000000..2353cbce3c --- /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 0000000000..aa18901607 --- /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 0000000000..bafc2f7d23 --- /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 0000000000..96d378d3dc --- /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, + } +}