diff --git a/pkg/auth/authorizer/abac/BUILD b/pkg/auth/authorizer/abac/BUILD deleted file mode 100644 index 4d55745831..0000000000 --- a/pkg/auth/authorizer/abac/BUILD +++ /dev/null @@ -1,60 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) - -go_library( - name = "go_default_library", - srcs = ["abac.go"], - importpath = "k8s.io/kubernetes/pkg/auth/authorizer/abac", - deps = [ - "//pkg/apis/abac:go_default_library", - "//pkg/apis/abac/latest:go_default_library", - "//pkg/apis/abac/v0:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library", - "//vendor/k8s.io/klog:go_default_library", - ], -) - -filegroup( - name = "example_policy", - testonly = True, - srcs = [ - "example_policy_file.jsonl", - ], -) - -go_test( - name = "go_default_test", - srcs = ["abac_test.go"], - data = [ - ":example_policy", - ], - embed = [":go_default_library"], - deps = [ - "//pkg/apis/abac:go_default_library", - "//pkg/apis/abac/v0:go_default_library", - "//pkg/apis/abac/v1beta1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library", - "//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], -) diff --git a/pkg/auth/authorizer/abac/abac.go b/pkg/auth/authorizer/abac/abac.go deleted file mode 100644 index 8f49c98246..0000000000 --- a/pkg/auth/authorizer/abac/abac.go +++ /dev/null @@ -1,273 +0,0 @@ -/* -Copyright 2014 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 abac - -// Policy authorizes Kubernetes API actions using an Attribute-based access -// control scheme. - -import ( - "bufio" - "fmt" - "os" - "strings" - - "k8s.io/klog" - - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apiserver/pkg/authentication/user" - "k8s.io/apiserver/pkg/authorization/authorizer" - "k8s.io/kubernetes/pkg/apis/abac" - _ "k8s.io/kubernetes/pkg/apis/abac/latest" - "k8s.io/kubernetes/pkg/apis/abac/v0" -) - -type policyLoadError struct { - path string - line int - data []byte - err error -} - -func (p policyLoadError) Error() string { - if p.line >= 0 { - return fmt.Sprintf("error reading policy file %s, line %d: %s: %v", p.path, p.line, string(p.data), p.err) - } - return fmt.Sprintf("error reading policy file %s: %v", p.path, p.err) -} - -type policyList []*abac.Policy - -// TODO: Have policies be created via an API call and stored in REST storage. -func NewFromFile(path string) (policyList, error) { - // File format is one map per line. This allows easy concatenation of files, - // comments in files, and identification of errors by line number. - file, err := os.Open(path) - if err != nil { - return nil, err - } - defer file.Close() - - scanner := bufio.NewScanner(file) - pl := make(policyList, 0) - - decoder := abac.Codecs.UniversalDecoder() - - i := 0 - unversionedLines := 0 - for scanner.Scan() { - i++ - p := &abac.Policy{} - b := scanner.Bytes() - - // skip comment lines and blank lines - trimmed := strings.TrimSpace(string(b)) - if len(trimmed) == 0 || strings.HasPrefix(trimmed, "#") { - continue - } - - decodedObj, _, err := decoder.Decode(b, nil, nil) - if err != nil { - if !(runtime.IsMissingVersion(err) || runtime.IsMissingKind(err) || runtime.IsNotRegisteredError(err)) { - return nil, policyLoadError{path, i, b, err} - } - unversionedLines++ - // Migrate unversioned policy object - oldPolicy := &v0.Policy{} - if err := runtime.DecodeInto(decoder, b, oldPolicy); err != nil { - return nil, policyLoadError{path, i, b, err} - } - if err := abac.Scheme.Convert(oldPolicy, p, nil); err != nil { - return nil, policyLoadError{path, i, b, err} - } - pl = append(pl, p) - continue - } - - decodedPolicy, ok := decodedObj.(*abac.Policy) - if !ok { - return nil, policyLoadError{path, i, b, fmt.Errorf("unrecognized object: %#v", decodedObj)} - } - pl = append(pl, decodedPolicy) - } - - if unversionedLines > 0 { - klog.Warningf("Policy file %s contained unversioned rules. See docs/admin/authorization.md#abac-mode for ABAC file format details.", path) - } - - if err := scanner.Err(); err != nil { - return nil, policyLoadError{path, -1, nil, err} - } - return pl, nil -} - -func matches(p abac.Policy, a authorizer.Attributes) bool { - if subjectMatches(p, a.GetUser()) { - if verbMatches(p, a) { - // Resource and non-resource requests are mutually exclusive, at most one will match a policy - if resourceMatches(p, a) { - return true - } - if nonResourceMatches(p, a) { - return true - } - } - } - return false -} - -// subjectMatches returns true if specified user and group properties in the policy match the attributes -func subjectMatches(p abac.Policy, user user.Info) bool { - matched := false - - if user == nil { - return false - } - username := user.GetName() - groups := user.GetGroups() - - // If the policy specified a user, ensure it matches - if len(p.Spec.User) > 0 { - if p.Spec.User == "*" { - matched = true - } else { - matched = p.Spec.User == username - if !matched { - return false - } - } - } - - // If the policy specified a group, ensure it matches - if len(p.Spec.Group) > 0 { - if p.Spec.Group == "*" { - matched = true - } else { - matched = false - for _, group := range groups { - if p.Spec.Group == group { - matched = true - } - } - if !matched { - return false - } - } - } - - return matched -} - -func verbMatches(p abac.Policy, a authorizer.Attributes) bool { - // TODO: match on verb - - // All policies allow read only requests - if a.IsReadOnly() { - return true - } - - // Allow if policy is not readonly - if !p.Spec.Readonly { - return true - } - - return false -} - -func nonResourceMatches(p abac.Policy, a authorizer.Attributes) bool { - // A non-resource policy cannot match a resource request - if !a.IsResourceRequest() { - // Allow wildcard match - if p.Spec.NonResourcePath == "*" { - return true - } - // Allow exact match - if p.Spec.NonResourcePath == a.GetPath() { - return true - } - // Allow a trailing * subpath match - if strings.HasSuffix(p.Spec.NonResourcePath, "*") && strings.HasPrefix(a.GetPath(), strings.TrimRight(p.Spec.NonResourcePath, "*")) { - return true - } - } - return false -} - -func resourceMatches(p abac.Policy, a authorizer.Attributes) bool { - // A resource policy cannot match a non-resource request - if a.IsResourceRequest() { - if p.Spec.Namespace == "*" || p.Spec.Namespace == a.GetNamespace() { - if p.Spec.Resource == "*" || p.Spec.Resource == a.GetResource() { - if p.Spec.APIGroup == "*" || p.Spec.APIGroup == a.GetAPIGroup() { - return true - } - } - } - } - return false -} - -// Authorizer implements authorizer.Authorize -func (pl policyList) Authorize(a authorizer.Attributes) (authorizer.Decision, string, error) { - for _, p := range pl { - if matches(*p, a) { - return authorizer.DecisionAllow, "", nil - } - } - return authorizer.DecisionNoOpinion, "No policy matched.", nil - // TODO: Benchmark how much time policy matching takes with a medium size - // policy file, compared to other steps such as encoding/decoding. - // Then, add Caching only if needed. -} - -func (pl policyList) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) { - var ( - resourceRules []authorizer.ResourceRuleInfo - nonResourceRules []authorizer.NonResourceRuleInfo - ) - - for _, p := range pl { - if subjectMatches(*p, user) { - if p.Spec.Namespace == "*" || p.Spec.Namespace == namespace { - if len(p.Spec.Resource) > 0 { - r := authorizer.DefaultResourceRuleInfo{ - Verbs: getVerbs(p.Spec.Readonly), - APIGroups: []string{p.Spec.APIGroup}, - Resources: []string{p.Spec.Resource}, - } - var resourceRule authorizer.ResourceRuleInfo = &r - resourceRules = append(resourceRules, resourceRule) - } - if len(p.Spec.NonResourcePath) > 0 { - r := authorizer.DefaultNonResourceRuleInfo{ - Verbs: getVerbs(p.Spec.Readonly), - NonResourceURLs: []string{p.Spec.NonResourcePath}, - } - var nonResourceRule authorizer.NonResourceRuleInfo = &r - nonResourceRules = append(nonResourceRules, nonResourceRule) - } - } - } - } - return resourceRules, nonResourceRules, false, nil -} - -func getVerbs(isReadOnly bool) []string { - if isReadOnly { - return []string{"get", "list", "watch"} - } - return []string{"*"} -} diff --git a/pkg/auth/authorizer/abac/abac_test.go b/pkg/auth/authorizer/abac/abac_test.go deleted file mode 100644 index 6d73239920..0000000000 --- a/pkg/auth/authorizer/abac/abac_test.go +++ /dev/null @@ -1,1268 +0,0 @@ -/* -Copyright 2014 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 abac - -import ( - "io/ioutil" - "os" - "reflect" - "testing" - - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apiserver/pkg/authentication/user" - "k8s.io/apiserver/pkg/authorization/authorizer" - "k8s.io/kubernetes/pkg/apis/abac" - "k8s.io/kubernetes/pkg/apis/abac/v0" - "k8s.io/kubernetes/pkg/apis/abac/v1beta1" -) - -func TestEmptyFile(t *testing.T) { - _, err := newWithContents(t, "") - if err != nil { - t.Errorf("unable to read policy file: %v", err) - } -} - -func TestOneLineFileNoNewLine(t *testing.T) { - _, err := newWithContents(t, `{"user":"scheduler", "readonly": true, "resource": "pods", "namespace":"ns1"}`) - if err != nil { - t.Errorf("unable to read policy file: %v", err) - } -} - -func TestTwoLineFile(t *testing.T) { - _, err := newWithContents(t, `{"user":"scheduler", "readonly": true, "resource": "pods"} -{"user":"scheduler", "readonly": true, "resource": "services"} -`) - if err != nil { - t.Errorf("unable to read policy file: %v", err) - } -} - -// Test the file that we will point users at as an example. -func TestExampleFile(t *testing.T) { - _, err := NewFromFile("./example_policy_file.jsonl") - if err != nil { - t.Errorf("unable to read policy file: %v", err) - } -} - -func TestAuthorizeV0(t *testing.T) { - a, err := newWithContents(t, `{ "readonly": true, "resource": "events" } -{"user":"scheduler", "readonly": true, "resource": "pods" } -{"user":"scheduler", "resource": "bindings" } -{"user":"kubelet", "readonly": true, "resource": "bindings" } -{"user":"kubelet", "resource": "events" } -{"user":"alice", "namespace": "projectCaribou"} -{"user":"bob", "readonly": true, "namespace": "projectCaribou"} -`) - if err != nil { - t.Fatalf("unable to read policy file: %v", err) - } - - authenticatedGroup := []string{user.AllAuthenticated} - - uScheduler := user.DefaultInfo{Name: "scheduler", UID: "uid1", Groups: authenticatedGroup} - uAlice := user.DefaultInfo{Name: "alice", UID: "uid3", Groups: authenticatedGroup} - uChuck := user.DefaultInfo{Name: "chuck", UID: "uid5", Groups: authenticatedGroup} - - testCases := []struct { - User user.DefaultInfo - Verb string - Resource string - NS string - APIGroup string - Path string - ExpectDecision authorizer.Decision - }{ - // Scheduler can read pods - {User: uScheduler, Verb: "list", Resource: "pods", NS: "ns1", ExpectDecision: authorizer.DecisionAllow}, - {User: uScheduler, Verb: "list", Resource: "pods", NS: "", ExpectDecision: authorizer.DecisionAllow}, - // Scheduler cannot write pods - {User: uScheduler, Verb: "create", Resource: "pods", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion}, - {User: uScheduler, Verb: "create", Resource: "pods", NS: "", ExpectDecision: authorizer.DecisionNoOpinion}, - // Scheduler can write bindings - {User: uScheduler, Verb: "get", Resource: "bindings", NS: "ns1", ExpectDecision: authorizer.DecisionAllow}, - {User: uScheduler, Verb: "get", Resource: "bindings", NS: "", ExpectDecision: authorizer.DecisionAllow}, - - // Alice can read and write anything in the right namespace. - {User: uAlice, Verb: "get", Resource: "pods", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow}, - {User: uAlice, Verb: "get", Resource: "widgets", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow}, - {User: uAlice, Verb: "get", Resource: "", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow}, - {User: uAlice, Verb: "update", Resource: "pods", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow}, - {User: uAlice, Verb: "update", Resource: "widgets", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow}, - {User: uAlice, Verb: "update", Resource: "", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow}, - {User: uAlice, Verb: "update", Resource: "foo", NS: "projectCaribou", APIGroup: "bar", ExpectDecision: authorizer.DecisionAllow}, - // .. but not the wrong namespace. - {User: uAlice, Verb: "get", Resource: "pods", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion}, - {User: uAlice, Verb: "get", Resource: "widgets", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion}, - {User: uAlice, Verb: "get", Resource: "", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion}, - - // Chuck can read events, since anyone can. - {User: uChuck, Verb: "get", Resource: "events", NS: "ns1", ExpectDecision: authorizer.DecisionAllow}, - {User: uChuck, Verb: "get", Resource: "events", NS: "", ExpectDecision: authorizer.DecisionAllow}, - // Chuck can't do other things. - {User: uChuck, Verb: "update", Resource: "events", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion}, - {User: uChuck, Verb: "get", Resource: "pods", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion}, - {User: uChuck, Verb: "get", Resource: "floop", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion}, - // Chunk can't access things with no kind or namespace - {User: uChuck, Verb: "get", Path: "/", Resource: "", NS: "", ExpectDecision: authorizer.DecisionNoOpinion}, - } - for i, tc := range testCases { - attr := authorizer.AttributesRecord{ - User: &tc.User, - Verb: tc.Verb, - Resource: tc.Resource, - Namespace: tc.NS, - APIGroup: tc.APIGroup, - Path: tc.Path, - - ResourceRequest: len(tc.NS) > 0 || len(tc.Resource) > 0, - } - decision, _, _ := a.Authorize(attr) - if tc.ExpectDecision != decision { - t.Logf("tc: %v -> attr %v", tc, attr) - t.Errorf("%d: Expected allowed=%v but actually allowed=%v\n\t%v", - i, tc.ExpectDecision, decision, tc) - } - } -} - -func getResourceRules(infos []authorizer.ResourceRuleInfo) []authorizer.DefaultResourceRuleInfo { - rules := make([]authorizer.DefaultResourceRuleInfo, len(infos)) - for i, info := range infos { - rules[i] = authorizer.DefaultResourceRuleInfo{ - Verbs: info.GetVerbs(), - APIGroups: info.GetAPIGroups(), - Resources: info.GetResources(), - ResourceNames: info.GetResourceNames(), - } - } - return rules -} - -func getNonResourceRules(infos []authorizer.NonResourceRuleInfo) []authorizer.DefaultNonResourceRuleInfo { - rules := make([]authorizer.DefaultNonResourceRuleInfo, len(infos)) - for i, info := range infos { - rules[i] = authorizer.DefaultNonResourceRuleInfo{ - Verbs: info.GetVerbs(), - NonResourceURLs: info.GetNonResourceURLs(), - } - } - return rules -} - -func TestRulesFor(t *testing.T) { - a, err := newWithContents(t, ` -{ "readonly": true, "resource": "events" } -{"user":"scheduler", "readonly": true, "resource": "pods" } -{"user":"scheduler", "resource": "bindings" } -{"user":"kubelet", "readonly": true, "resource": "pods" } -{"user":"kubelet", "resource": "events" } -{"user":"alice", "namespace": "projectCaribou"} -{"user":"bob", "readonly": true, "namespace": "projectCaribou"} -{"user":"bob", "readonly": true, "nonResourcePath": "*"} -{"group":"a", "resource": "bindings" } -{"group":"b", "readonly": true, "nonResourcePath": "*"} -`) - if err != nil { - t.Fatalf("unable to read policy file: %v", err) - } - - authenticatedGroup := []string{user.AllAuthenticated} - - uScheduler := user.DefaultInfo{Name: "scheduler", UID: "uid1", Groups: authenticatedGroup} - uKubelet := user.DefaultInfo{Name: "kubelet", UID: "uid2", Groups: []string{"a", "b"}} - uAlice := user.DefaultInfo{Name: "alice", UID: "uid3", Groups: authenticatedGroup} - uBob := user.DefaultInfo{Name: "bob", UID: "uid4", Groups: authenticatedGroup} - uChuck := user.DefaultInfo{Name: "chuck", UID: "uid5", Groups: []string{"a", "b"}} - - testCases := []struct { - User user.DefaultInfo - Namespace string - ExpectResourceRules []authorizer.DefaultResourceRuleInfo - ExpectNonResourceRules []authorizer.DefaultNonResourceRuleInfo - }{ - { - User: uScheduler, - Namespace: "ns1", - ExpectResourceRules: []authorizer.DefaultResourceRuleInfo{ - { - Verbs: []string{"get", "list", "watch"}, - APIGroups: []string{"*"}, - Resources: []string{"events"}, - }, - { - Verbs: []string{"get", "list", "watch"}, - APIGroups: []string{"*"}, - Resources: []string{"pods"}, - }, - { - Verbs: []string{"*"}, - APIGroups: []string{"*"}, - Resources: []string{"bindings"}, - }, - }, - ExpectNonResourceRules: []authorizer.DefaultNonResourceRuleInfo{}, - }, - { - User: uKubelet, - Namespace: "ns1", - ExpectResourceRules: []authorizer.DefaultResourceRuleInfo{ - { - Verbs: []string{"get", "list", "watch"}, - APIGroups: []string{"*"}, - Resources: []string{"pods"}, - }, - { - Verbs: []string{"*"}, - APIGroups: []string{"*"}, - Resources: []string{"events"}, - }, - { - Verbs: []string{"*"}, - APIGroups: []string{"*"}, - Resources: []string{"bindings"}, - }, - { - Verbs: []string{"get", "list", "watch"}, - APIGroups: []string{"*"}, - Resources: []string{"*"}, - }, - }, - ExpectNonResourceRules: []authorizer.DefaultNonResourceRuleInfo{ - { - Verbs: []string{"get", "list", "watch"}, - NonResourceURLs: []string{"*"}, - }, - }, - }, - { - User: uAlice, - Namespace: "projectCaribou", - ExpectResourceRules: []authorizer.DefaultResourceRuleInfo{ - { - Verbs: []string{"get", "list", "watch"}, - APIGroups: []string{"*"}, - Resources: []string{"events"}, - }, - { - Verbs: []string{"*"}, - APIGroups: []string{"*"}, - Resources: []string{"*"}, - }, - }, - ExpectNonResourceRules: []authorizer.DefaultNonResourceRuleInfo{}, - }, - { - User: uBob, - Namespace: "projectCaribou", - ExpectResourceRules: []authorizer.DefaultResourceRuleInfo{ - { - Verbs: []string{"get", "list", "watch"}, - APIGroups: []string{"*"}, - Resources: []string{"events"}, - }, - { - Verbs: []string{"get", "list", "watch"}, - APIGroups: []string{"*"}, - Resources: []string{"*"}, - }, - { - Verbs: []string{"get", "list", "watch"}, - APIGroups: []string{"*"}, - Resources: []string{"*"}, - }, - }, - ExpectNonResourceRules: []authorizer.DefaultNonResourceRuleInfo{ - { - Verbs: []string{"get", "list", "watch"}, - NonResourceURLs: []string{"*"}, - }, - }, - }, - { - User: uChuck, - Namespace: "ns1", - ExpectResourceRules: []authorizer.DefaultResourceRuleInfo{ - { - Verbs: []string{"*"}, - APIGroups: []string{"*"}, - Resources: []string{"bindings"}, - }, - { - Verbs: []string{"get", "list", "watch"}, - APIGroups: []string{"*"}, - Resources: []string{"*"}, - }, - }, - ExpectNonResourceRules: []authorizer.DefaultNonResourceRuleInfo{ - { - Verbs: []string{"get", "list", "watch"}, - NonResourceURLs: []string{"*"}, - }, - }, - }, - } - for i, tc := range testCases { - attr := authorizer.AttributesRecord{ - User: &tc.User, - Namespace: tc.Namespace, - } - resourceRules, nonResourceRules, _, _ := a.RulesFor(attr.GetUser(), attr.GetNamespace()) - actualResourceRules := getResourceRules(resourceRules) - if !reflect.DeepEqual(tc.ExpectResourceRules, actualResourceRules) { - t.Logf("tc: %v -> attr %v", tc, attr) - t.Errorf("%d: Expected: \n%#v\n but actual: \n%#v\n", - i, tc.ExpectResourceRules, actualResourceRules) - } - actualNonResourceRules := getNonResourceRules(nonResourceRules) - if !reflect.DeepEqual(tc.ExpectNonResourceRules, actualNonResourceRules) { - t.Logf("tc: %v -> attr %v", tc, attr) - t.Errorf("%d: Expected: \n%#v\n but actual: \n%#v\n", - i, tc.ExpectNonResourceRules, actualNonResourceRules) - } - } -} - -func TestAuthorizeV1beta1(t *testing.T) { - a, err := newWithContents(t, - ` - # Comment line, after a blank line - {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"*", "readonly": true, "nonResourcePath": "/api"}} - {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"*", "nonResourcePath": "/custom"}} - {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"*", "nonResourcePath": "/root/*"}} - {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"noresource", "nonResourcePath": "*"}} - {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"*", "readonly": true, "resource": "events", "namespace": "*"}} - {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"scheduler", "readonly": true, "resource": "pods", "namespace": "*"}} - {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"scheduler", "resource": "bindings", "namespace": "*"}} - {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"kubelet", "readonly": true, "resource": "bindings", "namespace": "*"}} - {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"kubelet", "resource": "events", "namespace": "*"}} - {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"alice", "resource": "*", "namespace": "projectCaribou"}} - {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"bob", "readonly": true, "resource": "*", "namespace": "projectCaribou"}} - {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"debbie", "resource": "pods", "namespace": "projectCaribou"}} - {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"apigroupuser", "resource": "*", "namespace": "projectAnyGroup", "apiGroup": "*"}} - {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"apigroupuser", "resource": "*", "namespace": "projectEmptyGroup", "apiGroup": "" }} - {"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec":{"user":"apigroupuser", "resource": "*", "namespace": "projectXGroup", "apiGroup": "x"}}`) - - if err != nil { - t.Fatalf("unable to read policy file: %v", err) - } - - authenticatedGroup := []string{user.AllAuthenticated} - - uScheduler := user.DefaultInfo{Name: "scheduler", UID: "uid1", Groups: authenticatedGroup} - uAlice := user.DefaultInfo{Name: "alice", UID: "uid3", Groups: authenticatedGroup} - uChuck := user.DefaultInfo{Name: "chuck", UID: "uid5", Groups: authenticatedGroup} - uDebbie := user.DefaultInfo{Name: "debbie", UID: "uid6", Groups: authenticatedGroup} - uNoResource := user.DefaultInfo{Name: "noresource", UID: "uid7", Groups: authenticatedGroup} - uAPIGroup := user.DefaultInfo{Name: "apigroupuser", UID: "uid8", Groups: authenticatedGroup} - - testCases := []struct { - User user.DefaultInfo - Verb string - Resource string - APIGroup string - NS string - Path string - ExpectDecision authorizer.Decision - }{ - // Scheduler can read pods - {User: uScheduler, Verb: "list", Resource: "pods", NS: "ns1", ExpectDecision: authorizer.DecisionAllow}, - {User: uScheduler, Verb: "list", Resource: "pods", NS: "", ExpectDecision: authorizer.DecisionAllow}, - // Scheduler cannot write pods - {User: uScheduler, Verb: "create", Resource: "pods", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion}, - {User: uScheduler, Verb: "create", Resource: "pods", NS: "", ExpectDecision: authorizer.DecisionNoOpinion}, - // Scheduler can write bindings - {User: uScheduler, Verb: "get", Resource: "bindings", NS: "ns1", ExpectDecision: authorizer.DecisionAllow}, - {User: uScheduler, Verb: "get", Resource: "bindings", NS: "", ExpectDecision: authorizer.DecisionAllow}, - - // Alice can read and write anything in the right namespace. - {User: uAlice, Verb: "get", Resource: "pods", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow}, - {User: uAlice, Verb: "get", Resource: "widgets", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow}, - {User: uAlice, Verb: "get", Resource: "", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow}, - {User: uAlice, Verb: "update", Resource: "pods", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow}, - {User: uAlice, Verb: "update", Resource: "widgets", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow}, - {User: uAlice, Verb: "update", Resource: "", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow}, - // .. but not the wrong namespace. - {User: uAlice, Verb: "get", Resource: "pods", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion}, - {User: uAlice, Verb: "get", Resource: "widgets", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion}, - {User: uAlice, Verb: "get", Resource: "", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion}, - - // Debbie can write to pods in the right namespace - {User: uDebbie, Verb: "update", Resource: "pods", NS: "projectCaribou", ExpectDecision: authorizer.DecisionAllow}, - - // Chuck can read events, since anyone can. - {User: uChuck, Verb: "get", Resource: "events", NS: "ns1", ExpectDecision: authorizer.DecisionAllow}, - {User: uChuck, Verb: "get", Resource: "events", NS: "", ExpectDecision: authorizer.DecisionAllow}, - // Chuck can't do other things. - {User: uChuck, Verb: "update", Resource: "events", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion}, - {User: uChuck, Verb: "get", Resource: "pods", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion}, - {User: uChuck, Verb: "get", Resource: "floop", NS: "ns1", ExpectDecision: authorizer.DecisionNoOpinion}, - // Chuck can't access things with no resource or namespace - {User: uChuck, Verb: "get", Path: "/", Resource: "", NS: "", ExpectDecision: authorizer.DecisionNoOpinion}, - // but can access /api - {User: uChuck, Verb: "get", Path: "/api", Resource: "", NS: "", ExpectDecision: authorizer.DecisionAllow}, - // though he cannot write to it - {User: uChuck, Verb: "create", Path: "/api", Resource: "", NS: "", ExpectDecision: authorizer.DecisionNoOpinion}, - // while he can write to /custom - {User: uChuck, Verb: "update", Path: "/custom", Resource: "", NS: "", ExpectDecision: authorizer.DecisionAllow}, - // he cannot get "/root" - {User: uChuck, Verb: "get", Path: "/root", Resource: "", NS: "", ExpectDecision: authorizer.DecisionNoOpinion}, - // but can get any subpath - {User: uChuck, Verb: "get", Path: "/root/", Resource: "", NS: "", ExpectDecision: authorizer.DecisionAllow}, - {User: uChuck, Verb: "get", Path: "/root/test/1/2/3", Resource: "", NS: "", ExpectDecision: authorizer.DecisionAllow}, - - // the user "noresource" can get any non-resource request - {User: uNoResource, Verb: "get", Path: "", Resource: "", NS: "", ExpectDecision: authorizer.DecisionAllow}, - {User: uNoResource, Verb: "get", Path: "/", Resource: "", NS: "", ExpectDecision: authorizer.DecisionAllow}, - {User: uNoResource, Verb: "get", Path: "/foo/bar/baz", Resource: "", NS: "", ExpectDecision: authorizer.DecisionAllow}, - // but cannot get any request where IsResourceRequest() == true - {User: uNoResource, Verb: "get", Path: "/", Resource: "", NS: "bar", ExpectDecision: authorizer.DecisionNoOpinion}, - {User: uNoResource, Verb: "get", Path: "/foo/bar/baz", Resource: "foo", NS: "bar", ExpectDecision: authorizer.DecisionNoOpinion}, - - // Test APIGroup matching - {User: uAPIGroup, Verb: "get", APIGroup: "x", Resource: "foo", NS: "projectAnyGroup", ExpectDecision: authorizer.DecisionAllow}, - {User: uAPIGroup, Verb: "get", APIGroup: "x", Resource: "foo", NS: "projectEmptyGroup", ExpectDecision: authorizer.DecisionNoOpinion}, - {User: uAPIGroup, Verb: "get", APIGroup: "x", Resource: "foo", NS: "projectXGroup", ExpectDecision: authorizer.DecisionAllow}, - } - for i, tc := range testCases { - attr := authorizer.AttributesRecord{ - User: &tc.User, - Verb: tc.Verb, - Resource: tc.Resource, - APIGroup: tc.APIGroup, - Namespace: tc.NS, - ResourceRequest: len(tc.NS) > 0 || len(tc.Resource) > 0, - Path: tc.Path, - } - // t.Logf("tc %2v: %v -> attr %v", i, tc, attr) - decision, _, _ := a.Authorize(attr) - if tc.ExpectDecision != decision { - t.Errorf("%d: Expected allowed=%v but actually allowed=%v, for case %+v & %+v", - i, tc.ExpectDecision, decision, tc, attr) - } - } -} - -func TestSubjectMatches(t *testing.T) { - testCases := map[string]struct { - User user.DefaultInfo - Policy runtime.Object - ExpectMatch bool - }{ - "v0 empty policy does not match unauthed user": { - User: user.DefaultInfo{Name: "system:anonymous", Groups: []string{"system:unauthenticated"}}, - Policy: &v0.Policy{ - User: "", - Group: "", - }, - ExpectMatch: false, - }, - "v0 * user policy does not match unauthed user": { - User: user.DefaultInfo{Name: "system:anonymous", Groups: []string{"system:unauthenticated"}}, - Policy: &v0.Policy{ - User: "*", - Group: "", - }, - ExpectMatch: false, - }, - "v0 * group policy does not match unauthed user": { - User: user.DefaultInfo{Name: "system:anonymous", Groups: []string{"system:unauthenticated"}}, - Policy: &v0.Policy{ - User: "", - Group: "*", - }, - ExpectMatch: false, - }, - "v0 empty policy matches authed user": { - User: user.DefaultInfo{Name: "Foo", Groups: []string{user.AllAuthenticated}}, - Policy: &v0.Policy{ - User: "", - Group: "", - }, - ExpectMatch: true, - }, - "v0 empty policy matches authed user with groups": { - User: user.DefaultInfo{Name: "Foo", Groups: []string{"a", "b", user.AllAuthenticated}}, - Policy: &v0.Policy{ - User: "", - Group: "", - }, - ExpectMatch: true, - }, - - "v0 user policy does not match unauthed user": { - User: user.DefaultInfo{Name: "system:anonymous", Groups: []string{"system:unauthenticated"}}, - Policy: &v0.Policy{ - User: "Foo", - Group: "", - }, - ExpectMatch: false, - }, - "v0 user policy does not match different user": { - User: user.DefaultInfo{Name: "Bar", Groups: []string{user.AllAuthenticated}}, - Policy: &v0.Policy{ - User: "Foo", - Group: "", - }, - ExpectMatch: false, - }, - "v0 user policy is case-sensitive": { - User: user.DefaultInfo{Name: "foo", Groups: []string{user.AllAuthenticated}}, - Policy: &v0.Policy{ - User: "Foo", - Group: "", - }, - ExpectMatch: false, - }, - "v0 user policy does not match substring": { - User: user.DefaultInfo{Name: "FooBar", Groups: []string{user.AllAuthenticated}}, - Policy: &v0.Policy{ - User: "Foo", - Group: "", - }, - ExpectMatch: false, - }, - "v0 user policy matches username": { - User: user.DefaultInfo{Name: "Foo", Groups: []string{user.AllAuthenticated}}, - Policy: &v0.Policy{ - User: "Foo", - Group: "", - }, - ExpectMatch: true, - }, - - "v0 group policy does not match unauthed user": { - User: user.DefaultInfo{Name: "system:anonymous", Groups: []string{"system:unauthenticated"}}, - Policy: &v0.Policy{ - User: "", - Group: "Foo", - }, - ExpectMatch: false, - }, - "v0 group policy does not match user in different group": { - User: user.DefaultInfo{Name: "FooBar", Groups: []string{"B", user.AllAuthenticated}}, - Policy: &v0.Policy{ - User: "", - Group: "A", - }, - ExpectMatch: false, - }, - "v0 group policy is case-sensitive": { - User: user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C", user.AllAuthenticated}}, - Policy: &v0.Policy{ - User: "", - Group: "b", - }, - ExpectMatch: false, - }, - "v0 group policy does not match substring": { - User: user.DefaultInfo{Name: "Foo", Groups: []string{"A", "BBB", "C", user.AllAuthenticated}}, - Policy: &v0.Policy{ - User: "", - Group: "B", - }, - ExpectMatch: false, - }, - "v0 group policy matches user in group": { - User: user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C", user.AllAuthenticated}}, - Policy: &v0.Policy{ - User: "", - Group: "B", - }, - ExpectMatch: true, - }, - - "v0 user and group policy requires user match": { - User: user.DefaultInfo{Name: "Bar", Groups: []string{"A", "B", "C", user.AllAuthenticated}}, - Policy: &v0.Policy{ - User: "Foo", - Group: "B", - }, - ExpectMatch: false, - }, - "v0 user and group policy requires group match": { - User: user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C", user.AllAuthenticated}}, - Policy: &v0.Policy{ - User: "Foo", - Group: "D", - }, - ExpectMatch: false, - }, - "v0 user and group policy matches": { - User: user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C", user.AllAuthenticated}}, - Policy: &v0.Policy{ - User: "Foo", - Group: "B", - }, - ExpectMatch: true, - }, - - "v1 empty policy does not match unauthed user": { - User: user.DefaultInfo{Name: "system:anonymous", Groups: []string{"system:unauthenticated"}}, - Policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "", - Group: "", - }, - }, - ExpectMatch: false, - }, - "v1 * user policy does not match unauthed user": { - User: user.DefaultInfo{Name: "system:anonymous", Groups: []string{"system:unauthenticated"}}, - Policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "*", - Group: "", - }, - }, - ExpectMatch: false, - }, - "v1 * group policy does not match unauthed user": { - User: user.DefaultInfo{Name: "system:anonymous", Groups: []string{"system:unauthenticated"}}, - Policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "", - Group: "*", - }, - }, - ExpectMatch: false, - }, - "v1 empty policy does not match authed user": { - User: user.DefaultInfo{Name: "Foo", Groups: []string{user.AllAuthenticated}}, - Policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "", - Group: "", - }, - }, - ExpectMatch: false, - }, - "v1 empty policy does not match authed user with groups": { - User: user.DefaultInfo{Name: "Foo", Groups: []string{"a", "b", user.AllAuthenticated}}, - Policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "", - Group: "", - }, - }, - ExpectMatch: false, - }, - - "v1 user policy does not match unauthed user": { - User: user.DefaultInfo{Name: "system:anonymous", Groups: []string{"system:unauthenticated"}}, - Policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "Foo", - Group: "", - }, - }, - ExpectMatch: false, - }, - "v1 user policy does not match different user": { - User: user.DefaultInfo{Name: "Bar", Groups: []string{user.AllAuthenticated}}, - Policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "Foo", - Group: "", - }, - }, - ExpectMatch: false, - }, - "v1 user policy is case-sensitive": { - User: user.DefaultInfo{Name: "foo", Groups: []string{user.AllAuthenticated}}, - Policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "Foo", - Group: "", - }, - }, - ExpectMatch: false, - }, - "v1 user policy does not match substring": { - User: user.DefaultInfo{Name: "FooBar", Groups: []string{user.AllAuthenticated}}, - Policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "Foo", - Group: "", - }, - }, - ExpectMatch: false, - }, - "v1 user policy matches username": { - User: user.DefaultInfo{Name: "Foo", Groups: []string{user.AllAuthenticated}}, - Policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "Foo", - Group: "", - }, - }, - ExpectMatch: true, - }, - - "v1 group policy does not match unauthed user": { - User: user.DefaultInfo{Name: "system:anonymous", Groups: []string{"system:unauthenticated"}}, - Policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "", - Group: "Foo", - }, - }, - ExpectMatch: false, - }, - "v1 group policy does not match user in different group": { - User: user.DefaultInfo{Name: "FooBar", Groups: []string{"B", user.AllAuthenticated}}, - Policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "", - Group: "A", - }, - }, - ExpectMatch: false, - }, - "v1 group policy is case-sensitive": { - User: user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C", user.AllAuthenticated}}, - Policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "", - Group: "b", - }, - }, - ExpectMatch: false, - }, - "v1 group policy does not match substring": { - User: user.DefaultInfo{Name: "Foo", Groups: []string{"A", "BBB", "C", user.AllAuthenticated}}, - Policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "", - Group: "B", - }, - }, - ExpectMatch: false, - }, - "v1 group policy matches user in group": { - User: user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C", user.AllAuthenticated}}, - Policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "", - Group: "B", - }, - }, - ExpectMatch: true, - }, - - "v1 user and group policy requires user match": { - User: user.DefaultInfo{Name: "Bar", Groups: []string{"A", "B", "C", user.AllAuthenticated}}, - Policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "Foo", - Group: "B", - }, - }, - ExpectMatch: false, - }, - "v1 user and group policy requires group match": { - User: user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C", user.AllAuthenticated}}, - Policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "Foo", - Group: "D", - }, - }, - ExpectMatch: false, - }, - "v1 user and group policy matches": { - User: user.DefaultInfo{Name: "Foo", Groups: []string{"A", "B", "C", user.AllAuthenticated}}, - Policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "Foo", - Group: "B", - }, - }, - ExpectMatch: true, - }, - } - - for k, tc := range testCases { - policy := &abac.Policy{} - if err := abac.Scheme.Convert(tc.Policy, policy, nil); err != nil { - t.Errorf("%s: error converting: %v", k, err) - continue - } - attr := authorizer.AttributesRecord{ - User: &tc.User, - } - actualMatch := subjectMatches(*policy, attr.GetUser()) - if tc.ExpectMatch != actualMatch { - t.Errorf("%v: Expected actorMatches=%v but actually got=%v", - k, tc.ExpectMatch, actualMatch) - } - } -} - -func newWithContents(t *testing.T, contents string) (policyList, error) { - f, err := ioutil.TempFile("", "abac_test") - if err != nil { - t.Fatalf("unexpected error creating policyfile: %v", err) - } - f.Close() - defer os.Remove(f.Name()) - - if err := ioutil.WriteFile(f.Name(), []byte(contents), 0700); err != nil { - t.Fatalf("unexpected error writing policyfile: %v", err) - } - - pl, err := NewFromFile(f.Name()) - return pl, err -} - -func TestPolicy(t *testing.T) { - tests := []struct { - policy runtime.Object - attr authorizer.Attributes - matches bool - name string - }{ - // v0 mismatches - { - policy: &v0.Policy{ - Readonly: true, - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{user.AllAuthenticated}, - }, - Verb: "create", - }, - matches: false, - name: "v0 read-only mismatch", - }, - { - policy: &v0.Policy{ - User: "foo", - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "bar", - Groups: []string{user.AllAuthenticated}, - }, - }, - matches: false, - name: "v0 user name mis-match", - }, - { - policy: &v0.Policy{ - Resource: "foo", - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{user.AllAuthenticated}, - }, - Resource: "bar", - ResourceRequest: true, - }, - matches: false, - name: "v0 resource mis-match", - }, - { - policy: &v0.Policy{ - User: "foo", - Resource: "foo", - Namespace: "foo", - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{user.AllAuthenticated}, - }, - Resource: "foo", - Namespace: "foo", - ResourceRequest: true, - }, - matches: true, - name: "v0 namespace mis-match", - }, - - // v0 matches - { - policy: &v0.Policy{}, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{user.AllAuthenticated}, - }, - ResourceRequest: true, - }, - matches: true, - name: "v0 null resource", - }, - { - policy: &v0.Policy{ - Readonly: true, - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{user.AllAuthenticated}, - }, - Verb: "get", - }, - matches: true, - name: "v0 read-only match", - }, - { - policy: &v0.Policy{ - User: "foo", - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{user.AllAuthenticated}, - }, - }, - matches: true, - name: "v0 user name match", - }, - { - policy: &v0.Policy{ - Resource: "foo", - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{user.AllAuthenticated}, - }, - Resource: "foo", - ResourceRequest: true, - }, - matches: true, - name: "v0 resource match", - }, - - // v1 mismatches - { - policy: &v1beta1.Policy{}, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{user.AllAuthenticated}, - }, - ResourceRequest: true, - }, - matches: false, - name: "v1 null", - }, - { - policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "foo", - }, - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "bar", - Groups: []string{user.AllAuthenticated}, - }, - ResourceRequest: true, - }, - matches: false, - name: "v1 user name mis-match", - }, - { - policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "*", - Readonly: true, - }, - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{user.AllAuthenticated}, - }, - ResourceRequest: true, - }, - matches: false, - name: "v1 read-only mismatch", - }, - { - policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "*", - Resource: "foo", - }, - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{user.AllAuthenticated}, - }, - Resource: "bar", - ResourceRequest: true, - }, - matches: false, - name: "v1 resource mis-match", - }, - { - policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "foo", - Namespace: "barr", - Resource: "baz", - }, - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{user.AllAuthenticated}, - }, - Namespace: "bar", - Resource: "baz", - ResourceRequest: true, - }, - matches: false, - name: "v1 namespace mis-match", - }, - { - policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "*", - NonResourcePath: "/api", - }, - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{user.AllAuthenticated}, - }, - Path: "/api2", - ResourceRequest: false, - }, - matches: false, - name: "v1 non-resource mis-match", - }, - { - policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "*", - NonResourcePath: "/api/*", - }, - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{user.AllAuthenticated}, - }, - Path: "/api2/foo", - ResourceRequest: false, - }, - matches: false, - name: "v1 non-resource wildcard subpath mis-match", - }, - - // v1 matches - { - policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "foo", - }, - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{user.AllAuthenticated}, - }, - ResourceRequest: true, - }, - matches: true, - name: "v1 user match", - }, - { - policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "*", - }, - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{user.AllAuthenticated}, - }, - ResourceRequest: true, - }, - matches: true, - name: "v1 user wildcard match", - }, - { - policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - Group: "bar", - }, - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{"bar", user.AllAuthenticated}, - }, - ResourceRequest: true, - }, - matches: true, - name: "v1 group match", - }, - { - policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - Group: "*", - }, - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{"bar", user.AllAuthenticated}, - }, - ResourceRequest: true, - }, - matches: true, - name: "v1 group wildcard match", - }, - { - policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "*", - Readonly: true, - }, - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{user.AllAuthenticated}, - }, - Verb: "get", - ResourceRequest: true, - }, - matches: true, - name: "v1 read-only match", - }, - { - policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "*", - Resource: "foo", - }, - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{user.AllAuthenticated}, - }, - Resource: "foo", - ResourceRequest: true, - }, - matches: true, - name: "v1 resource match", - }, - { - policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "foo", - Namespace: "bar", - Resource: "baz", - }, - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{user.AllAuthenticated}, - }, - Namespace: "bar", - Resource: "baz", - ResourceRequest: true, - }, - matches: true, - name: "v1 namespace match", - }, - { - policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "*", - NonResourcePath: "/api", - }, - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{user.AllAuthenticated}, - }, - Path: "/api", - ResourceRequest: false, - }, - matches: true, - name: "v1 non-resource match", - }, - { - policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "*", - NonResourcePath: "*", - }, - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{user.AllAuthenticated}, - }, - Path: "/api", - ResourceRequest: false, - }, - matches: true, - name: "v1 non-resource wildcard match", - }, - { - policy: &v1beta1.Policy{ - Spec: v1beta1.PolicySpec{ - User: "*", - NonResourcePath: "/api/*", - }, - }, - attr: authorizer.AttributesRecord{ - User: &user.DefaultInfo{ - Name: "foo", - Groups: []string{user.AllAuthenticated}, - }, - Path: "/api/foo", - ResourceRequest: false, - }, - matches: true, - name: "v1 non-resource wildcard subpath match", - }, - } - for _, test := range tests { - policy := &abac.Policy{} - if err := abac.Scheme.Convert(test.policy, policy, nil); err != nil { - t.Errorf("%s: error converting: %v", test.name, err) - continue - } - matches := matches(*policy, test.attr) - if test.matches != matches { - t.Errorf("%s: expected: %t, saw: %t", test.name, test.matches, matches) - continue - } - } -} diff --git a/pkg/auth/authorizer/abac/example_policy_file.jsonl b/pkg/auth/authorizer/abac/example_policy_file.jsonl deleted file mode 100644 index 14993be27b..0000000000 --- a/pkg/auth/authorizer/abac/example_policy_file.jsonl +++ /dev/null @@ -1,11 +0,0 @@ -{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"group":"system:authenticated", "nonResourcePath": "*", "readonly": true}} -{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"group":"system:unauthenticated", "nonResourcePath": "*", "readonly": true}} -{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user":"admin", "namespace": "*", "resource": "*", "apiGroup": "*" }} -{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user":"scheduler", "namespace": "*", "resource": "pods", "readonly": true }} -{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user":"scheduler", "namespace": "*", "resource": "bindings" }} -{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user":"kubelet", "namespace": "*", "resource": "pods", "readonly": true }} -{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user":"kubelet", "namespace": "*", "resource": "services", "readonly": true }} -{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user":"kubelet", "namespace": "*", "resource": "endpoints", "readonly": true }} -{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user":"kubelet", "namespace": "*", "resource": "events" }} -{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user":"alice", "namespace": "projectCaribou", "resource": "*", "apiGroup": "*" }} -{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user":"bob", "namespace": "projectCaribou", "resource": "*", "apiGroup": "*", "readonly": true }} \ No newline at end of file diff --git a/pkg/kubeapiserver/authorizer/config.go b/pkg/kubeapiserver/authorizer/config.go index 8382be9c86..ee6c089217 100644 --- a/pkg/kubeapiserver/authorizer/config.go +++ b/pkg/kubeapiserver/authorizer/config.go @@ -25,7 +25,6 @@ import ( "k8s.io/apiserver/pkg/authorization/union" "k8s.io/apiserver/plugin/pkg/authorizer/webhook" versionedinformers "k8s.io/client-go/informers" - "k8s.io/kubernetes/pkg/auth/authorizer/abac" "k8s.io/kubernetes/pkg/auth/nodeidentifier" "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes" "k8s.io/kubernetes/plugin/pkg/auth/authorizer/node" @@ -39,9 +38,6 @@ type Config struct { // Options for ModeABAC - // Path to an ABAC policy file. - PolicyFile string - // Options for ModeWebhook // Kubeconfig file for Webhook authorization plugin. @@ -89,13 +85,6 @@ func (config Config) New() (authorizer.Authorizer, authorizer.RuleResolver, erro alwaysDenyAuthorizer := authorizerfactory.NewAlwaysDenyAuthorizer() authorizers = append(authorizers, alwaysDenyAuthorizer) ruleResolvers = append(ruleResolvers, alwaysDenyAuthorizer) - case modes.ModeABAC: - abacAuthorizer, err := abac.NewFromFile(config.PolicyFile) - if err != nil { - return nil, nil, err - } - authorizers = append(authorizers, abacAuthorizer) - ruleResolvers = append(ruleResolvers, abacAuthorizer) case modes.ModeWebhook: webhookAuthorizer, err := webhook.New(config.WebhookConfigFile, config.WebhookCacheAuthorizedTTL, diff --git a/pkg/kubeapiserver/authorizer/modes/modes.go b/pkg/kubeapiserver/authorizer/modes/modes.go index 501b98a95c..76319a342a 100644 --- a/pkg/kubeapiserver/authorizer/modes/modes.go +++ b/pkg/kubeapiserver/authorizer/modes/modes.go @@ -23,8 +23,6 @@ const ( ModeAlwaysAllow string = "AlwaysAllow" // ModeAlwaysDeny is the mode to set no requests as authorized ModeAlwaysDeny string = "AlwaysDeny" - // ModeABAC is the mode to use Attribute Based Access Control to authorize - ModeABAC string = "ABAC" // ModeWebhook is the mode to make an external webhook call to authorize ModeWebhook string = "Webhook" // ModeRBAC is the mode to use Role Based Access Control to authorize @@ -34,7 +32,7 @@ const ( ) // AuthorizationModeChoices is the list of supported authorization modes -var AuthorizationModeChoices = []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeABAC, ModeWebhook, ModeRBAC, ModeNode} +var AuthorizationModeChoices = []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeWebhook, ModeRBAC, ModeNode} // IsValidAuthorizationMode returns true if the given authorization mode is a valid one for the apiserver func IsValidAuthorizationMode(authzMode string) bool { diff --git a/pkg/kubeapiserver/options/authorization.go b/pkg/kubeapiserver/options/authorization.go index b708074ce0..9fe66d87f8 100644 --- a/pkg/kubeapiserver/options/authorization.go +++ b/pkg/kubeapiserver/options/authorization.go @@ -31,7 +31,6 @@ import ( type BuiltInAuthorizationOptions struct { Modes []string - PolicyFile string WebhookConfigFile string WebhookCacheAuthorizedTTL time.Duration WebhookCacheUnauthorizedTTL time.Duration @@ -61,11 +60,6 @@ func (s *BuiltInAuthorizationOptions) Validate() []error { if !allowedModes.Has(mode) { allErrors = append(allErrors, fmt.Errorf("authorization-mode %q is not a valid mode", mode)) } - if mode == authzmodes.ModeABAC { - if s.PolicyFile == "" { - allErrors = append(allErrors, fmt.Errorf("authorization-mode ABAC's authorization policy file not passed")) - } - } if mode == authzmodes.ModeWebhook { if s.WebhookConfigFile == "" { allErrors = append(allErrors, fmt.Errorf("authorization-mode Webhook's authorization config file not passed")) @@ -73,10 +67,6 @@ func (s *BuiltInAuthorizationOptions) Validate() []error { } } - if s.PolicyFile != "" && !modes.Has(authzmodes.ModeABAC) { - allErrors = append(allErrors, fmt.Errorf("cannot specify --authorization-policy-file without mode ABAC")) - } - if s.WebhookConfigFile != "" && !modes.Has(authzmodes.ModeWebhook) { allErrors = append(allErrors, fmt.Errorf("cannot specify --authorization-webhook-config-file without mode Webhook")) } @@ -93,9 +83,6 @@ func (s *BuiltInAuthorizationOptions) AddFlags(fs *pflag.FlagSet) { "Ordered list of plug-ins to do authorization on secure port. Comma-delimited list of: "+ strings.Join(authzmodes.AuthorizationModeChoices, ",")+".") - fs.StringVar(&s.PolicyFile, "authorization-policy-file", s.PolicyFile, ""+ - "File with authorization policy in json line by line 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.") @@ -112,7 +99,6 @@ func (s *BuiltInAuthorizationOptions) AddFlags(fs *pflag.FlagSet) { func (s *BuiltInAuthorizationOptions) ToAuthorizationConfig(versionedInformerFactory versionedinformers.SharedInformerFactory) authorizer.Config { return authorizer.Config{ AuthorizationModes: s.Modes, - PolicyFile: s.PolicyFile, WebhookConfigFile: s.WebhookConfigFile, WebhookCacheAuthorizedTTL: s.WebhookCacheAuthorizedTTL, WebhookCacheUnauthorizedTTL: s.WebhookCacheUnauthorizedTTL,