Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
1034efd4
Unverified
Commit
1034efd4
authored
May 02, 2018
by
Jordan Liggitt
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Allow non-RBAC authorizers to participate in role/clusterrole escalation checks
parent
f54593b7
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
514 additions
and
12 deletions
+514
-12
.golint_failures
hack/.golint_failures
+0
-1
create_role.go
pkg/kubectl/cmd/create/create_role.go
+11
-1
BUILD
pkg/registry/rbac/BUILD
+1
-0
BUILD
pkg/registry/rbac/clusterrole/policybased/BUILD
+21
-0
storage.go
pkg/registry/rbac/clusterrole/policybased/storage.go
+7
-4
storage_test.go
pkg/registry/rbac/clusterrole/policybased/storage_test.go
+195
-0
escalation_check.go
pkg/registry/rbac/escalation_check.go
+52
-0
storage_rbac.go
pkg/registry/rbac/rest/storage_rbac.go
+2
-2
BUILD
pkg/registry/rbac/role/policybased/BUILD
+21
-0
storage.go
pkg/registry/rbac/role/policybased/storage.go
+7
-4
storage_test.go
pkg/registry/rbac/role/policybased/storage_test.go
+197
-0
No files found.
hack/.golint_failures
View file @
1034efd4
...
@@ -311,7 +311,6 @@ pkg/registry/networking/rest
...
@@ -311,7 +311,6 @@ pkg/registry/networking/rest
pkg/registry/policy/poddisruptionbudget
pkg/registry/policy/poddisruptionbudget
pkg/registry/policy/poddisruptionbudget/storage
pkg/registry/policy/poddisruptionbudget/storage
pkg/registry/policy/rest
pkg/registry/policy/rest
pkg/registry/rbac
pkg/registry/rbac/clusterrole
pkg/registry/rbac/clusterrole
pkg/registry/rbac/clusterrole/policybased
pkg/registry/rbac/clusterrole/policybased
pkg/registry/rbac/clusterrolebinding
pkg/registry/rbac/clusterrolebinding
...
...
pkg/kubectl/cmd/create/create_role.go
View file @
1034efd4
...
@@ -54,7 +54,7 @@ var (
...
@@ -54,7 +54,7 @@ var (
kubectl create role foo --verb=get,list,watch --resource=pods,pods/status`
))
kubectl create role foo --verb=get,list,watch --resource=pods,pods/status`
))
// Valid resource verb list for validation.
// Valid resource verb list for validation.
validResourceVerbs
=
[]
string
{
"*"
,
"get"
,
"delete"
,
"list"
,
"create"
,
"update"
,
"patch"
,
"watch"
,
"proxy"
,
"deletecollection"
,
"use"
,
"bind"
,
"impersonate"
}
validResourceVerbs
=
[]
string
{
"*"
,
"get"
,
"delete"
,
"list"
,
"create"
,
"update"
,
"patch"
,
"watch"
,
"proxy"
,
"deletecollection"
,
"use"
,
"bind"
,
"
escalate"
,
"
impersonate"
}
// Specialized verbs and GroupResources
// Specialized verbs and GroupResources
specialVerbs
=
map
[
string
][]
schema
.
GroupResource
{
specialVerbs
=
map
[
string
][]
schema
.
GroupResource
{
...
@@ -74,6 +74,16 @@ var (
...
@@ -74,6 +74,16 @@ var (
Resource
:
"clusterroles"
,
Resource
:
"clusterroles"
,
},
},
},
},
"escalate"
:
{
{
Group
:
"rbac.authorization.k8s.io"
,
Resource
:
"roles"
,
},
{
Group
:
"rbac.authorization.k8s.io"
,
Resource
:
"clusterroles"
,
},
},
"impersonate"
:
{
"impersonate"
:
{
{
{
Group
:
""
,
Group
:
""
,
...
...
pkg/registry/rbac/BUILD
View file @
1034efd4
...
@@ -18,6 +18,7 @@ go_library(
...
@@ -18,6 +18,7 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
...
...
pkg/registry/rbac/clusterrole/policybased/BUILD
View file @
1034efd4
...
@@ -3,6 +3,7 @@ package(default_visibility = ["//visibility:public"])
...
@@ -3,6 +3,7 @@ package(default_visibility = ["//visibility:public"])
load(
load(
"@io_bazel_rules_go//go:def.bzl",
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_library",
"go_test",
)
)
go_library(
go_library(
...
@@ -16,6 +17,7 @@ go_library(
...
@@ -16,6 +17,7 @@ go_library(
"//pkg/registry/rbac/validation:go_default_library",
"//pkg/registry/rbac/validation:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
],
],
)
)
...
@@ -32,3 +34,22 @@ filegroup(
...
@@ -32,3 +34,22 @@ filegroup(
srcs = [":package-srcs"],
srcs = [":package-srcs"],
tags = ["automanaged"],
tags = ["automanaged"],
)
)
go_test(
name = "go_default_test",
srcs = ["storage_test.go"],
embed = [":go_default_library"],
deps = [
"//pkg/apis/rbac:go_default_library",
"//pkg/apis/rbac/install:go_default_library",
"//pkg/registry/rbac/validation:go_default_library",
"//vendor/k8s.io/api/rbac/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
],
)
pkg/registry/rbac/clusterrole/policybased/storage.go
View file @
1034efd4
...
@@ -23,6 +23,7 @@ import (
...
@@ -23,6 +23,7 @@ import (
apierrors
"k8s.io/apimachinery/pkg/api/errors"
apierrors
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/registry/rest"
kapihelper
"k8s.io/kubernetes/pkg/apis/core/helper"
kapihelper
"k8s.io/kubernetes/pkg/apis/core/helper"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/apis/rbac"
...
@@ -35,11 +36,13 @@ var groupResource = rbac.Resource("clusterroles")
...
@@ -35,11 +36,13 @@ var groupResource = rbac.Resource("clusterroles")
type
Storage
struct
{
type
Storage
struct
{
rest
.
StandardStorage
rest
.
StandardStorage
authorizer
authorizer
.
Authorizer
ruleResolver
rbacregistryvalidation
.
AuthorizationRuleResolver
ruleResolver
rbacregistryvalidation
.
AuthorizationRuleResolver
}
}
func
NewStorage
(
s
rest
.
StandardStorage
,
ruleResolver
rbacregistryvalidation
.
AuthorizationRuleResolver
)
*
Storage
{
func
NewStorage
(
s
rest
.
StandardStorage
,
authorizer
authorizer
.
Authorizer
,
ruleResolver
rbacregistryvalidation
.
AuthorizationRuleResolver
)
*
Storage
{
return
&
Storage
{
s
,
ruleResolver
}
return
&
Storage
{
s
,
authorizer
,
ruleResolver
}
}
}
func
(
r
*
Storage
)
NamespaceScoped
()
bool
{
func
(
r
*
Storage
)
NamespaceScoped
()
bool
{
...
@@ -52,7 +55,7 @@ var fullAuthority = []rbac.PolicyRule{
...
@@ -52,7 +55,7 @@ var fullAuthority = []rbac.PolicyRule{
}
}
func
(
s
*
Storage
)
Create
(
ctx
context
.
Context
,
obj
runtime
.
Object
,
createValidatingAdmission
rest
.
ValidateObjectFunc
,
includeUninitialized
bool
)
(
runtime
.
Object
,
error
)
{
func
(
s
*
Storage
)
Create
(
ctx
context
.
Context
,
obj
runtime
.
Object
,
createValidatingAdmission
rest
.
ValidateObjectFunc
,
includeUninitialized
bool
)
(
runtime
.
Object
,
error
)
{
if
rbacregistry
.
EscalationAllowed
(
ctx
)
{
if
rbacregistry
.
EscalationAllowed
(
ctx
)
||
rbacregistry
.
RoleEscalationAuthorized
(
ctx
,
s
.
authorizer
)
{
return
s
.
StandardStorage
.
Create
(
ctx
,
obj
,
createValidatingAdmission
,
includeUninitialized
)
return
s
.
StandardStorage
.
Create
(
ctx
,
obj
,
createValidatingAdmission
,
includeUninitialized
)
}
}
...
@@ -72,7 +75,7 @@ func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidati
...
@@ -72,7 +75,7 @@ func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidati
}
}
func
(
s
*
Storage
)
Update
(
ctx
context
.
Context
,
name
string
,
obj
rest
.
UpdatedObjectInfo
,
createValidation
rest
.
ValidateObjectFunc
,
updateValidation
rest
.
ValidateObjectUpdateFunc
)
(
runtime
.
Object
,
bool
,
error
)
{
func
(
s
*
Storage
)
Update
(
ctx
context
.
Context
,
name
string
,
obj
rest
.
UpdatedObjectInfo
,
createValidation
rest
.
ValidateObjectFunc
,
updateValidation
rest
.
ValidateObjectUpdateFunc
)
(
runtime
.
Object
,
bool
,
error
)
{
if
rbacregistry
.
EscalationAllowed
(
ctx
)
{
if
rbacregistry
.
EscalationAllowed
(
ctx
)
||
rbacregistry
.
RoleEscalationAuthorized
(
ctx
,
s
.
authorizer
)
{
return
s
.
StandardStorage
.
Update
(
ctx
,
name
,
obj
,
createValidation
,
updateValidation
)
return
s
.
StandardStorage
.
Update
(
ctx
,
name
,
obj
,
createValidation
,
updateValidation
)
}
}
...
...
pkg/registry/rbac/clusterrole/policybased/storage_test.go
0 → 100644
View file @
1034efd4
/*
Copyright 2018 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
policybased
import
(
"context"
"testing"
rbacv1
"k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/apis/rbac"
_
"k8s.io/kubernetes/pkg/apis/rbac/install"
"k8s.io/kubernetes/pkg/registry/rbac/validation"
)
func
TestEscalation
(
t
*
testing
.
T
)
{
createContext
:=
request
.
WithRequestInfo
(
request
.
WithNamespace
(
context
.
TODO
(),
""
),
&
request
.
RequestInfo
{
IsResourceRequest
:
true
,
Verb
:
"create"
,
APIGroup
:
"rbac.authorization.k8s.io"
,
APIVersion
:
"v1"
,
Resource
:
"clusterroles"
,
Name
:
""
,
})
updateContext
:=
request
.
WithRequestInfo
(
request
.
WithNamespace
(
context
.
TODO
(),
""
),
&
request
.
RequestInfo
{
IsResourceRequest
:
true
,
Verb
:
"update"
,
APIGroup
:
"rbac.authorization.k8s.io"
,
APIVersion
:
"v1"
,
Resource
:
"clusterroles"
,
Name
:
"myrole"
,
})
superuser
:=
&
user
.
DefaultInfo
{
Name
:
"superuser"
,
Groups
:
[]
string
{
"system:masters"
}}
bob
:=
&
user
.
DefaultInfo
{
Name
:
"bob"
}
steve
:=
&
user
.
DefaultInfo
{
Name
:
"steve"
}
alice
:=
&
user
.
DefaultInfo
{
Name
:
"alice"
}
authzCalled
:=
0
fakeStorage
:=
&
fakeStorage
{}
fakeAuthorizer
:=
authorizer
.
AuthorizerFunc
(
func
(
attr
authorizer
.
Attributes
)
(
authorizer
.
Decision
,
string
,
error
)
{
authzCalled
++
if
attr
.
GetUser
()
.
GetName
()
==
"steve"
{
return
authorizer
.
DecisionAllow
,
""
,
nil
}
return
authorizer
.
DecisionNoOpinion
,
""
,
nil
})
fakeRuleResolver
,
_
:=
validation
.
NewTestRuleResolver
(
nil
,
nil
,
[]
*
rbacv1
.
ClusterRole
{{
ObjectMeta
:
metav1
.
ObjectMeta
{
Name
:
"alice-role"
},
Rules
:
[]
rbacv1
.
PolicyRule
{{
APIGroups
:
[]
string
{
"*"
},
Resources
:
[]
string
{
"*"
},
Verbs
:
[]
string
{
"*"
}}}}},
[]
*
rbacv1
.
ClusterRoleBinding
{{
RoleRef
:
rbacv1
.
RoleRef
{
Name
:
"alice-role"
,
APIGroup
:
"rbac.authorization.k8s.io"
,
Kind
:
"ClusterRole"
},
Subjects
:
[]
rbacv1
.
Subject
{{
Name
:
"alice"
,
Kind
:
"User"
,
APIGroup
:
"rbac.authorization.k8s.io"
}}}},
)
role
:=
&
rbac
.
ClusterRole
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Name
:
"myrole"
,
Namespace
:
""
},
Rules
:
[]
rbac
.
PolicyRule
{{
APIGroups
:
[]
string
{
""
},
Verbs
:
[]
string
{
"get"
},
Resources
:
[]
string
{
"pods"
}}},
}
s
:=
NewStorage
(
fakeStorage
,
fakeAuthorizer
,
fakeRuleResolver
)
testcases
:=
[]
struct
{
name
string
user
user
.
Info
expectAllowed
bool
expectAuthz
bool
}{
// superuser doesn't even trigger an authz check, and is allowed
{
name
:
"superuser"
,
user
:
superuser
,
expectAuthz
:
false
,
expectAllowed
:
true
,
},
// bob triggers an authz check, is disallowed by the authorizer, and has no RBAC permissions, so is not allowed
{
name
:
"bob"
,
user
:
bob
,
expectAuthz
:
true
,
expectAllowed
:
false
,
},
// steve triggers an authz check, is allowed by the authorizer, and has no RBAC permissions, but is still allowed
{
name
:
"steve"
,
user
:
steve
,
expectAuthz
:
true
,
expectAllowed
:
true
,
},
// alice triggers an authz check, is denied by the authorizer, but has RBAC permissions in the fakeRuleResolver, so is allowed
{
name
:
"alice"
,
user
:
alice
,
expectAuthz
:
true
,
expectAllowed
:
true
,
},
}
for
_
,
tc
:=
range
testcases
{
t
.
Run
(
tc
.
name
,
func
(
t
*
testing
.
T
)
{
authzCalled
,
fakeStorage
.
created
,
fakeStorage
.
updated
=
0
,
0
,
0
_
,
err
:=
s
.
Create
(
request
.
WithUser
(
createContext
,
tc
.
user
),
role
,
nil
,
false
)
if
tc
.
expectAllowed
{
if
err
!=
nil
{
t
.
Error
(
err
)
return
}
if
fakeStorage
.
created
!=
1
{
t
.
Errorf
(
"unexpected calls to underlying storage.Create: %d"
,
fakeStorage
.
created
)
return
}
}
else
{
if
!
errors
.
IsForbidden
(
err
)
{
t
.
Errorf
(
"expected forbidden, got %v"
,
err
)
return
}
if
fakeStorage
.
created
!=
0
{
t
.
Errorf
(
"unexpected calls to underlying storage.Create: %d"
,
fakeStorage
.
created
)
return
}
}
if
tc
.
expectAuthz
!=
(
authzCalled
>
0
)
{
t
.
Fatalf
(
"expected authz=%v, saw %d calls"
,
tc
.
expectAuthz
,
authzCalled
)
}
authzCalled
,
fakeStorage
.
created
,
fakeStorage
.
updated
=
0
,
0
,
0
_
,
_
,
err
=
s
.
Update
(
request
.
WithUser
(
updateContext
,
tc
.
user
),
role
.
Name
,
rest
.
DefaultUpdatedObjectInfo
(
role
),
nil
,
nil
)
if
tc
.
expectAllowed
{
if
err
!=
nil
{
t
.
Error
(
err
)
return
}
if
fakeStorage
.
updated
!=
1
{
t
.
Errorf
(
"unexpected calls to underlying storage.Update: %d"
,
fakeStorage
.
updated
)
return
}
}
else
{
if
!
errors
.
IsForbidden
(
err
)
{
t
.
Errorf
(
"expected forbidden, got %v"
,
err
)
return
}
if
fakeStorage
.
updated
!=
0
{
t
.
Errorf
(
"unexpected calls to underlying storage.Update: %d"
,
fakeStorage
.
updated
)
return
}
}
if
tc
.
expectAuthz
!=
(
authzCalled
>
0
)
{
t
.
Fatalf
(
"expected authz=%v, saw %d calls"
,
tc
.
expectAuthz
,
authzCalled
)
}
})
}
}
type
fakeStorage
struct
{
updated
int
created
int
rest
.
StandardStorage
}
func
(
f
*
fakeStorage
)
Create
(
ctx
context
.
Context
,
obj
runtime
.
Object
,
createValidation
rest
.
ValidateObjectFunc
,
includeUninitialized
bool
)
(
runtime
.
Object
,
error
)
{
f
.
created
++
return
nil
,
nil
}
func
(
f
*
fakeStorage
)
Update
(
ctx
context
.
Context
,
name
string
,
objInfo
rest
.
UpdatedObjectInfo
,
createValidation
rest
.
ValidateObjectFunc
,
updateValidation
rest
.
ValidateObjectUpdateFunc
)
(
runtime
.
Object
,
bool
,
error
)
{
obj
,
err
:=
objInfo
.
UpdatedObject
(
ctx
,
&
rbac
.
ClusterRole
{})
if
err
!=
nil
{
return
obj
,
false
,
err
}
f
.
updated
++
return
nil
,
false
,
nil
}
pkg/registry/rbac/escalation_check.go
View file @
1034efd4
...
@@ -20,6 +20,7 @@ import (
...
@@ -20,6 +20,7 @@ import (
"context"
"context"
"fmt"
"fmt"
"k8s.io/apimachinery/pkg/runtime/schema"
utilruntime
"k8s.io/apimachinery/pkg/util/runtime"
utilruntime
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/authorization/authorizer"
...
@@ -27,6 +28,7 @@ import (
...
@@ -27,6 +28,7 @@ import (
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/apis/rbac"
)
)
// EscalationAllowed checks if the user associated with the context is a superuser
func
EscalationAllowed
(
ctx
context
.
Context
)
bool
{
func
EscalationAllowed
(
ctx
context
.
Context
)
bool
{
u
,
ok
:=
genericapirequest
.
UserFrom
(
ctx
)
u
,
ok
:=
genericapirequest
.
UserFrom
(
ctx
)
if
!
ok
{
if
!
ok
{
...
@@ -44,6 +46,56 @@ func EscalationAllowed(ctx context.Context) bool {
...
@@ -44,6 +46,56 @@ func EscalationAllowed(ctx context.Context) bool {
return
false
return
false
}
}
var
roleResources
=
map
[
schema
.
GroupResource
]
bool
{
rbac
.
SchemeGroupVersion
.
WithResource
(
"clusterroles"
)
.
GroupResource
()
:
true
,
rbac
.
SchemeGroupVersion
.
WithResource
(
"roles"
)
.
GroupResource
()
:
true
,
}
// RoleEscalationAuthorized checks if the user associated with the context is explicitly authorized to escalate the role resource associated with the context
func
RoleEscalationAuthorized
(
ctx
context
.
Context
,
a
authorizer
.
Authorizer
)
bool
{
if
a
==
nil
{
return
false
}
user
,
ok
:=
genericapirequest
.
UserFrom
(
ctx
)
if
!
ok
{
return
false
}
requestInfo
,
ok
:=
genericapirequest
.
RequestInfoFrom
(
ctx
)
if
!
ok
{
return
false
}
if
!
requestInfo
.
IsResourceRequest
{
return
false
}
requestResource
:=
schema
.
GroupResource
{
Group
:
requestInfo
.
APIGroup
,
Resource
:
requestInfo
.
Resource
}
if
!
roleResources
[
requestResource
]
{
return
false
}
attrs
:=
authorizer
.
AttributesRecord
{
User
:
user
,
Verb
:
"escalate"
,
APIGroup
:
requestInfo
.
APIGroup
,
Resource
:
requestInfo
.
Resource
,
Name
:
requestInfo
.
Name
,
Namespace
:
requestInfo
.
Namespace
,
ResourceRequest
:
true
,
}
decision
,
_
,
err
:=
a
.
Authorize
(
attrs
)
if
err
!=
nil
{
utilruntime
.
HandleError
(
fmt
.
Errorf
(
"error authorizing user %#v to escalate %#v named %q in namespace %q: %v"
,
user
,
requestResource
,
requestInfo
.
Name
,
requestInfo
.
Namespace
,
err
,
))
}
return
decision
==
authorizer
.
DecisionAllow
}
// BindingAuthorized returns true if the user associated with the context is explicitly authorized to bind the specified roleRef
// BindingAuthorized returns true if the user associated with the context is explicitly authorized to bind the specified roleRef
func
BindingAuthorized
(
ctx
context
.
Context
,
roleRef
rbac
.
RoleRef
,
bindingNamespace
string
,
a
authorizer
.
Authorizer
)
bool
{
func
BindingAuthorized
(
ctx
context
.
Context
,
roleRef
rbac
.
RoleRef
,
bindingNamespace
string
,
a
authorizer
.
Authorizer
)
bool
{
if
a
==
nil
{
if
a
==
nil
{
...
...
pkg/registry/rbac/rest/storage_rbac.go
View file @
1034efd4
...
@@ -98,13 +98,13 @@ func (p RESTStorageProvider) storage(version schema.GroupVersion, apiResourceCon
...
@@ -98,13 +98,13 @@ func (p RESTStorageProvider) storage(version schema.GroupVersion, apiResourceCon
)
)
// roles
// roles
storage
[
"roles"
]
=
rolepolicybased
.
NewStorage
(
rolesStorage
,
authorizationRuleResolver
)
storage
[
"roles"
]
=
rolepolicybased
.
NewStorage
(
rolesStorage
,
p
.
Authorizer
,
authorizationRuleResolver
)
// rolebindings
// rolebindings
storage
[
"rolebindings"
]
=
rolebindingpolicybased
.
NewStorage
(
roleBindingsStorage
,
p
.
Authorizer
,
authorizationRuleResolver
)
storage
[
"rolebindings"
]
=
rolebindingpolicybased
.
NewStorage
(
roleBindingsStorage
,
p
.
Authorizer
,
authorizationRuleResolver
)
// clusterroles
// clusterroles
storage
[
"clusterroles"
]
=
clusterrolepolicybased
.
NewStorage
(
clusterRolesStorage
,
authorizationRuleResolver
)
storage
[
"clusterroles"
]
=
clusterrolepolicybased
.
NewStorage
(
clusterRolesStorage
,
p
.
Authorizer
,
authorizationRuleResolver
)
// clusterrolebindings
// clusterrolebindings
storage
[
"clusterrolebindings"
]
=
clusterrolebindingpolicybased
.
NewStorage
(
clusterRoleBindingsStorage
,
p
.
Authorizer
,
authorizationRuleResolver
)
storage
[
"clusterrolebindings"
]
=
clusterrolebindingpolicybased
.
NewStorage
(
clusterRoleBindingsStorage
,
p
.
Authorizer
,
authorizationRuleResolver
)
...
...
pkg/registry/rbac/role/policybased/BUILD
View file @
1034efd4
...
@@ -3,6 +3,7 @@ package(default_visibility = ["//visibility:public"])
...
@@ -3,6 +3,7 @@ package(default_visibility = ["//visibility:public"])
load(
load(
"@io_bazel_rules_go//go:def.bzl",
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_library",
"go_test",
)
)
go_library(
go_library(
...
@@ -16,6 +17,7 @@ go_library(
...
@@ -16,6 +17,7 @@ go_library(
"//pkg/registry/rbac/validation:go_default_library",
"//pkg/registry/rbac/validation:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
],
],
)
)
...
@@ -32,3 +34,22 @@ filegroup(
...
@@ -32,3 +34,22 @@ filegroup(
srcs = [":package-srcs"],
srcs = [":package-srcs"],
tags = ["automanaged"],
tags = ["automanaged"],
)
)
go_test(
name = "go_default_test",
srcs = ["storage_test.go"],
embed = [":go_default_library"],
deps = [
"//pkg/apis/rbac:go_default_library",
"//pkg/apis/rbac/install:go_default_library",
"//pkg/registry/rbac/validation:go_default_library",
"//vendor/k8s.io/api/rbac/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
],
)
pkg/registry/rbac/role/policybased/storage.go
View file @
1034efd4
...
@@ -22,6 +22,7 @@ import (
...
@@ -22,6 +22,7 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/registry/rest"
kapihelper
"k8s.io/kubernetes/pkg/apis/core/helper"
kapihelper
"k8s.io/kubernetes/pkg/apis/core/helper"
"k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/apis/rbac"
...
@@ -34,11 +35,13 @@ var groupResource = rbac.Resource("roles")
...
@@ -34,11 +35,13 @@ var groupResource = rbac.Resource("roles")
type
Storage
struct
{
type
Storage
struct
{
rest
.
StandardStorage
rest
.
StandardStorage
authorizer
authorizer
.
Authorizer
ruleResolver
rbacregistryvalidation
.
AuthorizationRuleResolver
ruleResolver
rbacregistryvalidation
.
AuthorizationRuleResolver
}
}
func
NewStorage
(
s
rest
.
StandardStorage
,
ruleResolver
rbacregistryvalidation
.
AuthorizationRuleResolver
)
*
Storage
{
func
NewStorage
(
s
rest
.
StandardStorage
,
authorizer
authorizer
.
Authorizer
,
ruleResolver
rbacregistryvalidation
.
AuthorizationRuleResolver
)
*
Storage
{
return
&
Storage
{
s
,
ruleResolver
}
return
&
Storage
{
s
,
authorizer
,
ruleResolver
}
}
}
func
(
r
*
Storage
)
NamespaceScoped
()
bool
{
func
(
r
*
Storage
)
NamespaceScoped
()
bool
{
...
@@ -46,7 +49,7 @@ func (r *Storage) NamespaceScoped() bool {
...
@@ -46,7 +49,7 @@ func (r *Storage) NamespaceScoped() bool {
}
}
func
(
s
*
Storage
)
Create
(
ctx
context
.
Context
,
obj
runtime
.
Object
,
createValidation
rest
.
ValidateObjectFunc
,
includeUninitialized
bool
)
(
runtime
.
Object
,
error
)
{
func
(
s
*
Storage
)
Create
(
ctx
context
.
Context
,
obj
runtime
.
Object
,
createValidation
rest
.
ValidateObjectFunc
,
includeUninitialized
bool
)
(
runtime
.
Object
,
error
)
{
if
rbacregistry
.
EscalationAllowed
(
ctx
)
{
if
rbacregistry
.
EscalationAllowed
(
ctx
)
||
rbacregistry
.
RoleEscalationAuthorized
(
ctx
,
s
.
authorizer
)
{
return
s
.
StandardStorage
.
Create
(
ctx
,
obj
,
createValidation
,
includeUninitialized
)
return
s
.
StandardStorage
.
Create
(
ctx
,
obj
,
createValidation
,
includeUninitialized
)
}
}
...
@@ -59,7 +62,7 @@ func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidati
...
@@ -59,7 +62,7 @@ func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidati
}
}
func
(
s
*
Storage
)
Update
(
ctx
context
.
Context
,
name
string
,
obj
rest
.
UpdatedObjectInfo
,
createValidation
rest
.
ValidateObjectFunc
,
updateValidation
rest
.
ValidateObjectUpdateFunc
)
(
runtime
.
Object
,
bool
,
error
)
{
func
(
s
*
Storage
)
Update
(
ctx
context
.
Context
,
name
string
,
obj
rest
.
UpdatedObjectInfo
,
createValidation
rest
.
ValidateObjectFunc
,
updateValidation
rest
.
ValidateObjectUpdateFunc
)
(
runtime
.
Object
,
bool
,
error
)
{
if
rbacregistry
.
EscalationAllowed
(
ctx
)
{
if
rbacregistry
.
EscalationAllowed
(
ctx
)
||
rbacregistry
.
RoleEscalationAuthorized
(
ctx
,
s
.
authorizer
)
{
return
s
.
StandardStorage
.
Update
(
ctx
,
name
,
obj
,
createValidation
,
updateValidation
)
return
s
.
StandardStorage
.
Update
(
ctx
,
name
,
obj
,
createValidation
,
updateValidation
)
}
}
...
...
pkg/registry/rbac/role/policybased/storage_test.go
0 → 100644
View file @
1034efd4
/*
Copyright 2018 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
policybased
import
(
"context"
"testing"
rbacv1
"k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/apis/rbac"
_
"k8s.io/kubernetes/pkg/apis/rbac/install"
"k8s.io/kubernetes/pkg/registry/rbac/validation"
)
func
TestEscalation
(
t
*
testing
.
T
)
{
createContext
:=
request
.
WithRequestInfo
(
request
.
WithNamespace
(
context
.
TODO
(),
"myns"
),
&
request
.
RequestInfo
{
IsResourceRequest
:
true
,
Verb
:
"create"
,
APIGroup
:
"rbac.authorization.k8s.io"
,
APIVersion
:
"v1"
,
Namespace
:
"myns"
,
Resource
:
"roles"
,
Name
:
""
,
})
updateContext
:=
request
.
WithRequestInfo
(
request
.
WithNamespace
(
context
.
TODO
(),
"myns"
),
&
request
.
RequestInfo
{
IsResourceRequest
:
true
,
Verb
:
"update"
,
APIGroup
:
"rbac.authorization.k8s.io"
,
APIVersion
:
"v1"
,
Namespace
:
"myns"
,
Resource
:
"roles"
,
Name
:
"myrole"
,
})
superuser
:=
&
user
.
DefaultInfo
{
Name
:
"superuser"
,
Groups
:
[]
string
{
"system:masters"
}}
bob
:=
&
user
.
DefaultInfo
{
Name
:
"bob"
}
steve
:=
&
user
.
DefaultInfo
{
Name
:
"steve"
}
alice
:=
&
user
.
DefaultInfo
{
Name
:
"alice"
}
authzCalled
:=
0
fakeStorage
:=
&
fakeStorage
{}
fakeAuthorizer
:=
authorizer
.
AuthorizerFunc
(
func
(
attr
authorizer
.
Attributes
)
(
authorizer
.
Decision
,
string
,
error
)
{
authzCalled
++
if
attr
.
GetUser
()
.
GetName
()
==
"steve"
{
return
authorizer
.
DecisionAllow
,
""
,
nil
}
return
authorizer
.
DecisionNoOpinion
,
""
,
nil
})
fakeRuleResolver
,
_
:=
validation
.
NewTestRuleResolver
(
nil
,
nil
,
[]
*
rbacv1
.
ClusterRole
{{
ObjectMeta
:
metav1
.
ObjectMeta
{
Name
:
"alice-role"
},
Rules
:
[]
rbacv1
.
PolicyRule
{{
APIGroups
:
[]
string
{
"*"
},
Resources
:
[]
string
{
"*"
},
Verbs
:
[]
string
{
"*"
}}}}},
[]
*
rbacv1
.
ClusterRoleBinding
{{
RoleRef
:
rbacv1
.
RoleRef
{
Name
:
"alice-role"
,
APIGroup
:
"rbac.authorization.k8s.io"
,
Kind
:
"ClusterRole"
},
Subjects
:
[]
rbacv1
.
Subject
{{
Name
:
"alice"
,
Kind
:
"User"
,
APIGroup
:
"rbac.authorization.k8s.io"
}}}},
)
role
:=
&
rbac
.
Role
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Name
:
"myrole"
,
Namespace
:
"myns"
},
Rules
:
[]
rbac
.
PolicyRule
{{
APIGroups
:
[]
string
{
""
},
Verbs
:
[]
string
{
"get"
},
Resources
:
[]
string
{
"pods"
}}},
}
s
:=
NewStorage
(
fakeStorage
,
fakeAuthorizer
,
fakeRuleResolver
)
testcases
:=
[]
struct
{
name
string
user
user
.
Info
expectAllowed
bool
expectAuthz
bool
}{
// superuser doesn't even trigger an authz check, and is allowed
{
name
:
"superuser"
,
user
:
superuser
,
expectAuthz
:
false
,
expectAllowed
:
true
,
},
// bob triggers an authz check, is disallowed by the authorizer, and has no RBAC permissions, so is not allowed
{
name
:
"bob"
,
user
:
bob
,
expectAuthz
:
true
,
expectAllowed
:
false
,
},
// steve triggers an authz check, is allowed by the authorizer, and has no RBAC permissions, but is still allowed
{
name
:
"steve"
,
user
:
steve
,
expectAuthz
:
true
,
expectAllowed
:
true
,
},
// alice triggers an authz check, is denied by the authorizer, but has RBAC permissions in the fakeRuleResolver, so is allowed
{
name
:
"alice"
,
user
:
alice
,
expectAuthz
:
true
,
expectAllowed
:
true
,
},
}
for
_
,
tc
:=
range
testcases
{
t
.
Run
(
tc
.
name
,
func
(
t
*
testing
.
T
)
{
authzCalled
,
fakeStorage
.
created
,
fakeStorage
.
updated
=
0
,
0
,
0
_
,
err
:=
s
.
Create
(
request
.
WithUser
(
createContext
,
tc
.
user
),
role
,
nil
,
false
)
if
tc
.
expectAllowed
{
if
err
!=
nil
{
t
.
Error
(
err
)
return
}
if
fakeStorage
.
created
!=
1
{
t
.
Errorf
(
"unexpected calls to underlying storage.Create: %d"
,
fakeStorage
.
created
)
return
}
}
else
{
if
!
errors
.
IsForbidden
(
err
)
{
t
.
Errorf
(
"expected forbidden, got %v"
,
err
)
return
}
if
fakeStorage
.
created
!=
0
{
t
.
Errorf
(
"unexpected calls to underlying storage.Create: %d"
,
fakeStorage
.
created
)
return
}
}
if
tc
.
expectAuthz
!=
(
authzCalled
>
0
)
{
t
.
Fatalf
(
"expected authz=%v, saw %d calls"
,
tc
.
expectAuthz
,
authzCalled
)
}
authzCalled
,
fakeStorage
.
created
,
fakeStorage
.
updated
=
0
,
0
,
0
_
,
_
,
err
=
s
.
Update
(
request
.
WithUser
(
updateContext
,
tc
.
user
),
role
.
Name
,
rest
.
DefaultUpdatedObjectInfo
(
role
),
nil
,
nil
)
if
tc
.
expectAllowed
{
if
err
!=
nil
{
t
.
Error
(
err
)
return
}
if
fakeStorage
.
updated
!=
1
{
t
.
Errorf
(
"unexpected calls to underlying storage.Update: %d"
,
fakeStorage
.
updated
)
return
}
}
else
{
if
!
errors
.
IsForbidden
(
err
)
{
t
.
Errorf
(
"expected forbidden, got %v"
,
err
)
return
}
if
fakeStorage
.
updated
!=
0
{
t
.
Errorf
(
"unexpected calls to underlying storage.Update: %d"
,
fakeStorage
.
updated
)
return
}
}
if
tc
.
expectAuthz
!=
(
authzCalled
>
0
)
{
t
.
Fatalf
(
"expected authz=%v, saw %d calls"
,
tc
.
expectAuthz
,
authzCalled
)
}
})
}
}
type
fakeStorage
struct
{
updated
int
created
int
rest
.
StandardStorage
}
func
(
f
*
fakeStorage
)
Create
(
ctx
context
.
Context
,
obj
runtime
.
Object
,
createValidation
rest
.
ValidateObjectFunc
,
includeUninitialized
bool
)
(
runtime
.
Object
,
error
)
{
f
.
created
++
return
nil
,
nil
}
func
(
f
*
fakeStorage
)
Update
(
ctx
context
.
Context
,
name
string
,
objInfo
rest
.
UpdatedObjectInfo
,
createValidation
rest
.
ValidateObjectFunc
,
updateValidation
rest
.
ValidateObjectUpdateFunc
)
(
runtime
.
Object
,
bool
,
error
)
{
obj
,
err
:=
objInfo
.
UpdatedObject
(
ctx
,
&
rbac
.
Role
{})
if
err
!=
nil
{
return
obj
,
false
,
err
}
f
.
updated
++
return
nil
,
false
,
nil
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment