Merge pull request #41810 from luxas/kubeadm_rbac_default

Automatic merge from submit-queue (batch tested with PRs 38702, 41810, 41778, 41858, 41872)

Always enable RBAC in kubeadm and make a pkg with authorization constants

**What this PR does / why we need it**:

This PR:
 - Splits the authz constants out into a dedicated package, so consumers don't have to import lots of other things (informers, etc...)
 - Makes a `IsValidAuthorizationMode` function for easy checking
 - Hooks up kubeadm against the new constant package, for example using the validation method when validating the kubeadm API obj
 - Always enables RBAC in kubeadm as discussed with @liggitt and @jbeda 
   - This because we have to grant some rules in all cases for kubeadm (for instance, making the cluster-info configmap public) 
 - Adds more unit tests

**Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #

**Special notes for your reviewer**:

**Release note**:

```release-note
NONE
```
@liggitt @jbeda @errordeveloper @dmmcquay @pires @deads2k
This commit is contained in:
Kubernetes Submit Queue 2017-02-23 07:54:36 -08:00 committed by GitHub
commit 3418c8eaf2
19 changed files with 300 additions and 61 deletions

View File

@ -15,6 +15,7 @@ go_library(
deps = [ deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/constants: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", "//pkg/registry/core/service/ipallocator:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/util/validation/field", "//vendor:k8s.io/apimachinery/pkg/util/validation/field",
], ],

View File

@ -22,6 +22,7 @@ import (
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator" "k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
) )
@ -43,8 +44,9 @@ var cloudproviders = []string{
func ValidateMasterConfiguration(c *kubeadm.MasterConfiguration) field.ErrorList { func ValidateMasterConfiguration(c *kubeadm.MasterConfiguration) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidateDiscovery(&c.Discovery, field.NewPath("discovery"))...) 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, ValidateCloudProvider(c.CloudProvider, field.NewPath("cloudprovider"))...)
allErrs = append(allErrs, ValidateAuthorizationMode(c.AuthorizationMode, field.NewPath("authorization-mode"))...)
return allErrs return allErrs
} }
@ -96,6 +98,13 @@ func ValidateTokenDiscovery(c *kubeadm.TokenDiscovery, fldPath *field.Path) fiel
return allErrs 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 { func ValidateServiceSubnet(subnet string, fldPath *field.Path) field.ErrorList {
_, svcSubnet, err := net.ParseCIDR(subnet) _, svcSubnet, err := net.ParseCIDR(subnet)
if err != nil { if err != nil {

View File

@ -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) { func TestValidateServiceSubnet(t *testing.T) {
var tests = []struct { var tests = []struct {
s string s string
@ -111,16 +135,28 @@ func TestValidateMasterConfiguration(t *testing.T) {
Addresses: []string{"foobar"}, Addresses: []string{"foobar"},
}, },
}, },
AuthorizationMode: "RBAC",
Networking: kubeadm.Networking{
ServiceSubnet: "10.96.0.1/12",
},
}, false}, }, false},
{&kubeadm.MasterConfiguration{ {&kubeadm.MasterConfiguration{
Discovery: kubeadm.Discovery{ Discovery: kubeadm.Discovery{
HTTPS: &kubeadm.HTTPSDiscovery{URL: "foo"}, HTTPS: &kubeadm.HTTPSDiscovery{URL: "foo"},
}, },
AuthorizationMode: "RBAC",
Networking: kubeadm.Networking{
ServiceSubnet: "10.96.0.1/12",
},
}, true}, }, true},
{&kubeadm.MasterConfiguration{ {&kubeadm.MasterConfiguration{
Discovery: kubeadm.Discovery{ Discovery: kubeadm.Discovery{
File: &kubeadm.FileDiscovery{Path: "foo"}, File: &kubeadm.FileDiscovery{Path: "foo"},
}, },
AuthorizationMode: "RBAC",
Networking: kubeadm.Networking{
ServiceSubnet: "10.96.0.1/12",
},
}, true}, }, true},
{&kubeadm.MasterConfiguration{ {&kubeadm.MasterConfiguration{
Discovery: kubeadm.Discovery{ Discovery: kubeadm.Discovery{
@ -130,6 +166,10 @@ func TestValidateMasterConfiguration(t *testing.T) {
Addresses: []string{"foobar"}, Addresses: []string{"foobar"},
}, },
}, },
AuthorizationMode: "RBAC",
Networking: kubeadm.Networking{
ServiceSubnet: "10.96.0.1/12",
},
}, true}, }, true},
} }
for _, rt := range tests { for _, rt := range tests {

View File

@ -253,12 +253,10 @@ func (i *Init) Run(out io.Writer) error {
return err return err
} }
if i.cfg.AuthorizationMode == kubeadmconstants.AuthzModeRBAC {
err = apiconfigphase.CreateRBACRules(client) err = apiconfigphase.CreateRBACRules(client)
if err != nil { if err != nil {
return err return err
} }
}
if err := addonsphase.CreateEssentialAddons(i.cfg, client); err != nil { if err := addonsphase.CreateEssentialAddons(i.cfg, client); err != nil {
return err return err

View File

@ -16,11 +16,14 @@ limitations under the License.
package constants package constants
import "time" import (
"path"
"time"
)
const ( const (
AuthorizationPolicyFile = "abac_policy.json" // KubernetesDir is the directory kubernetes owns for storing various configuration files
AuthorizationWebhookConfigFile = "webhook_authz.conf" KubernetesDir = "/etc/kubernetes"
CACertAndKeyBaseName = "ca" CACertAndKeyBaseName = "ca"
CACertName = "ca.crt" CACertName = "ca.crt"
@ -49,14 +52,6 @@ const (
AdminKubeConfigFileName = "admin.conf" AdminKubeConfigFileName = "admin.conf"
KubeletKubeConfigFileName = "kubelet.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 // Important: a "v"-prefix shouldn't exist here; semver doesn't allow that
MinimumControlPlaneVersion = "1.6.0-alpha.2" MinimumControlPlaneVersion = "1.6.0-alpha.2"
@ -83,3 +78,8 @@ const (
// The file name of the tokens file that can be used for bootstrapping // The file name of the tokens file that can be used for bootstrapping
CSVTokenFileName = "tokens.csv" CSVTokenFileName = "tokens.csv"
) )
var (
AuthorizationPolicyPath = path.Join(KubernetesDir, "abac_policy.json")
AuthorizationWebhookConfigPath = path.Join(KubernetesDir, "webhook_authz.conf")
)

View File

@ -24,6 +24,7 @@ go_library(
"//cmd/kubeadm/app/images:go_default_library", "//cmd/kubeadm/app/images:go_default_library",
"//cmd/kubeadm/app/util:go_default_library", "//cmd/kubeadm/app/util:go_default_library",
"//cmd/kubeadm/app/util/kubeconfig: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", "//pkg/kubectl/cmd/util:go_default_library",
"//vendor:github.com/ghodss/yaml", "//vendor:github.com/ghodss/yaml",
"//vendor:k8s.io/apimachinery/pkg/api/errors", "//vendor:k8s.io/apimachinery/pkg/api/errors",

View File

@ -32,6 +32,7 @@ import (
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/images" "k8s.io/kubernetes/cmd/kubeadm/app/images"
authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" 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", "--requestheader-allowed-names=front-proxy-client",
) )
if cfg.AuthorizationMode != "" { command = append(command, getAuthzParameters(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))
}
}
// Use first address we are given // Use first address we are given
if len(cfg.API.AdvertiseAddresses) > 0 { if len(cfg.API.AdvertiseAddresses) > 0 {
@ -459,3 +452,22 @@ func getSelfHostedAPIServerEnv() []api.EnvVar {
return append(getProxyEnvVars(), podIPEnvVar) 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
}

View File

@ -388,6 +388,7 @@ func TestGetAPIServerCommand(t *testing.T) {
"--requestheader-extra-headers-prefix=X-Remote-Extra-", "--requestheader-extra-headers-prefix=X-Remote-Extra-",
"--requestheader-client-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/front-proxy-ca.crt", "--requestheader-client-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/front-proxy-ca.crt",
"--requestheader-allowed-names=front-proxy-client", "--requestheader-allowed-names=front-proxy-client",
"--authorization-mode=RBAC",
"--etcd-servers=http://127.0.0.1:2379", "--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-extra-headers-prefix=X-Remote-Extra-",
"--requestheader-client-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/front-proxy-ca.crt", "--requestheader-client-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/front-proxy-ca.crt",
"--requestheader-allowed-names=front-proxy-client", "--requestheader-allowed-names=front-proxy-client",
"--authorization-mode=RBAC",
"--advertise-address=foo", "--advertise-address=foo",
"--etcd-servers=http://127.0.0.1:2379", "--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-extra-headers-prefix=X-Remote-Extra-",
"--requestheader-client-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/front-proxy-ca.crt", "--requestheader-client-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/front-proxy-ca.crt",
"--requestheader-allowed-names=front-proxy-client", "--requestheader-allowed-names=front-proxy-client",
"--authorization-mode=RBAC",
"--etcd-servers=http://127.0.0.1:2379", "--etcd-servers=http://127.0.0.1:2379",
"--etcd-certfile=fiz", "--etcd-certfile=fiz",
"--etcd-keyfile=faz", "--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],
)
}
}
}
}

View File

@ -16,6 +16,7 @@ go_library(
"//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library", "//cmd/kubeadm/app/constants:go_default_library",
"//pkg/api/validation:go_default_library", "//pkg/api/validation:go_default_library",
"//pkg/kubeapiserver/authorizer/modes:go_default_library",
"//pkg/util/initsystem:go_default_library", "//pkg/util/initsystem:go_default_library",
"//pkg/util/node:go_default_library", "//pkg/util/node:go_default_library",
"//test/e2e_node/system:go_default_library", "//test/e2e_node/system:go_default_library",

View File

@ -31,6 +31,7 @@ import (
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/pkg/api/validation" "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/initsystem"
"k8s.io/kubernetes/pkg/util/node" "k8s.io/kubernetes/pkg/util/node"
"k8s.io/kubernetes/test/e2e_node/system" "k8s.io/kubernetes/test/e2e_node/system"
@ -363,12 +364,10 @@ func RunInitMasterChecks(cfg *kubeadmapi.MasterConfiguration) error {
// Check the config for authorization mode // Check the config for authorization mode
switch cfg.AuthorizationMode { switch cfg.AuthorizationMode {
case kubeadmconstants.AuthzModeABAC: case authzmodes.ModeABAC:
authorizationPolicyPath := filepath.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, kubeadmconstants.AuthorizationPolicyFile) checks = append(checks, FileExistingCheck{Path: kubeadmconstants.AuthorizationPolicyPath})
checks = append(checks, FileExistingCheck{Path: authorizationPolicyPath}) case authzmodes.ModeWebhook:
case kubeadmconstants.AuthzModeWebhook: checks = append(checks, FileExistingCheck{Path: kubeadmconstants.AuthorizationWebhookConfigPath})
authorizationWebhookConfigPath := filepath.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, kubeadmconstants.AuthorizationWebhookConfigFile)
checks = append(checks, FileExistingCheck{Path: authorizationWebhookConfigPath})
} }
return RunChecks(checks, os.Stderr) return RunChecks(checks, os.Stderr)

View File

@ -16,6 +16,7 @@ go_test(
], ],
library = ":go_default_library", library = ":go_default_library",
tags = ["automanaged"], tags = ["automanaged"],
deps = ["//pkg/kubeapiserver/authorizer/modes:go_default_library"],
) )
go_library( go_library(
@ -25,6 +26,7 @@ go_library(
deps = [ deps = [
"//pkg/auth/authorizer/abac:go_default_library", "//pkg/auth/authorizer/abac:go_default_library",
"//pkg/controller/informers:go_default_library", "//pkg/controller/informers:go_default_library",
"//pkg/kubeapiserver/authorizer/modes:go_default_library",
"//plugin/pkg/auth/authorizer/rbac:go_default_library", "//plugin/pkg/auth/authorizer/rbac:go_default_library",
"//vendor:k8s.io/apiserver/pkg/authorization/authorizer", "//vendor:k8s.io/apiserver/pkg/authorization/authorizer",
"//vendor:k8s.io/apiserver/pkg/authorization/authorizerfactory", "//vendor:k8s.io/apiserver/pkg/authorization/authorizerfactory",
@ -42,6 +44,9 @@ filegroup(
filegroup( filegroup(
name = "all-srcs", name = "all-srcs",
srcs = [":package-srcs"], srcs = [
":package-srcs",
"//pkg/kubeapiserver/authorizer/modes:all-srcs",
],
tags = ["automanaged"], tags = ["automanaged"],
) )

View File

@ -27,17 +27,10 @@ import (
"k8s.io/apiserver/plugin/pkg/authorizer/webhook" "k8s.io/apiserver/plugin/pkg/authorizer/webhook"
"k8s.io/kubernetes/pkg/auth/authorizer/abac" "k8s.io/kubernetes/pkg/auth/authorizer/abac"
"k8s.io/kubernetes/pkg/controller/informers" "k8s.io/kubernetes/pkg/controller/informers"
"k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac" "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 { type AuthorizationConfig struct {
AuthorizationModes []string AuthorizationModes []string
@ -79,11 +72,11 @@ func (config AuthorizationConfig) New() (authorizer.Authorizer, error) {
} }
// Keep cases in sync with constant list above. // Keep cases in sync with constant list above.
switch authorizationMode { switch authorizationMode {
case ModeAlwaysAllow: case modes.ModeAlwaysAllow:
authorizers = append(authorizers, authorizerfactory.NewAlwaysAllowAuthorizer()) authorizers = append(authorizers, authorizerfactory.NewAlwaysAllowAuthorizer())
case ModeAlwaysDeny: case modes.ModeAlwaysDeny:
authorizers = append(authorizers, authorizerfactory.NewAlwaysDenyAuthorizer()) authorizers = append(authorizers, authorizerfactory.NewAlwaysDenyAuthorizer())
case ModeABAC: case modes.ModeABAC:
if config.PolicyFile == "" { if config.PolicyFile == "" {
return nil, errors.New("ABAC's authorization policy file not passed") 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 return nil, err
} }
authorizers = append(authorizers, abacAuthorizer) authorizers = append(authorizers, abacAuthorizer)
case ModeWebhook: case modes.ModeWebhook:
if config.WebhookConfigFile == "" { if config.WebhookConfigFile == "" {
return nil, errors.New("Webhook's configuration file not passed") return nil, errors.New("Webhook's configuration file not passed")
} }
@ -103,7 +96,7 @@ func (config AuthorizationConfig) New() (authorizer.Authorizer, error) {
return nil, err return nil, err
} }
authorizers = append(authorizers, webhookAuthorizer) authorizers = append(authorizers, webhookAuthorizer)
case ModeRBAC: case modes.ModeRBAC:
rbacAuthorizer := rbac.New( rbacAuthorizer := rbac.New(
config.InformerFactory.Roles().Lister(), config.InformerFactory.Roles().Lister(),
config.InformerFactory.RoleBindings().Lister(), config.InformerFactory.RoleBindings().Lister(),
@ -117,13 +110,13 @@ func (config AuthorizationConfig) New() (authorizer.Authorizer, error) {
authorizerMap[authorizationMode] = true 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") 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") 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") return nil, errors.New("Cannot specify --authorization-rbac-super-user without mode RBAC")
} }

View File

@ -17,6 +17,7 @@ limitations under the License.
package authorizer package authorizer
import ( import (
"k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
"testing" "testing"
) )
@ -39,19 +40,19 @@ func TestNew(t *testing.T) {
{ {
// ModeAlwaysAllow and ModeAlwaysDeny should return without authorizationPolicyFile // ModeAlwaysAllow and ModeAlwaysDeny should return without authorizationPolicyFile
// but error if one is given // 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", msg: "returned an error for valid config",
}, },
{ {
// ModeABAC requires a policy file // ModeABAC requires a policy file
config: AuthorizationConfig{AuthorizationModes: []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeABAC}}, config: AuthorizationConfig{AuthorizationModes: []string{modes.ModeAlwaysAllow, modes.ModeAlwaysDeny, modes.ModeABAC}},
wantErr: true, wantErr: true,
msg: "specifying ABAC with no policy file should return an error", msg: "specifying ABAC with no policy file should return an error",
}, },
{ {
// ModeABAC should not error if a valid policy path is provided // ModeABAC should not error if a valid policy path is provided
config: AuthorizationConfig{ config: AuthorizationConfig{
AuthorizationModes: []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeABAC}, AuthorizationModes: []string{modes.ModeAlwaysAllow, modes.ModeAlwaysDeny, modes.ModeABAC},
PolicyFile: examplePolicyFile, PolicyFile: examplePolicyFile,
}, },
msg: "errored while using a valid policy file", 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 // Authorization Policy file cannot be used without ModeABAC
config: AuthorizationConfig{ config: AuthorizationConfig{
AuthorizationModes: []string{ModeAlwaysAllow, ModeAlwaysDeny}, AuthorizationModes: []string{modes.ModeAlwaysAllow, modes.ModeAlwaysDeny},
PolicyFile: examplePolicyFile, PolicyFile: examplePolicyFile,
}, },
wantErr: true, wantErr: true,
@ -74,14 +75,14 @@ func TestNew(t *testing.T) {
}, },
{ {
// ModeWebhook requires at minimum a target. // ModeWebhook requires at minimum a target.
config: AuthorizationConfig{AuthorizationModes: []string{ModeWebhook}}, config: AuthorizationConfig{AuthorizationModes: []string{modes.ModeWebhook}},
wantErr: true, wantErr: true,
msg: "should have errored when config was empty with ModeWebhook", msg: "should have errored when config was empty with ModeWebhook",
}, },
{ {
// Cannot provide webhook flags without ModeWebhook // Cannot provide webhook flags without ModeWebhook
config: AuthorizationConfig{ config: AuthorizationConfig{
AuthorizationModes: []string{ModeAlwaysAllow}, AuthorizationModes: []string{modes.ModeAlwaysAllow},
WebhookConfigFile: "authz_webhook_config.yml", WebhookConfigFile: "authz_webhook_config.yml",
}, },
wantErr: true, wantErr: true,

View File

@ -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"],
)

View File

@ -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
}

View File

@ -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,
)
}
}
}

View File

@ -25,6 +25,7 @@ go_library(
"//pkg/controller/informers:go_default_library", "//pkg/controller/informers:go_default_library",
"//pkg/kubeapiserver/authenticator:go_default_library", "//pkg/kubeapiserver/authenticator:go_default_library",
"//pkg/kubeapiserver/authorizer:go_default_library", "//pkg/kubeapiserver/authorizer:go_default_library",
"//pkg/kubeapiserver/authorizer/modes:go_default_library",
"//vendor:github.com/golang/glog", "//vendor:github.com/golang/glog",
"//vendor:github.com/spf13/pflag", "//vendor:github.com/spf13/pflag",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema", "//vendor:k8s.io/apimachinery/pkg/runtime/schema",

View File

@ -27,7 +27,7 @@ import (
genericapiserver "k8s.io/apiserver/pkg/server" genericapiserver "k8s.io/apiserver/pkg/server"
genericoptions "k8s.io/apiserver/pkg/server/options" genericoptions "k8s.io/apiserver/pkg/server/options"
"k8s.io/kubernetes/pkg/kubeapiserver/authenticator" "k8s.io/kubernetes/pkg/kubeapiserver/authenticator"
"k8s.io/kubernetes/pkg/kubeapiserver/authorizer" authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
) )
type BuiltInAuthenticationOptions struct { type BuiltInAuthenticationOptions struct {
@ -353,7 +353,7 @@ func (o *BuiltInAuthenticationOptions) ApplyAuthorization(authorization *BuiltIn
if o.Anonymous.Allow { if o.Anonymous.Allow {
found := false found := false
for _, mode := range strings.Split(authorization.Mode, ",") { for _, mode := range strings.Split(authorization.Mode, ",") {
if mode == authorizer.ModeAlwaysAllow { if mode == authzmodes.ModeAlwaysAllow {
found = true found = true
break break
} }

View File

@ -24,10 +24,9 @@ import (
"k8s.io/kubernetes/pkg/controller/informers" "k8s.io/kubernetes/pkg/controller/informers"
"k8s.io/kubernetes/pkg/kubeapiserver/authorizer" "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 { type BuiltInAuthorizationOptions struct {
Mode string Mode string
PolicyFile string PolicyFile string
@ -38,7 +37,7 @@ type BuiltInAuthorizationOptions struct {
func NewBuiltInAuthorizationOptions() *BuiltInAuthorizationOptions { func NewBuiltInAuthorizationOptions() *BuiltInAuthorizationOptions {
return &BuiltInAuthorizationOptions{ return &BuiltInAuthorizationOptions{
Mode: authorizer.ModeAlwaysAllow, Mode: authzmodes.ModeAlwaysAllow,
WebhookCacheAuthorizedTTL: 5 * time.Minute, WebhookCacheAuthorizedTTL: 5 * time.Minute,
WebhookCacheUnauthorizedTTL: 30 * time.Second, WebhookCacheUnauthorizedTTL: 30 * time.Second,
} }
@ -52,7 +51,7 @@ func (s *BuiltInAuthorizationOptions) Validate() []error {
func (s *BuiltInAuthorizationOptions) AddFlags(fs *pflag.FlagSet) { func (s *BuiltInAuthorizationOptions) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&s.Mode, "authorization-mode", s.Mode, ""+ fs.StringVar(&s.Mode, "authorization-mode", s.Mode, ""+
"Ordered list of plug-ins to do authorization on secure port. Comma-delimited list of: "+ "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, ""+ 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.") "File with authorization policy in csv format, used with --authorization-mode=ABAC, on the secure port.")