diff --git a/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go b/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go index 7d5bdac3919..7e4261b827f 100644 --- a/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go +++ b/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go @@ -32,7 +32,7 @@ func KubeadmFuzzerFuncs(t apitesting.TestingCommon) []interface{} { obj.API.AdvertiseAddress = "foo" obj.Networking.ServiceSubnet = "foo" obj.Networking.DNSDomain = "foo" - obj.AuthorizationMode = "foo" + obj.AuthorizationModes = []string{"foo"} obj.CertificatesDir = "foo" obj.APIServerCertSANs = []string{} obj.Token = "foo" diff --git a/cmd/kubeadm/app/apis/kubeadm/types.go b/cmd/kubeadm/app/apis/kubeadm/types.go index 0686f302b0e..04f3c7ad2d5 100644 --- a/cmd/kubeadm/app/apis/kubeadm/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/types.go @@ -33,12 +33,12 @@ type EnvParams struct { type MasterConfiguration struct { metav1.TypeMeta - API API - Etcd Etcd - Networking Networking - KubernetesVersion string - CloudProvider string - AuthorizationMode string + API API + Etcd Etcd + Networking Networking + KubernetesVersion string + CloudProvider string + AuthorizationModes []string Token string TokenTTL time.Duration diff --git a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/defaults.go b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/defaults.go index 4ea69723992..abca4add187 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/defaults.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/defaults.go @@ -59,8 +59,8 @@ func SetDefaults_MasterConfiguration(obj *MasterConfiguration) { obj.Networking.DNSDomain = DefaultServiceDNSDomain } - if obj.AuthorizationMode == "" { - obj.AuthorizationMode = DefaultAuthorizationMode + if len(obj.AuthorizationModes) == 0 { + obj.AuthorizationModes = []string{DefaultAuthorizationMode} } if obj.CertificatesDir == "" { diff --git a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/types.go b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/types.go index 3fe5d7946e0..a431573e8a1 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/types.go @@ -25,12 +25,12 @@ import ( type MasterConfiguration struct { metav1.TypeMeta `json:",inline"` - API API `json:"api"` - Etcd Etcd `json:"etcd"` - Networking Networking `json:"networking"` - KubernetesVersion string `json:"kubernetesVersion"` - CloudProvider string `json:"cloudProvider"` - AuthorizationMode string `json:"authorizationMode"` + API API `json:"api"` + Etcd Etcd `json:"etcd"` + Networking Networking `json:"networking"` + KubernetesVersion string `json:"kubernetesVersion"` + CloudProvider string `json:"cloudProvider"` + AuthorizationModes []string `json:"authorizationModes"` Token string `json:"token"` TokenTTL time.Duration `json:"tokenTTL"` diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go index f5bcf67af1f..e338c4aefcf 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go @@ -52,7 +52,7 @@ var cloudproviders = []string{ func ValidateMasterConfiguration(c *kubeadm.MasterConfiguration) field.ErrorList { allErrs := field.ErrorList{} allErrs = append(allErrs, ValidateCloudProvider(c.CloudProvider, field.NewPath("cloudprovider"))...) - allErrs = append(allErrs, ValidateAuthorizationMode(c.AuthorizationMode, field.NewPath("authorization-mode"))...) + allErrs = append(allErrs, ValidateAuthorizationModes(c.AuthorizationModes, field.NewPath("authorization-mode"))...) allErrs = append(allErrs, ValidateNetworking(&c.Networking, field.NewPath("networking"))...) allErrs = append(allErrs, ValidateAPIServerCertSANs(c.APIServerCertSANs, field.NewPath("cert-altnames"))...) allErrs = append(allErrs, ValidateAbsolutePath(c.CertificatesDir, field.NewPath("certificates-dir"))...) @@ -70,11 +70,23 @@ func ValidateNodeConfiguration(c *kubeadm.NodeConfiguration) field.ErrorList { return allErrs } -func ValidateAuthorizationMode(authzMode string, fldPath *field.Path) field.ErrorList { - if !authzmodes.IsValidAuthorizationMode(authzMode) { - return field.ErrorList{field.Invalid(fldPath, authzMode, "invalid authorization mode")} +func ValidateAuthorizationModes(authzModes []string, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + for _, authzMode := range authzModes { + if !authzmodes.IsValidAuthorizationMode(authzMode) { + allErrs = append(allErrs, field.Invalid(fldPath, authzMode, "invalid authorization mode")) + } } - return field.ErrorList{} + + found := map[string]bool{} + for _, authzMode := range authzModes { + if found[authzMode] { + allErrs = append(allErrs, field.Invalid(fldPath, authzMode, "duplicate authorization mode")) + continue + } + found[authzMode] = true + } + return allErrs } func ValidateDiscovery(c *kubeadm.NodeConfiguration, fldPath *field.Path) field.ErrorList { diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go index 287b413cf04..4c80c082e91 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go @@ -45,23 +45,26 @@ func TestValidateTokenDiscovery(t *testing.T) { } } -func TestValidateAuthorizationMode(t *testing.T) { +func TestValidateAuthorizationModes(t *testing.T) { var tests = []struct { - s string + 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 + {[]string{""}, nil, false}, + {[]string{"rBAC"}, nil, false}, // not supported + {[]string{"rBAC", "Webhook"}, nil, false}, // not supported + {[]string{"RBAC", "Webhook", "Webhook"}, nil, false}, // not supported + {[]string{"not valid"}, nil, false}, // not supported + {[]string{"RBAC"}, nil, true}, // supported + {[]string{"Webhook"}, nil, true}, // supported + {[]string{"RBAC", "Webhook"}, nil, true}, // supported } for _, rt := range tests { - actual := ValidateAuthorizationMode(rt.s, rt.f) + actual := ValidateAuthorizationModes(rt.s, rt.f) if (len(actual) == 0) != rt.expected { t.Errorf( - "failed ValidateAuthorizationMode:\n\texpected: %t\n\t actual: %t", + "failed ValidateAuthorizationModes:\n\texpected: %t\n\t actual: %t", rt.expected, (len(actual) == 0), ) @@ -172,7 +175,7 @@ func TestValidateMasterConfiguration(t *testing.T) { }{ {&kubeadm.MasterConfiguration{}, false}, {&kubeadm.MasterConfiguration{ - AuthorizationMode: "RBAC", + AuthorizationModes: []string{"RBAC"}, Networking: kubeadm.Networking{ ServiceSubnet: "10.96.0.1/12", DNSDomain: "cluster.local", @@ -180,7 +183,7 @@ func TestValidateMasterConfiguration(t *testing.T) { CertificatesDir: "/some/cert/dir", }, false}, {&kubeadm.MasterConfiguration{ - AuthorizationMode: "RBAC", + AuthorizationModes: []string{"RBAC"}, Networking: kubeadm.Networking{ ServiceSubnet: "10.96.0.1/12", DNSDomain: "cluster.local", diff --git a/cmd/kubeadm/app/cmd/defaults.go b/cmd/kubeadm/app/cmd/defaults.go index c503809cac3..2eb3e23ae6d 100644 --- a/cmd/kubeadm/app/cmd/defaults.go +++ b/cmd/kubeadm/app/cmd/defaults.go @@ -58,7 +58,7 @@ func setInitDynamicDefaults(cfg *kubeadmapi.MasterConfiguration) error { } fmt.Printf("[init] Using Kubernetes version: %s\n", cfg.KubernetesVersion) - fmt.Printf("[init] Using Authorization mode: %s\n", cfg.AuthorizationMode) + fmt.Printf("[init] Using Authorization mode: %v\n", cfg.AuthorizationModes) // Warn about the limitations with the current cloudprovider solution. if cfg.CloudProvider != "" { diff --git a/cmd/kubeadm/app/master/manifests.go b/cmd/kubeadm/app/master/manifests.go index e00e5994cb0..b1f416b7c3d 100644 --- a/cmd/kubeadm/app/master/manifests.go +++ b/cmd/kubeadm/app/master/manifests.go @@ -330,7 +330,7 @@ func getAPIServerCommand(cfg *kubeadmapi.MasterConfiguration, selfHosted bool) [ command = getComponentBaseCommand(apiServer) command = append(command, getExtraParameters(cfg.APIServerExtraArgs, defaultArguments)...) - command = append(command, getAuthzParameters(cfg.AuthorizationMode)...) + command = append(command, getAuthzParameters(cfg.AuthorizationModes)...) if selfHosted { command = append(command, "--advertise-address=$(POD_IP)") @@ -460,22 +460,23 @@ func getSelfHostedAPIServerEnv() []api.EnvVar { return append(getProxyEnvVars(), podIPEnvVar) } -func getAuthzParameters(authzMode string) []string { +func getAuthzParameters(modes []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) + for _, authzMode := range modes { + if len(authzMode) != 0 && authzMode != authzmodes.ModeRBAC { + authzModes = append(authzModes, authzMode) + } + 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) + } } 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 f9fb8ac23d1..35b31d70d45 100644 --- a/cmd/kubeadm/app/master/manifests_test.go +++ b/cmd/kubeadm/app/master/manifests_test.go @@ -652,44 +652,53 @@ func TestGetSchedulerCommand(t *testing.T) { func TestGetAuthzParameters(t *testing.T) { var tests = []struct { - authMode string + authMode []string expected []string }{ { - authMode: "", + authMode: []string{}, expected: []string{ "--authorization-mode=RBAC", }, }, { - authMode: "RBAC", + authMode: []string{"RBAC"}, expected: []string{ "--authorization-mode=RBAC", }, }, { - authMode: "AlwaysAllow", + authMode: []string{"AlwaysAllow"}, expected: []string{ "--authorization-mode=RBAC,AlwaysAllow", }, }, { - authMode: "AlwaysDeny", + authMode: []string{"AlwaysDeny"}, expected: []string{ "--authorization-mode=RBAC,AlwaysDeny", }, }, { - authMode: "ABAC", + authMode: []string{"ABAC"}, expected: []string{ "--authorization-mode=RBAC,ABAC", "--authorization-policy-file=/etc/kubernetes/abac_policy.json", }, }, { - authMode: "Webhook", + authMode: []string{"ABAC", "Webhook"}, expected: []string{ - "--authorization-mode=RBAC,Webhook", + "--authorization-mode=RBAC,ABAC,Webhook", + "--authorization-policy-file=/etc/kubernetes/abac_policy.json", + "--authorization-webhook-config-file=/etc/kubernetes/webhook_authz.conf", + }, + }, + { + authMode: []string{"ABAC", "RBAC", "Webhook"}, + expected: []string{ + "--authorization-mode=RBAC,ABAC,Webhook", + "--authorization-policy-file=/etc/kubernetes/abac_policy.json", "--authorization-webhook-config-file=/etc/kubernetes/webhook_authz.conf", }, }, @@ -697,14 +706,10 @@ func TestGetAuthzParameters(t *testing.T) { 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], - ) - } + sort.Strings(actual) + sort.Strings(rt.expected) + if !reflect.DeepEqual(actual, rt.expected) { + t.Errorf("failed getAuthzParameters:\nexpected:\n%v\nsaw:\n%v", rt.expected, actual) } } } diff --git a/cmd/kubeadm/app/preflight/checks.go b/cmd/kubeadm/app/preflight/checks.go index f139863889c..bceca054f85 100644 --- a/cmd/kubeadm/app/preflight/checks.go +++ b/cmd/kubeadm/app/preflight/checks.go @@ -525,11 +525,13 @@ func RunInitMasterChecks(cfg *kubeadmapi.MasterConfiguration) error { } // Check the config for authorization mode - switch cfg.AuthorizationMode { - case authzmodes.ModeABAC: - checks = append(checks, FileExistingCheck{Path: kubeadmconstants.AuthorizationPolicyPath}) - case authzmodes.ModeWebhook: - checks = append(checks, FileExistingCheck{Path: kubeadmconstants.AuthorizationWebhookConfigPath}) + for _, authzMode := range cfg.AuthorizationModes { + switch authzMode { + 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)