From ab344da565d8afa43763e4aa2a7266ab4b2f8781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20K=C3=A4ldstr=C3=B6m?= Date: Thu, 23 Feb 2017 15:27:16 +0200 Subject: [PATCH 1/2] Move the authorization mode constants into a separate package --- pkg/kubeapiserver/authorizer/BUILD | 7 ++- pkg/kubeapiserver/authorizer/config.go | 25 ++++------- pkg/kubeapiserver/authorizer/config_test.go | 13 +++--- pkg/kubeapiserver/authorizer/modes/BUILD | 35 +++++++++++++++ pkg/kubeapiserver/authorizer/modes/modes.go | 37 +++++++++++++++ .../authorizer/modes/modes_test.go | 45 +++++++++++++++++++ pkg/kubeapiserver/options/BUILD | 1 + pkg/kubeapiserver/options/authentication.go | 4 +- pkg/kubeapiserver/options/authorization.go | 7 ++- 9 files changed, 145 insertions(+), 29 deletions(-) create mode 100644 pkg/kubeapiserver/authorizer/modes/BUILD create mode 100644 pkg/kubeapiserver/authorizer/modes/modes.go create mode 100644 pkg/kubeapiserver/authorizer/modes/modes_test.go diff --git a/pkg/kubeapiserver/authorizer/BUILD b/pkg/kubeapiserver/authorizer/BUILD index 81b9b921280..89f46026310 100644 --- a/pkg/kubeapiserver/authorizer/BUILD +++ b/pkg/kubeapiserver/authorizer/BUILD @@ -16,6 +16,7 @@ go_test( ], library = ":go_default_library", tags = ["automanaged"], + deps = ["//pkg/kubeapiserver/authorizer/modes:go_default_library"], ) go_library( @@ -25,6 +26,7 @@ go_library( deps = [ "//pkg/auth/authorizer/abac:go_default_library", "//pkg/controller/informers:go_default_library", + "//pkg/kubeapiserver/authorizer/modes:go_default_library", "//plugin/pkg/auth/authorizer/rbac:go_default_library", "//vendor:k8s.io/apiserver/pkg/authorization/authorizer", "//vendor:k8s.io/apiserver/pkg/authorization/authorizerfactory", @@ -42,6 +44,9 @@ filegroup( filegroup( name = "all-srcs", - srcs = [":package-srcs"], + srcs = [ + ":package-srcs", + "//pkg/kubeapiserver/authorizer/modes:all-srcs", + ], tags = ["automanaged"], ) diff --git a/pkg/kubeapiserver/authorizer/config.go b/pkg/kubeapiserver/authorizer/config.go index 750edc2eddd..3d6389ceeed 100644 --- a/pkg/kubeapiserver/authorizer/config.go +++ b/pkg/kubeapiserver/authorizer/config.go @@ -27,17 +27,10 @@ import ( "k8s.io/apiserver/plugin/pkg/authorizer/webhook" "k8s.io/kubernetes/pkg/auth/authorizer/abac" "k8s.io/kubernetes/pkg/controller/informers" + "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes" "k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac" ) -const ( - ModeAlwaysAllow string = "AlwaysAllow" - ModeAlwaysDeny string = "AlwaysDeny" - ModeABAC string = "ABAC" - ModeWebhook string = "Webhook" - ModeRBAC string = "RBAC" -) - type AuthorizationConfig struct { AuthorizationModes []string @@ -79,11 +72,11 @@ func (config AuthorizationConfig) New() (authorizer.Authorizer, error) { } // Keep cases in sync with constant list above. switch authorizationMode { - case ModeAlwaysAllow: + case modes.ModeAlwaysAllow: authorizers = append(authorizers, authorizerfactory.NewAlwaysAllowAuthorizer()) - case ModeAlwaysDeny: + case modes.ModeAlwaysDeny: authorizers = append(authorizers, authorizerfactory.NewAlwaysDenyAuthorizer()) - case ModeABAC: + case modes.ModeABAC: if config.PolicyFile == "" { return nil, errors.New("ABAC's authorization policy file not passed") } @@ -92,7 +85,7 @@ func (config AuthorizationConfig) New() (authorizer.Authorizer, error) { return nil, err } authorizers = append(authorizers, abacAuthorizer) - case ModeWebhook: + case modes.ModeWebhook: if config.WebhookConfigFile == "" { return nil, errors.New("Webhook's configuration file not passed") } @@ -103,7 +96,7 @@ func (config AuthorizationConfig) New() (authorizer.Authorizer, error) { return nil, err } authorizers = append(authorizers, webhookAuthorizer) - case ModeRBAC: + case modes.ModeRBAC: rbacAuthorizer := rbac.New( config.InformerFactory.Roles().Lister(), config.InformerFactory.RoleBindings().Lister(), @@ -117,13 +110,13 @@ func (config AuthorizationConfig) New() (authorizer.Authorizer, error) { authorizerMap[authorizationMode] = true } - if !authorizerMap[ModeABAC] && config.PolicyFile != "" { + if !authorizerMap[modes.ModeABAC] && config.PolicyFile != "" { return nil, errors.New("Cannot specify --authorization-policy-file without mode ABAC") } - if !authorizerMap[ModeWebhook] && config.WebhookConfigFile != "" { + if !authorizerMap[modes.ModeWebhook] && config.WebhookConfigFile != "" { return nil, errors.New("Cannot specify --authorization-webhook-config-file without mode Webhook") } - if !authorizerMap[ModeRBAC] && config.RBACSuperUser != "" { + if !authorizerMap[modes.ModeRBAC] && config.RBACSuperUser != "" { return nil, errors.New("Cannot specify --authorization-rbac-super-user without mode RBAC") } diff --git a/pkg/kubeapiserver/authorizer/config_test.go b/pkg/kubeapiserver/authorizer/config_test.go index 2353cbce3c8..ac2d5e983fd 100644 --- a/pkg/kubeapiserver/authorizer/config_test.go +++ b/pkg/kubeapiserver/authorizer/config_test.go @@ -17,6 +17,7 @@ limitations under the License. package authorizer import ( + "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes" "testing" ) @@ -39,19 +40,19 @@ func TestNew(t *testing.T) { { // ModeAlwaysAllow and ModeAlwaysDeny should return without authorizationPolicyFile // but error if one is given - config: AuthorizationConfig{AuthorizationModes: []string{ModeAlwaysAllow, ModeAlwaysDeny}}, + config: AuthorizationConfig{AuthorizationModes: []string{modes.ModeAlwaysAllow, modes.ModeAlwaysDeny}}, msg: "returned an error for valid config", }, { // ModeABAC requires a policy file - config: AuthorizationConfig{AuthorizationModes: []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeABAC}}, + config: AuthorizationConfig{AuthorizationModes: []string{modes.ModeAlwaysAllow, modes.ModeAlwaysDeny, modes.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}, + AuthorizationModes: []string{modes.ModeAlwaysAllow, modes.ModeAlwaysDeny, modes.ModeABAC}, PolicyFile: examplePolicyFile, }, msg: "errored while using a valid policy file", @@ -60,7 +61,7 @@ func TestNew(t *testing.T) { // Authorization Policy file cannot be used without ModeABAC config: AuthorizationConfig{ - AuthorizationModes: []string{ModeAlwaysAllow, ModeAlwaysDeny}, + AuthorizationModes: []string{modes.ModeAlwaysAllow, modes.ModeAlwaysDeny}, PolicyFile: examplePolicyFile, }, wantErr: true, @@ -74,14 +75,14 @@ func TestNew(t *testing.T) { }, { // ModeWebhook requires at minimum a target. - config: AuthorizationConfig{AuthorizationModes: []string{ModeWebhook}}, + config: AuthorizationConfig{AuthorizationModes: []string{modes.ModeWebhook}}, wantErr: true, msg: "should have errored when config was empty with ModeWebhook", }, { // Cannot provide webhook flags without ModeWebhook config: AuthorizationConfig{ - AuthorizationModes: []string{ModeAlwaysAllow}, + AuthorizationModes: []string{modes.ModeAlwaysAllow}, WebhookConfigFile: "authz_webhook_config.yml", }, wantErr: true, diff --git a/pkg/kubeapiserver/authorizer/modes/BUILD b/pkg/kubeapiserver/authorizer/modes/BUILD new file mode 100644 index 00000000000..df54566faef --- /dev/null +++ b/pkg/kubeapiserver/authorizer/modes/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 = ["modes_test.go"], + library = ":go_default_library", + tags = ["automanaged"], +) + +go_library( + name = "go_default_library", + srcs = ["modes.go"], + tags = ["automanaged"], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], +) diff --git a/pkg/kubeapiserver/authorizer/modes/modes.go b/pkg/kubeapiserver/authorizer/modes/modes.go new file mode 100644 index 00000000000..0d2c2442090 --- /dev/null +++ b/pkg/kubeapiserver/authorizer/modes/modes.go @@ -0,0 +1,37 @@ +/* +Copyright 2017 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 modes + +const ( + ModeAlwaysAllow string = "AlwaysAllow" + ModeAlwaysDeny string = "AlwaysDeny" + ModeABAC string = "ABAC" + ModeWebhook string = "Webhook" + ModeRBAC string = "RBAC" +) + +var AuthorizationModeChoices = []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeABAC, ModeWebhook, ModeRBAC} + +// IsValidAuthorizationMode returns true if the given authorization mode is a valid one for the apiserver +func IsValidAuthorizationMode(authzMode string) bool { + for _, validMode := range AuthorizationModeChoices { + if authzMode == validMode { + return true + } + } + return false +} diff --git a/pkg/kubeapiserver/authorizer/modes/modes_test.go b/pkg/kubeapiserver/authorizer/modes/modes_test.go new file mode 100644 index 00000000000..63379234bf4 --- /dev/null +++ b/pkg/kubeapiserver/authorizer/modes/modes_test.go @@ -0,0 +1,45 @@ +/* +Copyright 2017 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 modes + +import "testing" + +func TestIsValidAuthorizationMode(t *testing.T) { + var tests = []struct { + authzMode string + expected bool + }{ + {"", false}, + {"rBAC", false}, // not supported + {"falsy value", false}, // not supported + {"RBAC", true}, // supported + {"ABAC", true}, // supported + {"Webhook", true}, // supported + {"AlwaysAllow", true}, // supported + {"AlwaysDeny", true}, // supported + } + for _, rt := range tests { + actual := IsValidAuthorizationMode(rt.authzMode) + if actual != rt.expected { + t.Errorf( + "failed ValidAuthorizationMode:\n\texpected: %t\n\t actual: %t", + rt.expected, + actual, + ) + } + } +} diff --git a/pkg/kubeapiserver/options/BUILD b/pkg/kubeapiserver/options/BUILD index 70efdfed679..15d867467cb 100644 --- a/pkg/kubeapiserver/options/BUILD +++ b/pkg/kubeapiserver/options/BUILD @@ -25,6 +25,7 @@ go_library( "//pkg/controller/informers:go_default_library", "//pkg/kubeapiserver/authenticator:go_default_library", "//pkg/kubeapiserver/authorizer:go_default_library", + "//pkg/kubeapiserver/authorizer/modes:go_default_library", "//vendor:github.com/golang/glog", "//vendor:github.com/spf13/pflag", "//vendor:k8s.io/apimachinery/pkg/runtime/schema", diff --git a/pkg/kubeapiserver/options/authentication.go b/pkg/kubeapiserver/options/authentication.go index da9c3fc1e79..0bdc8da2f21 100644 --- a/pkg/kubeapiserver/options/authentication.go +++ b/pkg/kubeapiserver/options/authentication.go @@ -27,7 +27,7 @@ import ( genericapiserver "k8s.io/apiserver/pkg/server" genericoptions "k8s.io/apiserver/pkg/server/options" "k8s.io/kubernetes/pkg/kubeapiserver/authenticator" - "k8s.io/kubernetes/pkg/kubeapiserver/authorizer" + authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes" ) type BuiltInAuthenticationOptions struct { @@ -353,7 +353,7 @@ func (o *BuiltInAuthenticationOptions) ApplyAuthorization(authorization *BuiltIn if o.Anonymous.Allow { found := false for _, mode := range strings.Split(authorization.Mode, ",") { - if mode == authorizer.ModeAlwaysAllow { + if mode == authzmodes.ModeAlwaysAllow { found = true break } diff --git a/pkg/kubeapiserver/options/authorization.go b/pkg/kubeapiserver/options/authorization.go index 96d378d3dcc..de46ab3849b 100644 --- a/pkg/kubeapiserver/options/authorization.go +++ b/pkg/kubeapiserver/options/authorization.go @@ -24,10 +24,9 @@ import ( "k8s.io/kubernetes/pkg/controller/informers" "k8s.io/kubernetes/pkg/kubeapiserver/authorizer" + authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes" ) -var AuthorizationModeChoices = []string{authorizer.ModeAlwaysAllow, authorizer.ModeAlwaysDeny, authorizer.ModeABAC, authorizer.ModeWebhook, authorizer.ModeRBAC} - type BuiltInAuthorizationOptions struct { Mode string PolicyFile string @@ -38,7 +37,7 @@ type BuiltInAuthorizationOptions struct { func NewBuiltInAuthorizationOptions() *BuiltInAuthorizationOptions { return &BuiltInAuthorizationOptions{ - Mode: authorizer.ModeAlwaysAllow, + Mode: authzmodes.ModeAlwaysAllow, WebhookCacheAuthorizedTTL: 5 * time.Minute, WebhookCacheUnauthorizedTTL: 30 * time.Second, } @@ -52,7 +51,7 @@ func (s *BuiltInAuthorizationOptions) Validate() []error { 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, ",")+".") + strings.Join(authzmodes.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.") From 3c322d04de52d6f3dcf5e804ebbab11bfbf28a0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20K=C3=A4ldstr=C3=B6m?= Date: Thu, 23 Feb 2017 15:30:24 +0200 Subject: [PATCH 2/2] kubeadm: Always enable RBAC, validate authz mode and improve the code slightly --- cmd/kubeadm/app/apis/kubeadm/validation/BUILD | 1 + .../app/apis/kubeadm/validation/validation.go | 11 +++- .../kubeadm/validation/validation_test.go | 40 ++++++++++++ cmd/kubeadm/app/cmd/init.go | 8 +-- cmd/kubeadm/app/constants/constants.go | 22 +++---- cmd/kubeadm/app/master/BUILD | 1 + cmd/kubeadm/app/master/manifests.go | 30 ++++++--- cmd/kubeadm/app/master/manifests_test.go | 62 +++++++++++++++++++ cmd/kubeadm/app/preflight/BUILD | 1 + cmd/kubeadm/app/preflight/checks.go | 11 ++-- 10 files changed, 155 insertions(+), 32 deletions(-) diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/BUILD b/cmd/kubeadm/app/apis/kubeadm/validation/BUILD index e1523b87f8d..710989116a4 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/BUILD +++ b/cmd/kubeadm/app/apis/kubeadm/validation/BUILD @@ -15,6 +15,7 @@ go_library( deps = [ "//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/constants:go_default_library", + "//pkg/kubeapiserver/authorizer/modes:go_default_library", "//pkg/registry/core/service/ipallocator:go_default_library", "//vendor:k8s.io/apimachinery/pkg/util/validation/field", ], diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go index 1d2f56f28a3..22f6f95211f 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go @@ -22,6 +22,7 @@ import ( "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" + authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes" "k8s.io/kubernetes/pkg/registry/core/service/ipallocator" ) @@ -43,8 +44,9 @@ var cloudproviders = []string{ func ValidateMasterConfiguration(c *kubeadm.MasterConfiguration) field.ErrorList { allErrs := field.ErrorList{} allErrs = append(allErrs, ValidateDiscovery(&c.Discovery, field.NewPath("discovery"))...) - allErrs = append(allErrs, ValidateDiscovery(&c.Discovery, field.NewPath("service subnet"))...) + allErrs = append(allErrs, ValidateServiceSubnet(c.Networking.ServiceSubnet, field.NewPath("service subnet"))...) allErrs = append(allErrs, ValidateCloudProvider(c.CloudProvider, field.NewPath("cloudprovider"))...) + allErrs = append(allErrs, ValidateAuthorizationMode(c.AuthorizationMode, field.NewPath("authorization-mode"))...) return allErrs } @@ -96,6 +98,13 @@ func ValidateTokenDiscovery(c *kubeadm.TokenDiscovery, fldPath *field.Path) fiel return allErrs } +func ValidateAuthorizationMode(authzMode string, fldPath *field.Path) field.ErrorList { + if !authzmodes.IsValidAuthorizationMode(authzMode) { + return field.ErrorList{field.Invalid(fldPath, nil, "invalid authorization mode")} + } + return field.ErrorList{} +} + func ValidateServiceSubnet(subnet string, fldPath *field.Path) field.ErrorList { _, svcSubnet, err := net.ParseCIDR(subnet) if err != nil { diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go index 54bed3b1890..2fb6157ee10 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go @@ -46,6 +46,30 @@ func TestValidateTokenDiscovery(t *testing.T) { } } +func TestValidateAuthorizationMode(t *testing.T) { + var tests = []struct { + s string + f *field.Path + expected bool + }{ + {"", nil, false}, + {"rBAC", nil, false}, // not supported + {"not valid", nil, false}, // not supported + {"RBAC", nil, true}, // supported + {"Webhook", nil, true}, // supported + } + for _, rt := range tests { + actual := ValidateAuthorizationMode(rt.s, rt.f) + if (len(actual) == 0) != rt.expected { + t.Errorf( + "failed ValidateAuthorizationMode:\n\texpected: %t\n\t actual: %t", + rt.expected, + (len(actual) == 0), + ) + } + } +} + func TestValidateServiceSubnet(t *testing.T) { var tests = []struct { s string @@ -111,16 +135,28 @@ func TestValidateMasterConfiguration(t *testing.T) { Addresses: []string{"foobar"}, }, }, + AuthorizationMode: "RBAC", + Networking: kubeadm.Networking{ + ServiceSubnet: "10.96.0.1/12", + }, }, false}, {&kubeadm.MasterConfiguration{ Discovery: kubeadm.Discovery{ HTTPS: &kubeadm.HTTPSDiscovery{URL: "foo"}, }, + AuthorizationMode: "RBAC", + Networking: kubeadm.Networking{ + ServiceSubnet: "10.96.0.1/12", + }, }, true}, {&kubeadm.MasterConfiguration{ Discovery: kubeadm.Discovery{ File: &kubeadm.FileDiscovery{Path: "foo"}, }, + AuthorizationMode: "RBAC", + Networking: kubeadm.Networking{ + ServiceSubnet: "10.96.0.1/12", + }, }, true}, {&kubeadm.MasterConfiguration{ Discovery: kubeadm.Discovery{ @@ -130,6 +166,10 @@ func TestValidateMasterConfiguration(t *testing.T) { Addresses: []string{"foobar"}, }, }, + AuthorizationMode: "RBAC", + Networking: kubeadm.Networking{ + ServiceSubnet: "10.96.0.1/12", + }, }, true}, } for _, rt := range tests { diff --git a/cmd/kubeadm/app/cmd/init.go b/cmd/kubeadm/app/cmd/init.go index 50e32f1e669..1da11c70cad 100644 --- a/cmd/kubeadm/app/cmd/init.go +++ b/cmd/kubeadm/app/cmd/init.go @@ -253,11 +253,9 @@ func (i *Init) Run(out io.Writer) error { return err } - if i.cfg.AuthorizationMode == kubeadmconstants.AuthzModeRBAC { - err = apiconfigphase.CreateRBACRules(client) - if err != nil { - return err - } + err = apiconfigphase.CreateRBACRules(client) + if err != nil { + return err } if err := addonsphase.CreateEssentialAddons(i.cfg, client); err != nil { diff --git a/cmd/kubeadm/app/constants/constants.go b/cmd/kubeadm/app/constants/constants.go index 8ff22523feb..a7b4b70151e 100644 --- a/cmd/kubeadm/app/constants/constants.go +++ b/cmd/kubeadm/app/constants/constants.go @@ -16,11 +16,14 @@ limitations under the License. package constants -import "time" +import ( + "path" + "time" +) const ( - AuthorizationPolicyFile = "abac_policy.json" - AuthorizationWebhookConfigFile = "webhook_authz.conf" + // KubernetesDir is the directory kubernetes owns for storing various configuration files + KubernetesDir = "/etc/kubernetes" CACertAndKeyBaseName = "ca" CACertName = "ca.crt" @@ -49,14 +52,6 @@ const ( AdminKubeConfigFileName = "admin.conf" KubeletKubeConfigFileName = "kubelet.conf" - // TODO: These constants should actually come from pkg/kubeapiserver/authorizer, but we can't vendor that package in now - // because of all the other sub-packages that would get vendored. To fix this, a pkg/kubeapiserver/authorizer/modes package - // or similar should exist that only has these constants; then we can vendor it. - AuthzModeAlwaysAllow = "AlwaysAllow" - AuthzModeABAC = "ABAC" - AuthzModeRBAC = "RBAC" - AuthzModeWebhook = "Webhook" - // Important: a "v"-prefix shouldn't exist here; semver doesn't allow that MinimumControlPlaneVersion = "1.6.0-alpha.2" @@ -83,3 +78,8 @@ const ( // The file name of the tokens file that can be used for bootstrapping CSVTokenFileName = "tokens.csv" ) + +var ( + AuthorizationPolicyPath = path.Join(KubernetesDir, "abac_policy.json") + AuthorizationWebhookConfigPath = path.Join(KubernetesDir, "webhook_authz.conf") +) diff --git a/cmd/kubeadm/app/master/BUILD b/cmd/kubeadm/app/master/BUILD index 3a952a67cdb..b98fece40b8 100644 --- a/cmd/kubeadm/app/master/BUILD +++ b/cmd/kubeadm/app/master/BUILD @@ -24,6 +24,7 @@ go_library( "//cmd/kubeadm/app/images:go_default_library", "//cmd/kubeadm/app/util:go_default_library", "//cmd/kubeadm/app/util/kubeconfig:go_default_library", + "//pkg/kubeapiserver/authorizer/modes:go_default_library", "//pkg/kubectl/cmd/util:go_default_library", "//vendor:github.com/ghodss/yaml", "//vendor:k8s.io/apimachinery/pkg/api/errors", diff --git a/cmd/kubeadm/app/master/manifests.go b/cmd/kubeadm/app/master/manifests.go index a3d9d04f447..03d0dead9df 100644 --- a/cmd/kubeadm/app/master/manifests.go +++ b/cmd/kubeadm/app/master/manifests.go @@ -32,6 +32,7 @@ import ( kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/cmd/kubeadm/app/images" + authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" ) @@ -326,15 +327,7 @@ func getAPIServerCommand(cfg *kubeadmapi.MasterConfiguration, selfHosted bool) [ "--requestheader-allowed-names=front-proxy-client", ) - if cfg.AuthorizationMode != "" { - command = append(command, "--authorization-mode="+cfg.AuthorizationMode) - switch cfg.AuthorizationMode { - case kubeadmconstants.AuthzModeABAC: - command = append(command, "--authorization-policy-file="+path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, kubeadmconstants.AuthorizationPolicyFile)) - case kubeadmconstants.AuthzModeWebhook: - command = append(command, "--authorization-webhook-config-file="+path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, kubeadmconstants.AuthorizationWebhookConfigFile)) - } - } + command = append(command, getAuthzParameters(cfg.AuthorizationMode)...) // Use first address we are given if len(cfg.API.AdvertiseAddresses) > 0 { @@ -459,3 +452,22 @@ func getSelfHostedAPIServerEnv() []api.EnvVar { return append(getProxyEnvVars(), podIPEnvVar) } + +func getAuthzParameters(authzMode string) []string { + command := []string{} + // RBAC is always on. If the user specified + authzModes := []string{authzmodes.ModeRBAC} + if len(authzMode) != 0 && authzMode != authzmodes.ModeRBAC { + authzModes = append(authzModes, authzMode) + } + + command = append(command, "--authorization-mode="+strings.Join(authzModes, ",")) + + switch authzMode { + case authzmodes.ModeABAC: + command = append(command, "--authorization-policy-file="+kubeadmconstants.AuthorizationPolicyPath) + case authzmodes.ModeWebhook: + command = append(command, "--authorization-webhook-config-file="+kubeadmconstants.AuthorizationWebhookConfigPath) + } + return command +} diff --git a/cmd/kubeadm/app/master/manifests_test.go b/cmd/kubeadm/app/master/manifests_test.go index 353ff5459b5..375ecb244ae 100644 --- a/cmd/kubeadm/app/master/manifests_test.go +++ b/cmd/kubeadm/app/master/manifests_test.go @@ -388,6 +388,7 @@ func TestGetAPIServerCommand(t *testing.T) { "--requestheader-extra-headers-prefix=X-Remote-Extra-", "--requestheader-client-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/front-proxy-ca.crt", "--requestheader-allowed-names=front-proxy-client", + "--authorization-mode=RBAC", "--etcd-servers=http://127.0.0.1:2379", }, }, @@ -417,6 +418,7 @@ func TestGetAPIServerCommand(t *testing.T) { "--requestheader-extra-headers-prefix=X-Remote-Extra-", "--requestheader-client-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/front-proxy-ca.crt", "--requestheader-allowed-names=front-proxy-client", + "--authorization-mode=RBAC", "--advertise-address=foo", "--etcd-servers=http://127.0.0.1:2379", }, @@ -448,6 +450,7 @@ func TestGetAPIServerCommand(t *testing.T) { "--requestheader-extra-headers-prefix=X-Remote-Extra-", "--requestheader-client-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/front-proxy-ca.crt", "--requestheader-allowed-names=front-proxy-client", + "--authorization-mode=RBAC", "--etcd-servers=http://127.0.0.1:2379", "--etcd-certfile=fiz", "--etcd-keyfile=faz", @@ -567,3 +570,62 @@ func TestGetSchedulerCommand(t *testing.T) { } } } + +func TestGetAuthzParameters(t *testing.T) { + var tests = []struct { + authMode string + expected []string + }{ + { + authMode: "", + expected: []string{ + "--authorization-mode=RBAC", + }, + }, + { + authMode: "RBAC", + expected: []string{ + "--authorization-mode=RBAC", + }, + }, + { + authMode: "AlwaysAllow", + expected: []string{ + "--authorization-mode=RBAC,AlwaysAllow", + }, + }, + { + authMode: "AlwaysDeny", + expected: []string{ + "--authorization-mode=RBAC,AlwaysDeny", + }, + }, + { + authMode: "ABAC", + expected: []string{ + "--authorization-mode=RBAC,ABAC", + "--authorization-policy-file=/etc/kubernetes/abac_policy.json", + }, + }, + { + authMode: "Webhook", + expected: []string{ + "--authorization-mode=RBAC,Webhook", + "--authorization-webhook-config-file=/etc/kubernetes/webhook_authz.conf", + }, + }, + } + + for _, rt := range tests { + actual := getAuthzParameters(rt.authMode) + for i := range actual { + if actual[i] != rt.expected[i] { + t.Errorf( + "failed getAuthzParameters:\n\texpected: %s\n\t actual: %s", + rt.expected[i], + actual[i], + ) + } + } + } +} diff --git a/cmd/kubeadm/app/preflight/BUILD b/cmd/kubeadm/app/preflight/BUILD index d967b98999b..774008ab128 100644 --- a/cmd/kubeadm/app/preflight/BUILD +++ b/cmd/kubeadm/app/preflight/BUILD @@ -16,6 +16,7 @@ go_library( "//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/constants:go_default_library", "//pkg/api/validation:go_default_library", + "//pkg/kubeapiserver/authorizer/modes:go_default_library", "//pkg/util/initsystem:go_default_library", "//pkg/util/node:go_default_library", "//test/e2e_node/system:go_default_library", diff --git a/cmd/kubeadm/app/preflight/checks.go b/cmd/kubeadm/app/preflight/checks.go index d94b752f0cc..cc7ec74cec6 100644 --- a/cmd/kubeadm/app/preflight/checks.go +++ b/cmd/kubeadm/app/preflight/checks.go @@ -31,6 +31,7 @@ import ( kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/pkg/api/validation" + authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes" "k8s.io/kubernetes/pkg/util/initsystem" "k8s.io/kubernetes/pkg/util/node" "k8s.io/kubernetes/test/e2e_node/system" @@ -363,12 +364,10 @@ func RunInitMasterChecks(cfg *kubeadmapi.MasterConfiguration) error { // Check the config for authorization mode switch cfg.AuthorizationMode { - case kubeadmconstants.AuthzModeABAC: - authorizationPolicyPath := filepath.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, kubeadmconstants.AuthorizationPolicyFile) - checks = append(checks, FileExistingCheck{Path: authorizationPolicyPath}) - case kubeadmconstants.AuthzModeWebhook: - authorizationWebhookConfigPath := filepath.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, kubeadmconstants.AuthorizationWebhookConfigFile) - checks = append(checks, FileExistingCheck{Path: authorizationWebhookConfigPath}) + case authzmodes.ModeABAC: + checks = append(checks, FileExistingCheck{Path: kubeadmconstants.AuthorizationPolicyPath}) + case authzmodes.ModeWebhook: + checks = append(checks, FileExistingCheck{Path: kubeadmconstants.AuthorizationWebhookConfigPath}) } return RunChecks(checks, os.Stderr)