2016-07-27 14:29:31 +00:00
/ *
Copyright 2016 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 .
* /
2016-09-23 19:10:47 +00:00
package rest
2016-07-27 14:29:31 +00:00
import (
2016-08-26 15:06:27 +00:00
"fmt"
2016-07-27 14:29:31 +00:00
"sync"
2016-12-22 13:19:11 +00:00
"time"
2016-07-27 14:29:31 +00:00
2016-08-26 15:06:27 +00:00
"github.com/golang/glog"
2017-06-22 18:24:23 +00:00
rbacapiv1alpha1 "k8s.io/api/rbac/v1alpha1"
rbacapiv1beta1 "k8s.io/api/rbac/v1beta1"
2017-02-27 20:09:27 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2017-01-09 17:24:21 +00:00
"k8s.io/apimachinery/pkg/runtime/schema"
2017-01-11 14:09:48 +00:00
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
2017-01-09 14:36:17 +00:00
"k8s.io/apiserver/pkg/authorization/authorizer"
2017-02-02 09:25:56 +00:00
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
2017-02-11 11:26:43 +00:00
serverstorage "k8s.io/apiserver/pkg/server/storage"
2017-02-02 07:30:11 +00:00
"k8s.io/kubernetes/pkg/api"
2016-07-27 14:29:31 +00:00
"k8s.io/kubernetes/pkg/apis/rbac"
2017-07-04 23:14:41 +00:00
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
2016-10-21 22:24:05 +00:00
rbacclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion"
2017-02-07 20:06:27 +00:00
"k8s.io/kubernetes/pkg/client/retry"
2016-09-15 17:41:48 +00:00
"k8s.io/kubernetes/pkg/registry/rbac/clusterrole"
clusterrolepolicybased "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/policybased"
2016-11-07 19:45:16 +00:00
clusterrolestore "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/storage"
2016-09-15 17:41:48 +00:00
"k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding"
clusterrolebindingpolicybased "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/policybased"
2016-11-07 19:45:16 +00:00
clusterrolebindingstore "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/storage"
2017-02-07 20:06:27 +00:00
"k8s.io/kubernetes/pkg/registry/rbac/reconciliation"
2016-09-15 17:41:48 +00:00
"k8s.io/kubernetes/pkg/registry/rbac/role"
rolepolicybased "k8s.io/kubernetes/pkg/registry/rbac/role/policybased"
2016-11-07 19:45:16 +00:00
rolestore "k8s.io/kubernetes/pkg/registry/rbac/role/storage"
2016-09-15 17:41:48 +00:00
"k8s.io/kubernetes/pkg/registry/rbac/rolebinding"
rolebindingpolicybased "k8s.io/kubernetes/pkg/registry/rbac/rolebinding/policybased"
2016-11-07 19:45:16 +00:00
rolebindingstore "k8s.io/kubernetes/pkg/registry/rbac/rolebinding/storage"
2017-01-11 10:11:25 +00:00
rbacregistryvalidation "k8s.io/kubernetes/pkg/registry/rbac/validation"
2016-08-26 15:06:27 +00:00
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy"
2016-07-27 14:29:31 +00:00
)
2017-03-31 01:40:51 +00:00
const PostStartHookName = "rbac/bootstrap-roles"
2017-01-09 14:36:17 +00:00
type RESTStorageProvider struct {
Authorizer authorizer . Authorizer
}
2016-07-27 14:29:31 +00:00
2016-10-27 18:24:11 +00:00
var _ genericapiserver . PostStartHookProvider = RESTStorageProvider { }
2016-07-27 14:29:31 +00:00
2017-02-11 11:26:43 +00:00
func ( p RESTStorageProvider ) NewRESTStorage ( apiResourceConfigSource serverstorage . APIResourceConfigSource , restOptionsGetter generic . RESTOptionsGetter ) ( genericapiserver . APIGroupInfo , bool ) {
2017-02-02 07:30:11 +00:00
apiGroupInfo := genericapiserver . NewDefaultAPIGroupInfo ( rbac . GroupName , api . Registry , api . Scheme , api . ParameterCodec , api . Codecs )
2017-06-09 20:34:04 +00:00
// If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities.
// TODO refactor the plumbing to provide the information in the APIGroupInfo
2016-07-27 14:29:31 +00:00
if apiResourceConfigSource . AnyResourcesForVersionEnabled ( rbacapiv1alpha1 . SchemeGroupVersion ) {
2017-01-09 17:24:21 +00:00
apiGroupInfo . VersionedResourcesStorageMap [ rbacapiv1alpha1 . SchemeGroupVersion . Version ] = p . storage ( rbacapiv1alpha1 . SchemeGroupVersion , apiResourceConfigSource , restOptionsGetter )
2016-07-27 14:29:31 +00:00
apiGroupInfo . GroupMeta . GroupVersion = rbacapiv1alpha1 . SchemeGroupVersion
}
2017-01-09 17:24:21 +00:00
if apiResourceConfigSource . AnyResourcesForVersionEnabled ( rbacapiv1beta1 . SchemeGroupVersion ) {
apiGroupInfo . VersionedResourcesStorageMap [ rbacapiv1beta1 . SchemeGroupVersion . Version ] = p . storage ( rbacapiv1beta1 . SchemeGroupVersion , apiResourceConfigSource , restOptionsGetter )
apiGroupInfo . GroupMeta . GroupVersion = rbacapiv1beta1 . SchemeGroupVersion
}
2016-07-27 14:29:31 +00:00
return apiGroupInfo , true
}
2017-02-11 11:26:43 +00:00
func ( p RESTStorageProvider ) storage ( version schema . GroupVersion , apiResourceConfigSource serverstorage . APIResourceConfigSource , restOptionsGetter generic . RESTOptionsGetter ) map [ string ] rest . Storage {
2016-07-27 14:29:31 +00:00
once := new ( sync . Once )
2016-12-01 02:09:56 +00:00
var (
2017-01-11 10:11:25 +00:00
authorizationRuleResolver rbacregistryvalidation . AuthorizationRuleResolver
2016-12-01 02:09:56 +00:00
rolesStorage rest . StandardStorage
roleBindingsStorage rest . StandardStorage
clusterRolesStorage rest . StandardStorage
clusterRoleBindingsStorage rest . StandardStorage
)
initializeStorage := func ( ) {
2016-07-27 14:29:31 +00:00
once . Do ( func ( ) {
2016-11-07 19:45:16 +00:00
rolesStorage = rolestore . NewREST ( restOptionsGetter )
roleBindingsStorage = rolebindingstore . NewREST ( restOptionsGetter )
clusterRolesStorage = clusterrolestore . NewREST ( restOptionsGetter )
clusterRoleBindingsStorage = clusterrolebindingstore . NewREST ( restOptionsGetter )
2016-12-01 02:09:56 +00:00
2017-01-11 10:11:25 +00:00
authorizationRuleResolver = rbacregistryvalidation . NewDefaultRuleResolver (
2016-12-01 02:09:56 +00:00
role . AuthorizerAdapter { Registry : role . NewRegistry ( rolesStorage ) } ,
rolebinding . AuthorizerAdapter { Registry : rolebinding . NewRegistry ( roleBindingsStorage ) } ,
clusterrole . AuthorizerAdapter { Registry : clusterrole . NewRegistry ( clusterRolesStorage ) } ,
clusterrolebinding . AuthorizerAdapter { Registry : clusterrolebinding . NewRegistry ( clusterRoleBindingsStorage ) } ,
2016-07-27 14:29:31 +00:00
)
} )
}
storage := map [ string ] rest . Storage { }
if apiResourceConfigSource . ResourceEnabled ( version . WithResource ( "roles" ) ) {
2016-12-01 02:09:56 +00:00
initializeStorage ( )
storage [ "roles" ] = rolepolicybased . NewStorage ( rolesStorage , authorizationRuleResolver )
2016-07-27 14:29:31 +00:00
}
if apiResourceConfigSource . ResourceEnabled ( version . WithResource ( "rolebindings" ) ) {
2016-12-01 02:09:56 +00:00
initializeStorage ( )
2017-01-09 14:36:17 +00:00
storage [ "rolebindings" ] = rolebindingpolicybased . NewStorage ( roleBindingsStorage , p . Authorizer , authorizationRuleResolver )
2016-07-27 14:29:31 +00:00
}
if apiResourceConfigSource . ResourceEnabled ( version . WithResource ( "clusterroles" ) ) {
2016-12-01 02:09:56 +00:00
initializeStorage ( )
storage [ "clusterroles" ] = clusterrolepolicybased . NewStorage ( clusterRolesStorage , authorizationRuleResolver )
2016-07-27 14:29:31 +00:00
}
if apiResourceConfigSource . ResourceEnabled ( version . WithResource ( "clusterrolebindings" ) ) {
2016-12-01 02:09:56 +00:00
initializeStorage ( )
2017-01-09 14:36:17 +00:00
storage [ "clusterrolebindings" ] = clusterrolebindingpolicybased . NewStorage ( clusterRoleBindingsStorage , p . Authorizer , authorizationRuleResolver )
2016-07-27 14:29:31 +00:00
}
return storage
}
2016-08-26 15:06:27 +00:00
2016-10-27 18:24:11 +00:00
func ( p RESTStorageProvider ) PostStartHook ( ) ( string , genericapiserver . PostStartHookFunc , error ) {
2017-03-31 01:40:51 +00:00
return PostStartHookName , PostStartHook , nil
2016-08-26 15:06:27 +00:00
}
2016-09-29 19:10:04 +00:00
func PostStartHook ( hookContext genericapiserver . PostStartHookContext ) error {
2016-12-22 13:19:11 +00:00
// intializing roles is really important. On some e2e runs, we've seen cases where etcd is down when the server
// starts, the roles don't initialize, and nothing works.
err := wait . Poll ( 1 * time . Second , 30 * time . Second , func ( ) ( done bool , err error ) {
2017-07-04 23:14:41 +00:00
coreclientset , err := coreclient . NewForConfig ( hookContext . LoopbackClientConfig )
if err != nil {
utilruntime . HandleError ( fmt . Errorf ( "unable to initialize client: %v" , err ) )
return false , nil
}
2016-12-22 13:19:11 +00:00
clientset , err := rbacclient . NewForConfig ( hookContext . LoopbackClientConfig )
if err != nil {
2017-02-27 20:09:27 +00:00
utilruntime . HandleError ( fmt . Errorf ( "unable to initialize client: %v" , err ) )
return false , nil
}
// Make sure etcd is responding before we start reconciling
if _ , err := clientset . ClusterRoles ( ) . List ( metav1 . ListOptions { } ) ; err != nil {
2016-09-29 19:10:04 +00:00
utilruntime . HandleError ( fmt . Errorf ( "unable to initialize clusterroles: %v" , err ) )
2016-12-22 13:19:11 +00:00
return false , nil
2016-08-26 15:06:27 +00:00
}
2017-02-27 20:09:27 +00:00
if _ , err := clientset . ClusterRoleBindings ( ) . List ( metav1 . ListOptions { } ) ; err != nil {
utilruntime . HandleError ( fmt . Errorf ( "unable to initialize clusterrolebindings: %v" , err ) )
return false , nil
}
2016-09-29 19:10:04 +00:00
2017-02-07 20:06:27 +00:00
// ensure bootstrap roles are created or reconciled
for _ , clusterRole := range append ( bootstrappolicy . ClusterRoles ( ) , bootstrappolicy . ControllerRoles ( ) ... ) {
2017-02-28 16:05:57 +00:00
opts := reconciliation . ReconcileRoleOptions {
2017-02-22 20:57:17 +00:00
Role : reconciliation . ClusterRoleRuleOwner { ClusterRole : & clusterRole } ,
Client : reconciliation . ClusterRoleModifier { Client : clientset . ClusterRoles ( ) } ,
2017-02-07 20:06:27 +00:00
Confirm : true ,
}
err := retry . RetryOnConflict ( retry . DefaultBackoff , func ( ) error {
result , err := opts . Run ( )
if err != nil {
return err
}
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 )
case result . Operation == reconciliation . ReconcileUpdate :
glog . 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 )
2016-12-22 13:19:11 +00:00
}
2017-02-07 20:06:27 +00:00
return nil
} )
if err != nil {
// don't fail on failures, try to create as many as you can
utilruntime . HandleError ( fmt . Errorf ( "unable to reconcile clusterrole.%s/%s: %v" , rbac . GroupName , clusterRole . Name , err ) )
2016-12-22 13:19:11 +00:00
}
}
2016-10-04 14:34:01 +00:00
2017-02-07 20:06:27 +00:00
// ensure bootstrap rolebindings are created or reconciled
for _ , clusterRoleBinding := range append ( bootstrappolicy . ClusterRoleBindings ( ) , bootstrappolicy . ControllerRoleBindings ( ) ... ) {
2017-02-28 16:05:57 +00:00
opts := reconciliation . ReconcileRoleBindingOptions {
RoleBinding : reconciliation . ClusterRoleBindingAdapter { ClusterRoleBinding : & clusterRoleBinding } ,
Client : reconciliation . ClusterRoleBindingClientAdapter { Client : clientset . ClusterRoleBindings ( ) } ,
2017-02-07 20:06:27 +00:00
Confirm : true ,
}
err := retry . RetryOnConflict ( retry . DefaultBackoff , func ( ) error {
result , err := opts . Run ( )
if err != nil {
return err
}
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 )
case result . Operation == reconciliation . ReconcileUpdate :
glog . 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 )
case result . Operation == reconciliation . ReconcileRecreate :
glog . Infof ( "recreated clusterrolebinding.%s/%s" , rbac . GroupName , clusterRoleBinding . Name )
2016-12-22 13:19:11 +00:00
}
2017-02-07 20:06:27 +00:00
return nil
} )
if err != nil {
// don't fail on failures, try to create as many as you can
utilruntime . HandleError ( fmt . Errorf ( "unable to reconcile clusterrolebinding.%s/%s: %v" , rbac . GroupName , clusterRoleBinding . Name , err ) )
2016-12-22 13:19:11 +00:00
}
2016-10-04 14:34:01 +00:00
}
2016-12-22 13:19:11 +00:00
2017-02-23 16:45:49 +00:00
// ensure bootstrap namespaced roles are created or reconciled
for namespace , roles := range bootstrappolicy . NamespaceRoles ( ) {
for _ , role := range roles {
2017-02-28 16:05:57 +00:00
opts := reconciliation . ReconcileRoleOptions {
2017-02-23 16:45:49 +00:00
Role : reconciliation . RoleRuleOwner { Role : & role } ,
2017-07-04 23:14:41 +00:00
Client : reconciliation . RoleModifier { Client : clientset , NamespaceClient : coreclientset . Namespaces ( ) } ,
2017-02-23 16:45:49 +00:00
Confirm : true ,
}
err := retry . RetryOnConflict ( retry . DefaultBackoff , func ( ) error {
result , err := opts . Run ( )
if err != nil {
return err
}
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 )
case result . Operation == reconciliation . ReconcileUpdate :
glog . 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 )
}
return nil
} )
if err != nil {
// don't fail on failures, try to create as many as you can
utilruntime . HandleError ( fmt . Errorf ( "unable to reconcile role.%s/%s in %v: %v" , rbac . GroupName , role . Name , namespace , err ) )
}
}
}
2017-02-28 16:11:32 +00:00
// ensure bootstrap namespaced rolebindings are created or reconciled
for namespace , roleBindings := range bootstrappolicy . NamespaceRoleBindings ( ) {
for _ , roleBinding := range roleBindings {
opts := reconciliation . ReconcileRoleBindingOptions {
RoleBinding : reconciliation . RoleBindingAdapter { RoleBinding : & roleBinding } ,
2017-07-04 23:14:41 +00:00
Client : reconciliation . RoleBindingClientAdapter { Client : clientset , NamespaceClient : coreclientset . Namespaces ( ) } ,
2017-02-28 16:11:32 +00:00
Confirm : true ,
}
err := retry . RetryOnConflict ( retry . DefaultBackoff , func ( ) error {
result , err := opts . Run ( )
if err != nil {
return err
}
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 )
case result . Operation == reconciliation . ReconcileUpdate :
glog . 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 )
case result . Operation == reconciliation . ReconcileRecreate :
glog . Infof ( "recreated rolebinding.%s/%s in %v" , rbac . GroupName , roleBinding . Name , namespace )
}
return nil
} )
if err != nil {
// don't fail on failures, try to create as many as you can
utilruntime . HandleError ( fmt . Errorf ( "unable to reconcile rolebinding.%s/%s in %v: %v" , rbac . GroupName , roleBinding . Name , namespace , err ) )
}
}
}
2016-12-22 13:19:11 +00:00
return true , nil
} )
// if we're never able to make it through intialization, kill the API server
if err != nil {
return fmt . Errorf ( "unable to initialize roles: %v" , err )
2016-10-04 14:34:01 +00:00
}
2016-09-29 19:10:04 +00:00
return nil
2016-08-26 15:06:27 +00:00
}
2016-10-27 18:24:11 +00:00
func ( p RESTStorageProvider ) GroupName ( ) string {
return rbac . GroupName
}