modify policy to correctly identify resource versus kind

pull/6/head
deads2k 2015-01-29 14:14:54 -05:00
parent 1c9216a45e
commit 8a2fe9bd2b
7 changed files with 45 additions and 45 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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