1/*
2Copyright 2016 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17// Package policybased implements a standard storage for RoleBinding that prevents privilege escalation.
18package policybased
19
20import (
21	"context"
22
23	rbacv1 "k8s.io/api/rbac/v1"
24	"k8s.io/apimachinery/pkg/api/errors"
25	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26	"k8s.io/apimachinery/pkg/runtime"
27	"k8s.io/apiserver/pkg/authorization/authorizer"
28	genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
29	"k8s.io/apiserver/pkg/registry/rest"
30	kapihelper "k8s.io/kubernetes/pkg/apis/core/helper"
31	"k8s.io/kubernetes/pkg/apis/rbac"
32	rbacv1helpers "k8s.io/kubernetes/pkg/apis/rbac/v1"
33	rbacregistry "k8s.io/kubernetes/pkg/registry/rbac"
34	rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation"
35)
36
37var groupResource = rbac.Resource("rolebindings")
38
39type Storage struct {
40	rest.StandardStorage
41
42	authorizer authorizer.Authorizer
43
44	ruleResolver rbacregistryvalidation.AuthorizationRuleResolver
45}
46
47func NewStorage(s rest.StandardStorage, authorizer authorizer.Authorizer, ruleResolver rbacregistryvalidation.AuthorizationRuleResolver) *Storage {
48	return &Storage{s, authorizer, ruleResolver}
49}
50
51func (r *Storage) NamespaceScoped() bool {
52	return true
53}
54
55func (r *Storage) StorageVersion() runtime.GroupVersioner {
56	svp, ok := r.StandardStorage.(rest.StorageVersionProvider)
57	if !ok {
58		return nil
59	}
60	return svp.StorageVersion()
61}
62
63var _ rest.StorageVersionProvider = &Storage{}
64
65func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
66	if rbacregistry.EscalationAllowed(ctx) {
67		return s.StandardStorage.Create(ctx, obj, createValidation, options)
68	}
69
70	// Get the namespace from the context (populated from the URL).
71	// The namespace in the object can be empty until StandardStorage.Create()->BeforeCreate() populates it from the context.
72	namespace, ok := genericapirequest.NamespaceFrom(ctx)
73	if !ok {
74		return nil, errors.NewBadRequest("namespace is required")
75	}
76
77	roleBinding := obj.(*rbac.RoleBinding)
78	if rbacregistry.BindingAuthorized(ctx, roleBinding.RoleRef, namespace, s.authorizer) {
79		return s.StandardStorage.Create(ctx, obj, createValidation, options)
80	}
81
82	v1RoleRef := rbacv1.RoleRef{}
83	err := rbacv1helpers.Convert_rbac_RoleRef_To_v1_RoleRef(&roleBinding.RoleRef, &v1RoleRef, nil)
84	if err != nil {
85		return nil, err
86	}
87	rules, err := s.ruleResolver.GetRoleReferenceRules(v1RoleRef, namespace)
88	if err != nil {
89		return nil, err
90	}
91	if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
92		return nil, errors.NewForbidden(groupResource, roleBinding.Name, err)
93	}
94	return s.StandardStorage.Create(ctx, obj, createValidation, options)
95}
96
97func (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) {
98	if rbacregistry.EscalationAllowed(ctx) {
99		return s.StandardStorage.Update(ctx, name, obj, createValidation, updateValidation, forceAllowCreate, options)
100	}
101
102	nonEscalatingInfo := rest.WrapUpdatedObjectInfo(obj, func(ctx context.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
103		// Get the namespace from the context (populated from the URL).
104		// The namespace in the object can be empty until StandardStorage.Update()->BeforeUpdate() populates it from the context.
105		namespace, ok := genericapirequest.NamespaceFrom(ctx)
106		if !ok {
107			return nil, errors.NewBadRequest("namespace is required")
108		}
109
110		roleBinding := obj.(*rbac.RoleBinding)
111
112		// if we're only mutating fields needed for the GC to eventually delete this obj, return
113		if rbacregistry.IsOnlyMutatingGCFields(obj, oldObj, kapihelper.Semantic) {
114			return obj, nil
115		}
116
117		// if we're explicitly authorized to bind this role, return
118		if rbacregistry.BindingAuthorized(ctx, roleBinding.RoleRef, namespace, s.authorizer) {
119			return obj, nil
120		}
121
122		// Otherwise, see if we already have all the permissions contained in the referenced role
123		v1RoleRef := rbacv1.RoleRef{}
124		err := rbacv1helpers.Convert_rbac_RoleRef_To_v1_RoleRef(&roleBinding.RoleRef, &v1RoleRef, nil)
125		if err != nil {
126			return nil, err
127		}
128		rules, err := s.ruleResolver.GetRoleReferenceRules(v1RoleRef, namespace)
129		if err != nil {
130			return nil, err
131		}
132		if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
133			return nil, errors.NewForbidden(groupResource, roleBinding.Name, err)
134		}
135		return obj, nil
136	})
137
138	return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation, forceAllowCreate, options)
139}
140