*: add webhook implementation of authorizer.Authorizer plugin

This commit is contained in:
Eric Chiang
2016-02-18 09:26:36 -08:00
parent 6a199706cb
commit 3116346161
9 changed files with 1087 additions and 32 deletions

View File

@@ -23,6 +23,7 @@ import (
"k8s.io/kubernetes/pkg/auth/authorizer"
"k8s.io/kubernetes/pkg/auth/authorizer/abac"
"k8s.io/kubernetes/pkg/auth/authorizer/union"
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/webhook"
)
// Attributes implements authorizer.Attributes interface.
@@ -60,15 +61,28 @@ const (
ModeAlwaysAllow string = "AlwaysAllow"
ModeAlwaysDeny string = "AlwaysDeny"
ModeABAC string = "ABAC"
ModeWebhook string = "Webhook"
)
// Keep this list in sync with constant list above.
var AuthorizationModeChoices = []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeABAC}
var AuthorizationModeChoices = []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeABAC, ModeWebhook}
type AuthorizationConfig struct {
// Options for ModeABAC
// Path to a ABAC policy file.
PolicyFile string
// Options for ModeWebhook
// Kubeconfig file for Webhook authorization plugin.
WebhookConfigFile string
}
// NewAuthorizerFromAuthorizationConfig returns the right sort of union of multiple authorizer.Authorizer objects
// based on the authorizationMode or an error. authorizationMode should be a comma separated values
// of AuthorizationModeChoices.
func NewAuthorizerFromAuthorizationConfig(authorizationModes []string, authorizationPolicyFile string) (authorizer.Authorizer, error) {
func NewAuthorizerFromAuthorizationConfig(authorizationModes []string, config AuthorizationConfig) (authorizer.Authorizer, error) {
if len(authorizationModes) == 0 {
return nil, errors.New("Atleast one authorization mode should be passed")
@@ -88,23 +102,35 @@ func NewAuthorizerFromAuthorizationConfig(authorizationModes []string, authoriza
case ModeAlwaysDeny:
authorizers = append(authorizers, NewAlwaysDenyAuthorizer())
case ModeABAC:
if authorizationPolicyFile == "" {
if config.PolicyFile == "" {
return nil, errors.New("ABAC's authorization policy file not passed")
}
abacAuthorizer, err := abac.NewFromFile(authorizationPolicyFile)
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)
if err != nil {
return nil, err
}
authorizers = append(authorizers, webhookAuthorizer)
default:
return nil, fmt.Errorf("Unknown authorization mode %s specified", authorizationMode)
}
authorizerMap[authorizationMode] = true
}
if !authorizerMap[ModeABAC] && authorizationPolicyFile != "" {
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")
}
return union.New(authorizers...), nil
}

View File

@@ -41,31 +41,75 @@ 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) {
// Unknown modes should return errors
if _, err := NewAuthorizerFromAuthorizationConfig([]string{"DoesNotExist"}, ""); err == nil {
t.Errorf("NewAuthorizerFromAuthorizationConfig using a fake mode should have returned an error")
examplePolicyFile := "../auth/authorizer/abac/example_policy_file.jsonl"
tests := []struct {
modes []string
config AuthorizationConfig
wantErr bool
msg string
}{
{
// Unknown modes should return errors
modes: []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
modes: []string{ModeAlwaysAllow, ModeAlwaysDeny},
msg: "returned an error for valid config",
},
{
// ModeABAC requires a policy file
modes: []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
modes: []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeABAC},
config: AuthorizationConfig{PolicyFile: examplePolicyFile},
msg: "errored while using a valid policy file",
},
{
// Authorization Policy file cannot be used without ModeABAC
modes: []string{ModeAlwaysAllow, ModeAlwaysDeny},
config: AuthorizationConfig{PolicyFile: examplePolicyFile},
wantErr: true,
msg: "should have errored when Authorization Policy File is used without ModeABAC",
},
{
// Atleast one authorizationMode is necessary
modes: []string{},
config: AuthorizationConfig{PolicyFile: examplePolicyFile},
wantErr: true,
msg: "should have errored when no authorization modes are passed",
},
{
// ModeWebhook requires at minimum a target.
modes: []string{ModeWebhook},
wantErr: true,
msg: "should have errored when config was empty with ModeWebhook",
},
{
// Cannot provide webhook flags without ModeWebhook
modes: []string{ModeAlwaysAllow},
config: AuthorizationConfig{WebhookConfigFile: "authz_webhook_config.yml"},
wantErr: true,
msg: "should have errored when Webhook config file is used without ModeWebhook",
},
}
// ModeAlwaysAllow and ModeAlwaysDeny should return without authorizationPolicyFile
// but error if one is given
if _, err := NewAuthorizerFromAuthorizationConfig([]string{ModeAlwaysAllow, ModeAlwaysDeny}, ""); err != nil {
t.Errorf("NewAuthorizerFromAuthorizationConfig returned an error: %s", err)
}
// ModeABAC requires a policy file
if _, err := NewAuthorizerFromAuthorizationConfig([]string{ModeAlwaysAllow, ModeAlwaysDeny, ModeABAC}, ""); err == nil {
t.Errorf("NewAuthorizerFromAuthorizationConfig using a fake mode should have returned an error")
}
// ModeABAC should not error if a valid policy path is provided
if _, err := NewAuthorizerFromAuthorizationConfig([]string{ModeAlwaysAllow, ModeAlwaysDeny, ModeABAC}, "../auth/authorizer/abac/example_policy_file.jsonl"); err != nil {
t.Errorf("NewAuthorizerFromAuthorizationConfig errored while using a valid policy file: %s", err)
}
// Authorization Policy file cannot be used without ModeABAC
if _, err := NewAuthorizerFromAuthorizationConfig([]string{ModeAlwaysAllow, ModeAlwaysDeny}, "../auth/authorizer/abac/example_policy_file.jsonl"); err == nil {
t.Errorf("NewAuthorizerFromAuthorizationConfig should have errored when Authorization Policy File is used without ModeABAC")
}
// Atleast one authorizationMode is necessary
if _, err := NewAuthorizerFromAuthorizationConfig([]string{}, "../auth/authorizer/abac/example_policy_file.jsonl"); err == nil {
t.Errorf("NewAuthorizerFromAuthorizationConfig should have errored when no authorization modes are passed")
for _, tt := range tests {
_, err := NewAuthorizerFromAuthorizationConfig(tt.modes, 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)
}
}
}