Fresh dep ensure

This commit is contained in:
Mike Cronce
2018-11-26 13:23:56 -05:00
parent 93cb8a04d7
commit 407478ab9a
9016 changed files with 551394 additions and 279685 deletions

View File

@ -15,13 +15,14 @@ go_library(
importpath = "k8s.io/kubernetes/pkg/registry/rbac",
deps = [
"//pkg/apis/rbac: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/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/authorization/authorizer:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/conversion:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/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",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
],
)
@ -54,7 +55,7 @@ go_test(
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/apis/core/helper:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
],
)

View File

@ -1,4 +1,7 @@
approvers:
- sig-auth-authorizers-approvers
reviewers:
- deads2k
- liggitt
- enj
- sig-auth-authorizers-reviewers
labels:
- sig/auth

View File

@ -18,13 +18,13 @@ go_library(
"//pkg/apis/rbac:go_default_library",
"//pkg/apis/rbac/v1:go_default_library",
"//pkg/apis/rbac/validation:go_default_library",
"//vendor/k8s.io/api/rbac/v1: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/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library",
],
)

View File

@ -3,6 +3,7 @@ package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
@ -14,9 +15,11 @@ go_library(
"//pkg/apis/rbac:go_default_library",
"//pkg/registry/rbac: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/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
],
)
@ -32,3 +35,22 @@ filegroup(
srcs = [":package-srcs"],
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",
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1: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",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
],
)

View File

@ -22,7 +22,9 @@ import (
"errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/registry/rest"
kapihelper "k8s.io/kubernetes/pkg/apis/core/helper"
"k8s.io/kubernetes/pkg/apis/rbac"
@ -35,11 +37,13 @@ var groupResource = rbac.Resource("clusterroles")
type Storage struct {
rest.StandardStorage
authorizer authorizer.Authorizer
ruleResolver rbacregistryvalidation.AuthorizationRuleResolver
}
func NewStorage(s rest.StandardStorage, ruleResolver rbacregistryvalidation.AuthorizationRuleResolver) *Storage {
return &Storage{s, ruleResolver}
func NewStorage(s rest.StandardStorage, authorizer authorizer.Authorizer, ruleResolver rbacregistryvalidation.AuthorizationRuleResolver) *Storage {
return &Storage{s, authorizer, ruleResolver}
}
func (r *Storage) NamespaceScoped() bool {
@ -51,9 +55,9 @@ var fullAuthority = []rbac.PolicyRule{
rbac.NewRule("*").URLs("*").RuleOrDie(),
}
func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidatingAdmission rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) {
if rbacregistry.EscalationAllowed(ctx) {
return s.StandardStorage.Create(ctx, obj, createValidatingAdmission, includeUninitialized)
func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidatingAdmission rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
if rbacregistry.EscalationAllowed(ctx) || rbacregistry.RoleEscalationAuthorized(ctx, s.authorizer) {
return s.StandardStorage.Create(ctx, obj, createValidatingAdmission, options)
}
clusterRole := obj.(*rbac.ClusterRole)
@ -68,12 +72,12 @@ func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidati
}
}
return s.StandardStorage.Create(ctx, obj, createValidatingAdmission, includeUninitialized)
return s.StandardStorage.Create(ctx, obj, createValidatingAdmission, options)
}
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) {
return s.StandardStorage.Update(ctx, name, obj, createValidation, updateValidation)
func (s *Storage) Update(ctx context.Context, name string, obj rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
if rbacregistry.EscalationAllowed(ctx) || rbacregistry.RoleEscalationAuthorized(ctx, s.authorizer) {
return s.StandardStorage.Update(ctx, name, obj, createValidation, updateValidation, forceAllowCreate, options)
}
nonEscalatingInfo := rest.WrapUpdatedObjectInfo(obj, func(ctx context.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
@ -99,7 +103,7 @@ func (s *Storage) Update(ctx context.Context, name string, obj rest.UpdatedObjec
return obj, nil
})
return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation)
return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation, forceAllowCreate, options)
}
func hasAggregationRule(clusterRole *rbac.ClusterRole) bool {

View File

@ -0,0 +1,195 @@
/*
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, nil)
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, false, 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, options *metav1.CreateOptions) (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, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
obj, err := objInfo.UpdatedObject(ctx, &rbac.ClusterRole{})
if err != nil {
return obj, false, err
}
f.updated++
return nil, false, nil
}

View File

@ -12,9 +12,9 @@ go_library(
deps = [
"//pkg/apis/rbac:go_default_library",
"//pkg/registry/rbac/clusterrole:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
],
)

View File

@ -18,13 +18,13 @@ go_library(
"//pkg/apis/rbac:go_default_library",
"//pkg/apis/rbac/v1:go_default_library",
"//pkg/apis/rbac/validation:go_default_library",
"//vendor/k8s.io/api/rbac/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library",
],
)

View File

@ -15,12 +15,12 @@ go_library(
"//pkg/apis/rbac/v1:go_default_library",
"//pkg/registry/rbac: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/authorization/authorizer:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
],
)

View File

@ -51,14 +51,14 @@ func (r *Storage) NamespaceScoped() bool {
return false
}
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, options *metav1.CreateOptions) (runtime.Object, error) {
if rbacregistry.EscalationAllowed(ctx) {
return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized)
return s.StandardStorage.Create(ctx, obj, createValidation, options)
}
clusterRoleBinding := obj.(*rbac.ClusterRoleBinding)
if rbacregistry.BindingAuthorized(ctx, clusterRoleBinding.RoleRef, metav1.NamespaceNone, s.authorizer) {
return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized)
return s.StandardStorage.Create(ctx, obj, createValidation, options)
}
v1RoleRef := rbacv1.RoleRef{}
@ -73,12 +73,12 @@ func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidati
if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
return nil, errors.NewForbidden(groupResource, clusterRoleBinding.Name, err)
}
return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized)
return s.StandardStorage.Create(ctx, obj, createValidation, options)
}
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, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
if rbacregistry.EscalationAllowed(ctx) {
return s.StandardStorage.Update(ctx, name, obj, createValidation, updateValidation)
return s.StandardStorage.Update(ctx, name, obj, createValidation, updateValidation, forceAllowCreate, options)
}
nonEscalatingInfo := rest.WrapUpdatedObjectInfo(obj, func(ctx context.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
@ -110,5 +110,5 @@ func (s *Storage) Update(ctx context.Context, name string, obj rest.UpdatedObjec
return obj, nil
})
return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation)
return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation, forceAllowCreate, options)
}

View File

@ -15,9 +15,9 @@ go_library(
"//pkg/printers/internalversion:go_default_library",
"//pkg/printers/storage:go_default_library",
"//pkg/registry/rbac/clusterrolebinding:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
],
)

View File

@ -20,6 +20,7 @@ import (
"context"
"fmt"
"k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
@ -27,6 +28,7 @@ import (
"k8s.io/kubernetes/pkg/apis/rbac"
)
// EscalationAllowed checks if the user associated with the context is a superuser
func EscalationAllowed(ctx context.Context) bool {
u, ok := genericapirequest.UserFrom(ctx)
if !ok {
@ -44,6 +46,56 @@ func EscalationAllowed(ctx context.Context) bool {
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
func BindingAuthorized(ctx context.Context, roleRef rbac.RoleRef, bindingNamespace string, a authorizer.Authorizer) bool {
if a == nil {

View File

@ -15,9 +15,9 @@ go_test(
embed = [":go_default_library"],
deps = [
"//pkg/apis/core/helper:go_default_library",
"//vendor/k8s.io/api/rbac/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
],
)
@ -26,6 +26,7 @@ go_library(
srcs = [
"clusterrole_interfaces.go",
"clusterrolebinding_interfaces.go",
"namespace.go",
"reconcile_role.go",
"reconcile_rolebindings.go",
"role_interfaces.go",
@ -35,15 +36,16 @@ go_library(
importpath = "k8s.io/kubernetes/pkg/registry/rbac/reconciliation",
deps = [
"//pkg/registry/rbac/validation:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/api/rbac/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality: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/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/typed/rbac/v1:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1:go_default_library",
],
)

View File

@ -0,0 +1,44 @@
/*
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 reconciliation
import (
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
)
// tryEnsureNamespace gets or creates the given namespace while ignoring forbidden errors.
// It is a best effort attempt as the user may not be able to get or create namespaces.
// This allows us to handle flows where the user can only mutate roles and role bindings.
func tryEnsureNamespace(client corev1client.NamespaceInterface, namespace string) error {
_, getErr := client.Get(namespace, metav1.GetOptions{})
if getErr == nil {
return nil
}
if fatalGetErr := utilerrors.FilterOut(getErr, apierrors.IsNotFound, apierrors.IsForbidden); fatalGetErr != nil {
return fatalGetErr
}
ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}}
_, createErr := client.Create(ns)
return utilerrors.FilterOut(createErr, apierrors.IsAlreadyExists, apierrors.IsForbidden)
}

View File

@ -17,9 +17,7 @@ limitations under the License.
package reconciliation
import (
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
@ -90,8 +88,7 @@ func (c RoleModifier) Get(namespace, name string) (RuleOwner, error) {
}
func (c RoleModifier) Create(in RuleOwner) (RuleOwner, error) {
ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: in.GetNamespace()}}
if _, err := c.NamespaceClient.Create(ns); err != nil && !apierrors.IsAlreadyExists(err) {
if err := tryEnsureNamespace(c.NamespaceClient, in.GetNamespace()); err != nil {
return nil, err
}

View File

@ -17,9 +17,7 @@ limitations under the License.
package reconciliation
import (
corev1 "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
@ -92,8 +90,7 @@ func (c RoleBindingClientAdapter) Get(namespace, name string) (RoleBinding, erro
}
func (c RoleBindingClientAdapter) Create(in RoleBinding) (RoleBinding, error) {
ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: in.GetNamespace()}}
if _, err := c.NamespaceClient.Create(ns); err != nil && !apierrors.IsAlreadyExists(err) {
if err := tryEnsureNamespace(c.NamespaceClient, in.GetNamespace()); err != nil {
return nil, err
}

View File

@ -29,12 +29,8 @@ func (in *ClusterRoleBindingAdapter) DeepCopyInto(out *ClusterRoleBindingAdapter
*out = *in
if in.ClusterRoleBinding != nil {
in, out := &in.ClusterRoleBinding, &out.ClusterRoleBinding
if *in == nil {
*out = nil
} else {
*out = new(v1.ClusterRoleBinding)
(*in).DeepCopyInto(*out)
}
*out = new(v1.ClusterRoleBinding)
(*in).DeepCopyInto(*out)
}
return
}
@ -59,12 +55,8 @@ func (in *ClusterRoleRuleOwner) DeepCopyInto(out *ClusterRoleRuleOwner) {
*out = *in
if in.ClusterRole != nil {
in, out := &in.ClusterRole, &out.ClusterRole
if *in == nil {
*out = nil
} else {
*out = new(v1.ClusterRole)
(*in).DeepCopyInto(*out)
}
*out = new(v1.ClusterRole)
(*in).DeepCopyInto(*out)
}
return
}
@ -89,12 +81,8 @@ func (in *RoleBindingAdapter) DeepCopyInto(out *RoleBindingAdapter) {
*out = *in
if in.RoleBinding != nil {
in, out := &in.RoleBinding, &out.RoleBinding
if *in == nil {
*out = nil
} else {
*out = new(v1.RoleBinding)
(*in).DeepCopyInto(*out)
}
*out = new(v1.RoleBinding)
(*in).DeepCopyInto(*out)
}
return
}
@ -119,12 +107,8 @@ func (in *RoleRuleOwner) DeepCopyInto(out *RoleRuleOwner) {
*out = *in
if in.Role != nil {
in, out := &in.Role, &out.Role
if *in == nil {
*out = nil
} else {
*out = new(v1.Role)
(*in).DeepCopyInto(*out)
}
*out = new(v1.Role)
(*in).DeepCopyInto(*out)
}
return
}

View File

@ -27,23 +27,23 @@ go_library(
"//pkg/registry/rbac/rolebinding/storage:go_default_library",
"//pkg/registry/rbac/validation:go_default_library",
"//plugin/pkg/auth/authorizer/rbac/bootstrappolicy:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/api/rbac/v1:go_default_library",
"//vendor/k8s.io/api/rbac/v1alpha1:go_default_library",
"//vendor/k8s.io/api/rbac/v1beta1: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/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/storage:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/typed/rbac/v1:go_default_library",
"//vendor/k8s.io/client-go/util/retry:go_default_library",
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
"//staging/src/k8s.io/api/rbac/v1alpha1:go_default_library",
"//staging/src/k8s.io/api/rbac/v1beta1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/server:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/server/storage:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1:go_default_library",
"//staging/src/k8s.io/client-go/util/retry:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
],
)

View File

@ -20,7 +20,7 @@ import (
"fmt"
"time"
"github.com/golang/glog"
"k8s.io/klog"
rbacapiv1 "k8s.io/api/rbac/v1"
rbacapiv1alpha1 "k8s.io/api/rbac/v1alpha1"
@ -98,13 +98,13 @@ func (p RESTStorageProvider) storage(version schema.GroupVersion, apiResourceCon
)
// roles
storage["roles"] = rolepolicybased.NewStorage(rolesStorage, authorizationRuleResolver)
storage["roles"] = rolepolicybased.NewStorage(rolesStorage, p.Authorizer, authorizationRuleResolver)
// rolebindings
storage["rolebindings"] = rolebindingpolicybased.NewStorage(roleBindingsStorage, p.Authorizer, authorizationRuleResolver)
// clusterroles
storage["clusterroles"] = clusterrolepolicybased.NewStorage(clusterRolesStorage, authorizationRuleResolver)
storage["clusterroles"] = clusterrolepolicybased.NewStorage(clusterRolesStorage, p.Authorizer, authorizationRuleResolver)
// clusterrolebindings
storage["clusterrolebindings"] = clusterrolebindingpolicybased.NewStorage(clusterRoleBindingsStorage, p.Authorizer, authorizationRuleResolver)
@ -180,11 +180,11 @@ func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc {
}
switch {
case result.Protected && result.Operation != reconciliation.ReconcileNone:
glog.Warningf("skipped reconcile-protected clusterrole.%s/%s with missing permissions: %v", rbac.GroupName, clusterRole.Name, result.MissingRules)
klog.Warningf("skipped reconcile-protected clusterrole.%s/%s with missing permissions: %v", rbac.GroupName, clusterRole.Name, result.MissingRules)
case result.Operation == reconciliation.ReconcileUpdate:
glog.Infof("updated clusterrole.%s/%s with additional permissions: %v", rbac.GroupName, clusterRole.Name, result.MissingRules)
klog.Infof("updated clusterrole.%s/%s with additional permissions: %v", rbac.GroupName, clusterRole.Name, result.MissingRules)
case result.Operation == reconciliation.ReconcileCreate:
glog.Infof("created clusterrole.%s/%s", rbac.GroupName, clusterRole.Name)
klog.Infof("created clusterrole.%s/%s", rbac.GroupName, clusterRole.Name)
}
return nil
})
@ -208,13 +208,13 @@ func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc {
}
switch {
case result.Protected && result.Operation != reconciliation.ReconcileNone:
glog.Warningf("skipped reconcile-protected clusterrolebinding.%s/%s with missing subjects: %v", rbac.GroupName, clusterRoleBinding.Name, result.MissingSubjects)
klog.Warningf("skipped reconcile-protected clusterrolebinding.%s/%s with missing subjects: %v", rbac.GroupName, clusterRoleBinding.Name, result.MissingSubjects)
case result.Operation == reconciliation.ReconcileUpdate:
glog.Infof("updated clusterrolebinding.%s/%s with additional subjects: %v", rbac.GroupName, clusterRoleBinding.Name, result.MissingSubjects)
klog.Infof("updated clusterrolebinding.%s/%s with additional subjects: %v", rbac.GroupName, clusterRoleBinding.Name, result.MissingSubjects)
case result.Operation == reconciliation.ReconcileCreate:
glog.Infof("created clusterrolebinding.%s/%s", rbac.GroupName, clusterRoleBinding.Name)
klog.Infof("created clusterrolebinding.%s/%s", rbac.GroupName, clusterRoleBinding.Name)
case result.Operation == reconciliation.ReconcileRecreate:
glog.Infof("recreated clusterrolebinding.%s/%s", rbac.GroupName, clusterRoleBinding.Name)
klog.Infof("recreated clusterrolebinding.%s/%s", rbac.GroupName, clusterRoleBinding.Name)
}
return nil
})
@ -239,11 +239,11 @@ func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc {
}
switch {
case result.Protected && result.Operation != reconciliation.ReconcileNone:
glog.Warningf("skipped reconcile-protected role.%s/%s in %v with missing permissions: %v", rbac.GroupName, role.Name, namespace, result.MissingRules)
klog.Warningf("skipped reconcile-protected role.%s/%s in %v with missing permissions: %v", rbac.GroupName, role.Name, namespace, result.MissingRules)
case result.Operation == reconciliation.ReconcileUpdate:
glog.Infof("updated role.%s/%s in %v with additional permissions: %v", rbac.GroupName, role.Name, namespace, result.MissingRules)
klog.Infof("updated role.%s/%s in %v with additional permissions: %v", rbac.GroupName, role.Name, namespace, result.MissingRules)
case result.Operation == reconciliation.ReconcileCreate:
glog.Infof("created role.%s/%s in %v", rbac.GroupName, role.Name, namespace)
klog.Infof("created role.%s/%s in %v", rbac.GroupName, role.Name, namespace)
}
return nil
})
@ -269,13 +269,13 @@ func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc {
}
switch {
case result.Protected && result.Operation != reconciliation.ReconcileNone:
glog.Warningf("skipped reconcile-protected rolebinding.%s/%s in %v with missing subjects: %v", rbac.GroupName, roleBinding.Name, namespace, result.MissingSubjects)
klog.Warningf("skipped reconcile-protected rolebinding.%s/%s in %v with missing subjects: %v", rbac.GroupName, roleBinding.Name, namespace, result.MissingSubjects)
case result.Operation == reconciliation.ReconcileUpdate:
glog.Infof("updated rolebinding.%s/%s in %v with additional subjects: %v", rbac.GroupName, roleBinding.Name, namespace, result.MissingSubjects)
klog.Infof("updated rolebinding.%s/%s in %v with additional subjects: %v", rbac.GroupName, roleBinding.Name, namespace, result.MissingSubjects)
case result.Operation == reconciliation.ReconcileCreate:
glog.Infof("created rolebinding.%s/%s in %v", rbac.GroupName, roleBinding.Name, namespace)
klog.Infof("created rolebinding.%s/%s in %v", rbac.GroupName, roleBinding.Name, namespace)
case result.Operation == reconciliation.ReconcileRecreate:
glog.Infof("recreated rolebinding.%s/%s in %v", rbac.GroupName, roleBinding.Name, namespace)
klog.Infof("recreated rolebinding.%s/%s in %v", rbac.GroupName, roleBinding.Name, namespace)
}
return nil
})
@ -324,7 +324,7 @@ func primeAggregatedClusterRoles(clusterRolesToAggregate map[string]string, clus
// the old role already moved to an aggregated role, so there are no custom rules to migrate at this point
return nil
}
glog.V(1).Infof("migrating %v to %v", existingRole.Name, newName)
klog.V(1).Infof("migrating %v to %v", existingRole.Name, newName)
existingRole.Name = newName
existingRole.ResourceVersion = "" // clear this so the object can be created.
if _, err := clusterRoleClient.ClusterRoles().Create(existingRole); err != nil && !apierrors.IsAlreadyExists(err) {

View File

@ -18,13 +18,13 @@ go_library(
"//pkg/apis/rbac:go_default_library",
"//pkg/apis/rbac/v1:go_default_library",
"//pkg/apis/rbac/validation:go_default_library",
"//vendor/k8s.io/api/rbac/v1: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/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library",
],
)

View File

@ -3,6 +3,7 @@ package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
@ -14,9 +15,11 @@ go_library(
"//pkg/apis/rbac:go_default_library",
"//pkg/registry/rbac: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/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
],
)
@ -32,3 +35,22 @@ filegroup(
srcs = [":package-srcs"],
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",
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1: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",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
],
)

View File

@ -21,7 +21,9 @@ import (
"context"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/registry/rest"
kapihelper "k8s.io/kubernetes/pkg/apis/core/helper"
"k8s.io/kubernetes/pkg/apis/rbac"
@ -34,20 +36,22 @@ var groupResource = rbac.Resource("roles")
type Storage struct {
rest.StandardStorage
authorizer authorizer.Authorizer
ruleResolver rbacregistryvalidation.AuthorizationRuleResolver
}
func NewStorage(s rest.StandardStorage, ruleResolver rbacregistryvalidation.AuthorizationRuleResolver) *Storage {
return &Storage{s, ruleResolver}
func NewStorage(s rest.StandardStorage, authorizer authorizer.Authorizer, ruleResolver rbacregistryvalidation.AuthorizationRuleResolver) *Storage {
return &Storage{s, authorizer, ruleResolver}
}
func (r *Storage) NamespaceScoped() bool {
return true
}
func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) {
if rbacregistry.EscalationAllowed(ctx) {
return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized)
func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
if rbacregistry.EscalationAllowed(ctx) || rbacregistry.RoleEscalationAuthorized(ctx, s.authorizer) {
return s.StandardStorage.Create(ctx, obj, createValidation, options)
}
role := obj.(*rbac.Role)
@ -55,12 +59,12 @@ func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidati
if err := rbacregistryvalidation.ConfirmNoEscalationInternal(ctx, s.ruleResolver, rules); err != nil {
return nil, errors.NewForbidden(groupResource, role.Name, err)
}
return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized)
return s.StandardStorage.Create(ctx, obj, createValidation, options)
}
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) {
return s.StandardStorage.Update(ctx, name, obj, createValidation, updateValidation)
func (s *Storage) Update(ctx context.Context, name string, obj rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
if rbacregistry.EscalationAllowed(ctx) || rbacregistry.RoleEscalationAuthorized(ctx, s.authorizer) {
return s.StandardStorage.Update(ctx, name, obj, createValidation, updateValidation, forceAllowCreate, options)
}
nonEscalatingInfo := rest.WrapUpdatedObjectInfo(obj, func(ctx context.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
@ -78,5 +82,5 @@ func (s *Storage) Update(ctx context.Context, name string, obj rest.UpdatedObjec
return obj, nil
})
return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation)
return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation, forceAllowCreate, options)
}

View File

@ -0,0 +1,197 @@
/*
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, nil)
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, false, 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, options *metav1.CreateOptions) (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, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
obj, err := objInfo.UpdatedObject(ctx, &rbac.Role{})
if err != nil {
return obj, false, err
}
f.updated++
return nil, false, nil
}

View File

@ -12,9 +12,9 @@ go_library(
deps = [
"//pkg/apis/rbac:go_default_library",
"//pkg/registry/rbac/role:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
],
)

View File

@ -18,13 +18,13 @@ go_library(
"//pkg/apis/rbac:go_default_library",
"//pkg/apis/rbac/v1:go_default_library",
"//pkg/apis/rbac/validation:go_default_library",
"//vendor/k8s.io/api/rbac/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/names:go_default_library",
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/names:go_default_library",
],
)

View File

@ -15,12 +15,13 @@ go_library(
"//pkg/apis/rbac/v1:go_default_library",
"//pkg/registry/rbac: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/runtime: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",
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/rest:go_default_library",
],
)

View File

@ -22,6 +22,7 @@ import (
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/authorization/authorizer"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
@ -51,9 +52,9 @@ func (r *Storage) NamespaceScoped() bool {
return true
}
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, options *metav1.CreateOptions) (runtime.Object, error) {
if rbacregistry.EscalationAllowed(ctx) {
return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized)
return s.StandardStorage.Create(ctx, obj, createValidation, options)
}
// Get the namespace from the context (populated from the URL).
@ -65,7 +66,7 @@ func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidati
roleBinding := obj.(*rbac.RoleBinding)
if rbacregistry.BindingAuthorized(ctx, roleBinding.RoleRef, namespace, s.authorizer) {
return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized)
return s.StandardStorage.Create(ctx, obj, createValidation, options)
}
v1RoleRef := rbacv1.RoleRef{}
@ -80,12 +81,12 @@ func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidati
if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
return nil, errors.NewForbidden(groupResource, roleBinding.Name, err)
}
return s.StandardStorage.Create(ctx, obj, createValidation, includeUninitialized)
return s.StandardStorage.Create(ctx, obj, createValidation, options)
}
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, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
if rbacregistry.EscalationAllowed(ctx) {
return s.StandardStorage.Update(ctx, name, obj, createValidation, updateValidation)
return s.StandardStorage.Update(ctx, name, obj, createValidation, updateValidation, forceAllowCreate, options)
}
nonEscalatingInfo := rest.WrapUpdatedObjectInfo(obj, func(ctx context.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
@ -124,5 +125,5 @@ func (s *Storage) Update(ctx context.Context, name string, obj rest.UpdatedObjec
return obj, nil
})
return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation)
return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation, forceAllowCreate, options)
}

View File

@ -15,9 +15,9 @@ go_library(
"//pkg/printers/internalversion:go_default_library",
"//pkg/printers/storage:go_default_library",
"//pkg/registry/rbac/rolebinding:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
],
)

View File

@ -16,10 +16,10 @@ go_test(
embed = [":go_default_library"],
deps = [
"//pkg/apis/rbac/v1:go_default_library",
"//vendor/k8s.io/api/rbac/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
],
)
@ -35,13 +35,13 @@ go_library(
deps = [
"//pkg/apis/rbac:go_default_library",
"//pkg/apis/rbac/v1:go_default_library",
"//vendor/github.com/golang/glog: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/util/errors:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authentication/serviceaccount:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//staging/src/k8s.io/api/rbac/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/serviceaccount:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
],
)

View File

@ -20,15 +20,17 @@ import (
"context"
"errors"
"fmt"
"strings"
"github.com/golang/glog"
"k8s.io/klog"
rbacv1 "k8s.io/api/rbac/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/authentication/serviceaccount"
"k8s.io/apiserver/pkg/authentication/user"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
rbacv1helpers "k8s.io/kubernetes/pkg/apis/rbac/v1"
)
type AuthorizationRuleResolver interface {
@ -59,13 +61,28 @@ func ConfirmNoEscalation(ctx context.Context, ruleResolver AuthorizationRuleReso
ownerRules, err := ruleResolver.RulesFor(user, namespace)
if err != nil {
// As per AuthorizationRuleResolver contract, this may return a non fatal error with an incomplete list of policies. Log the error and continue.
glog.V(1).Infof("non-fatal error getting local rules for %v: %v", user, err)
klog.V(1).Infof("non-fatal error getting local rules for %v: %v", user, err)
ruleResolutionErrors = append(ruleResolutionErrors, err)
}
ownerRightsCover, missingRights := Covers(ownerRules, rules)
if !ownerRightsCover {
return apierrors.NewUnauthorized(fmt.Sprintf("attempt to grant extra privileges: %v user=%v ownerrules=%v ruleResolutionErrors=%v", missingRights, user, ownerRules, ruleResolutionErrors))
compactMissingRights := missingRights
if compact, err := CompactRules(missingRights); err == nil {
compactMissingRights = compact
}
missingDescriptions := sets.NewString()
for _, missing := range compactMissingRights {
missingDescriptions.Insert(rbacv1helpers.CompactString(missing))
}
msg := fmt.Sprintf("user %q (groups=%q) is attempting to grant RBAC permissions not currently held:\n%s", user.GetName(), user.GetGroups(), strings.Join(missingDescriptions.List(), "\n"))
if len(ruleResolutionErrors) > 0 {
msg = msg + fmt.Sprintf("; resolution errors: %v", ruleResolutionErrors)
}
return errors.New(msg)
}
return nil
}