From 8a2fe9bd2b174f5d8aaa0bebe32ec4b43d15845b Mon Sep 17 00:00:00 2001 From: deads2k Date: Thu, 29 Jan 2015 14:14:54 -0500 Subject: [PATCH] modify policy to correctly identify resource versus kind --- docs/authorization.md | 14 +++--- pkg/apiserver/handlers.go | 4 +- pkg/auth/authorizer/abac/abac.go | 4 +- pkg/auth/authorizer/abac/abac_test.go | 46 +++++++++---------- .../authorizer/abac/example_policy_file.jsonl | 12 ++--- pkg/auth/authorizer/interfaces.go | 8 ++-- test/integration/auth_test.go | 2 +- 7 files changed, 45 insertions(+), 45 deletions(-) diff --git a/docs/authorization.md b/docs/authorization.md index 488d97849b..6d5a077f9f 100644 --- a/docs/authorization.md +++ b/docs/authorization.md @@ -10,7 +10,7 @@ readonly port is not currently subject to authorization, but is planned to be removed soon.) The authorization check for any request compares attributes of the context of -the request, (such as user, resource kind, and namespace) with access +the request, (such as user, resource, and namespace) with access policies. An API call must be allowed by some policy in order to proceed. The following implementations are available, and are selected by flag: @@ -28,10 +28,10 @@ The following implementations are available, and are selected by flag: A request has 4 attributes that can be considered for authorization: - user (the user-string which a user was authenticated as). - whether the request is readonly (GETs are readonly) - - what kind of object is being accessed + - what resource is being accessed - applies only to the API endpoints, such as `/api/v1beta1/pods`. For miscelaneous endpoints, like `/version`, the - kind is the empty string. + resource is the empty string. - the namespace of the object being access, or the empty string if the endpoint does not support namespaced objects. @@ -49,7 +49,7 @@ Each line is a "policy object". A policy object is a map with the following pro - `user`, type string; the user-string from `--token_auth_file` - `readonly`, type boolean, when true, means that the policy only applies to GET operations. - - `kind`, type string; a kind of object, from an URL, such as `pods`. + - `resource`, type string; a resource from an URL, such as `pods`. - `namespace`, type string; a namespace string. An unset property is the same as a property set to the zero value for its type (e.g. empty string, 0, false). @@ -76,9 +76,9 @@ To permit an action Policy with an unset namespace applies regardless of namespa ### Examples 1. Alice can do anything: `{"user":"alice"}` - 2. Kubelet can read any pods: `{"user":"kubelet", "kind": "pods", "readonly": true}` - 3. Kubelet can read and write events: `{"user":"kubelet", "kind": "events"}` - 4. Bob can just read pods in namespace "projectCaribou": `{"user":"bob", "kind": "pods", "readonly": true, "ns": "projectCaribou"}` + 2. Kubelet can read any pods: `{"user":"kubelet", "resource": "pods", "readonly": true}` + 3. Kubelet can read and write events: `{"user":"kubelet", "resource": "events"}` + 4. Bob can just read pods in namespace "projectCaribou": `{"user":"bob", "resource": "pods", "readonly": true, "ns": "projectCaribou"}` [Complete file example](../pkg/auth/authorizer/abac/example_policy_file.jsonl) diff --git a/pkg/apiserver/handlers.go b/pkg/apiserver/handlers.go index 65ac7917b4..01ceab40ad 100644 --- a/pkg/apiserver/handlers.go +++ b/pkg/apiserver/handlers.go @@ -176,8 +176,8 @@ func (r *requestAttributeGetter) GetAttribs(req *http.Request) authorizer.Attrib apiRequestInfo, _ := r.apiRequestInfoResolver.GetAPIRequestInfo(req) // If a path follows the conventions of the REST object store, then - // we can extract the object Kind. Otherwise, not. - attribs.Kind = apiRequestInfo.Resource + // we can extract the resource. Otherwise, not. + attribs.Resource = apiRequestInfo.Resource // If the request specifies a namespace, then the namespace is filled in. // Assumes there is no empty string namespace. Unspecified results diff --git a/pkg/auth/authorizer/abac/abac.go b/pkg/auth/authorizer/abac/abac.go index cba7a796e7..6963df9908 100644 --- a/pkg/auth/authorizer/abac/abac.go +++ b/pkg/auth/authorizer/abac/abac.go @@ -52,7 +52,7 @@ type policy struct { // TODO: make this a proper REST object with its own registry. Readonly bool `json:"readonly,omitempty"` - Kind string `json:"kind,omitempty"` + Resource string `json:"resource,omitempty"` Namespace string `json:"namespace,omitempty"` // TODO: "expires" string in RFC3339 format. @@ -100,7 +100,7 @@ func NewFromFile(path string) (policyList, error) { func (p policy) matches(a authorizer.Attributes) bool { if p.subjectMatches(a) { if p.Readonly == false || (p.Readonly == a.IsReadOnly()) { - if p.Kind == "" || (p.Kind == a.GetKind()) { + if p.Resource == "" || (p.Resource == a.GetResource()) { if p.Namespace == "" || (p.Namespace == a.GetNamespace()) { return true } diff --git a/pkg/auth/authorizer/abac/abac_test.go b/pkg/auth/authorizer/abac/abac_test.go index 06855586a8..40cfff0ebf 100644 --- a/pkg/auth/authorizer/abac/abac_test.go +++ b/pkg/auth/authorizer/abac/abac_test.go @@ -76,49 +76,49 @@ func NotTestAuthorize(t *testing.T) { testCases := []struct { User user.DefaultInfo RO bool - Kind string + Resource string NS string ExpectAllow bool }{ // Scheduler can read pods - {User: uScheduler, RO: true, Kind: "pods", NS: "ns1", ExpectAllow: true}, - {User: uScheduler, RO: true, Kind: "pods", NS: "", ExpectAllow: true}, + {User: uScheduler, RO: true, Resource: "pods", NS: "ns1", ExpectAllow: true}, + {User: uScheduler, RO: true, Resource: "pods", NS: "", ExpectAllow: true}, // Scheduler cannot write pods - {User: uScheduler, RO: false, Kind: "pods", NS: "ns1", ExpectAllow: false}, - {User: uScheduler, RO: false, Kind: "pods", NS: "", ExpectAllow: false}, + {User: uScheduler, RO: false, Resource: "pods", NS: "ns1", ExpectAllow: false}, + {User: uScheduler, RO: false, Resource: "pods", NS: "", ExpectAllow: false}, // Scheduler can write bindings - {User: uScheduler, RO: true, Kind: "bindings", NS: "ns1", ExpectAllow: true}, - {User: uScheduler, RO: true, Kind: "bindings", NS: "", ExpectAllow: true}, + {User: uScheduler, RO: true, Resource: "bindings", NS: "ns1", ExpectAllow: true}, + {User: uScheduler, RO: true, Resource: "bindings", NS: "", ExpectAllow: true}, // Alice can read and write anything in the right namespace. - {User: uAlice, RO: true, Kind: "pods", NS: "projectCaribou", ExpectAllow: true}, - {User: uAlice, RO: true, Kind: "widgets", NS: "projectCaribou", ExpectAllow: true}, - {User: uAlice, RO: true, Kind: "", NS: "projectCaribou", ExpectAllow: true}, - {User: uAlice, RO: false, Kind: "pods", NS: "projectCaribou", ExpectAllow: true}, - {User: uAlice, RO: false, Kind: "widgets", NS: "projectCaribou", ExpectAllow: true}, - {User: uAlice, RO: false, Kind: "", NS: "projectCaribou", ExpectAllow: true}, + {User: uAlice, RO: true, Resource: "pods", NS: "projectCaribou", ExpectAllow: true}, + {User: uAlice, RO: true, Resource: "widgets", NS: "projectCaribou", ExpectAllow: true}, + {User: uAlice, RO: true, Resource: "", NS: "projectCaribou", ExpectAllow: true}, + {User: uAlice, RO: false, Resource: "pods", NS: "projectCaribou", ExpectAllow: true}, + {User: uAlice, RO: false, Resource: "widgets", NS: "projectCaribou", ExpectAllow: true}, + {User: uAlice, RO: false, Resource: "", NS: "projectCaribou", ExpectAllow: true}, // .. but not the wrong namespace. - {User: uAlice, RO: true, Kind: "pods", NS: "ns1", ExpectAllow: false}, - {User: uAlice, RO: true, Kind: "widgets", NS: "ns1", ExpectAllow: false}, - {User: uAlice, RO: true, Kind: "", NS: "ns1", ExpectAllow: false}, + {User: uAlice, RO: true, Resource: "pods", NS: "ns1", ExpectAllow: false}, + {User: uAlice, RO: true, Resource: "widgets", NS: "ns1", ExpectAllow: false}, + {User: uAlice, RO: true, Resource: "", NS: "ns1", ExpectAllow: false}, // Chuck can read events, since anyone can. - {User: uChuck, RO: true, Kind: "events", NS: "ns1", ExpectAllow: true}, - {User: uChuck, RO: true, Kind: "events", NS: "", ExpectAllow: true}, + {User: uChuck, RO: true, Resource: "events", NS: "ns1", ExpectAllow: true}, + {User: uChuck, RO: true, Resource: "events", NS: "", ExpectAllow: true}, // Chuck can't do other things. - {User: uChuck, RO: false, Kind: "events", NS: "ns1", ExpectAllow: false}, - {User: uChuck, RO: true, Kind: "pods", NS: "ns1", ExpectAllow: false}, - {User: uChuck, RO: true, Kind: "floop", NS: "ns1", ExpectAllow: false}, + {User: uChuck, RO: false, Resource: "events", NS: "ns1", ExpectAllow: false}, + {User: uChuck, RO: true, Resource: "pods", NS: "ns1", ExpectAllow: false}, + {User: uChuck, RO: true, Resource: "floop", NS: "ns1", ExpectAllow: false}, // Chunk can't access things with no kind or namespace // TODO: find a way to give someone access to miscelaneous endpoints, such as // /healthz, /version, etc. - {User: uChuck, RO: true, Kind: "", NS: "", ExpectAllow: false}, + {User: uChuck, RO: true, Resource: "", NS: "", ExpectAllow: false}, } for _, tc := range testCases { attr := authorizer.AttributesRecord{ User: &tc.User, ReadOnly: tc.RO, - Kind: tc.Kind, + Resource: tc.Resource, Namespace: tc.NS, } t.Logf("tc: %v -> attr %v", tc, attr) diff --git a/pkg/auth/authorizer/abac/example_policy_file.jsonl b/pkg/auth/authorizer/abac/example_policy_file.jsonl index ba566f16ea..a9bdb9eea0 100644 --- a/pkg/auth/authorizer/abac/example_policy_file.jsonl +++ b/pkg/auth/authorizer/abac/example_policy_file.jsonl @@ -1,9 +1,9 @@ {"user":"admin"} -{"user":"scheduler", "readonly": true, "kind": "pods"} -{"user":"scheduler", "kind": "bindings"} -{"user":"kubelet", "readonly": true, "kind": "pods"} -{"user":"kubelet", "readonly": true, "kind": "services"} -{"user":"kubelet", "readonly": true, "kind": "endpoints"} -{"user":"kubelet", "kind": "events"} +{"user":"scheduler", "readonly": true, "resource": "pods"} +{"user":"scheduler", "resource": "bindings"} +{"user":"kubelet", "readonly": true, "resource": "pods"} +{"user":"kubelet", "readonly": true, "resource": "services"} +{"user":"kubelet", "readonly": true, "resource": "endpoints"} +{"user":"kubelet", "resource": "events"} {"user":"alice", "ns": "projectCaribou"} {"user":"bob", "readonly": true, "ns": "projectCaribou"} diff --git a/pkg/auth/authorizer/interfaces.go b/pkg/auth/authorizer/interfaces.go index 30d897d0f4..2567a48af6 100644 --- a/pkg/auth/authorizer/interfaces.go +++ b/pkg/auth/authorizer/interfaces.go @@ -40,7 +40,7 @@ type Attributes interface { GetNamespace() string // The kind of object, if a request is for a REST object. - GetKind() string + GetResource() string } // Authorizer makes an authorization decision based on information gained by making @@ -55,7 +55,7 @@ type AttributesRecord struct { User user.Info ReadOnly bool Namespace string - Kind string + Resource string } func (a AttributesRecord) GetUserName() string { @@ -74,6 +74,6 @@ func (a AttributesRecord) GetNamespace() string { return a.Namespace } -func (a AttributesRecord) GetKind() string { - return a.Kind +func (a AttributesRecord) GetResource() string { + return a.Resource } diff --git a/test/integration/auth_test.go b/test/integration/auth_test.go index 1043de8e53..d4fde99808 100644 --- a/test/integration/auth_test.go +++ b/test/integration/auth_test.go @@ -691,7 +691,7 @@ func TestKindAuthorization(t *testing.T) { t.Fatalf("unexpected error: %v", err) } - a := newAuthorizerWithContents(t, `{"kind": "services"} + a := newAuthorizerWithContents(t, `{"resource": "services"} `) var m *master.Master