func BuildMasterConfig(options configapi.MasterConfig) (*MasterConfig, error) { client, err := etcd.EtcdClient(options.EtcdClientInfo) if err != nil { return nil, err } etcdHelper, err := NewEtcdStorage(client, options.EtcdStorageConfig.OpenShiftStorageVersion, options.EtcdStorageConfig.OpenShiftStoragePrefix) if err != nil { return nil, fmt.Errorf("Error setting up server storage: %v", err) } clientCAs, err := configapi.GetClientCertCAPool(options) if err != nil { return nil, err } apiClientCAs, err := configapi.GetAPIClientCertCAPool(options) if err != nil { return nil, err } privilegedLoopbackKubeClient, _, err := configapi.GetKubeClient(options.MasterClients.OpenShiftLoopbackKubeConfig) if err != nil { return nil, err } privilegedLoopbackOpenShiftClient, privilegedLoopbackClientConfig, err := configapi.GetOpenShiftClient(options.MasterClients.OpenShiftLoopbackKubeConfig) if err != nil { return nil, err } imageTemplate := variable.NewDefaultImageTemplate() imageTemplate.Format = options.ImageConfig.Format imageTemplate.Latest = options.ImageConfig.Latest policyCache, policyClient := newReadOnlyCacheAndClient(etcdHelper) requestContextMapper := kapi.NewRequestContextMapper() groupCache := usercache.NewGroupCache(groupregistry.NewRegistry(groupstorage.NewREST(etcdHelper))) kubeletClientConfig := configapi.GetKubeletClientConfig(options) // in-order list of plug-ins that should intercept admission decisions (origin only intercepts) admissionControlPluginNames := []string{"OriginNamespaceLifecycle", "BuildByStrategy"} admissionClient := admissionControlClient(privilegedLoopbackKubeClient, privilegedLoopbackOpenShiftClient) admissionController := admission.NewFromPlugins(admissionClient, admissionControlPluginNames, "") serviceAccountTokenGetter, err := newServiceAccountTokenGetter(options, client) if err != nil { return nil, err } plug, plugStart := newControllerPlug(options, client) config := &MasterConfig{ Options: options, Authenticator: newAuthenticator(options, etcdHelper, serviceAccountTokenGetter, apiClientCAs, groupCache), Authorizer: newAuthorizer(policyClient, options.ProjectConfig.ProjectRequestMessage), AuthorizationAttributeBuilder: newAuthorizationAttributeBuilder(requestContextMapper), PolicyCache: policyCache, GroupCache: groupCache, ProjectAuthorizationCache: newProjectAuthorizationCache(privilegedLoopbackOpenShiftClient, privilegedLoopbackKubeClient, policyClient), RequestContextMapper: requestContextMapper, AdmissionControl: admissionController, TLS: configapi.UseTLS(options.ServingInfo.ServingInfo), ControllerPlug: plug, ControllerPlugStart: plugStart, ImageFor: imageTemplate.ExpandOrDie, EtcdHelper: etcdHelper, EtcdClient: client, KubeletClientConfig: kubeletClientConfig, ClientCAs: clientCAs, APIClientCAs: apiClientCAs, PrivilegedLoopbackClientConfig: *privilegedLoopbackClientConfig, PrivilegedLoopbackOpenShiftClient: privilegedLoopbackOpenShiftClient, PrivilegedLoopbackKubernetesClient: privilegedLoopbackKubeClient, BuildControllerServiceAccount: bootstrappolicy.InfraBuildControllerServiceAccountName, DeploymentControllerServiceAccount: bootstrappolicy.InfraDeploymentControllerServiceAccountName, ReplicationControllerServiceAccount: bootstrappolicy.InfraReplicationControllerServiceAccountName, } return config, nil }
// BuildMasterConfig builds and returns the OpenShift master configuration based on the // provided options func BuildMasterConfig(options configapi.MasterConfig) (*MasterConfig, error) { client, err := etcd.EtcdClient(options.EtcdClientInfo) if err != nil { return nil, err } etcdClient, err := etcd.MakeNewEtcdClient(options.EtcdClientInfo) if err != nil { return nil, err } groupVersion := unversioned.GroupVersion{Group: "", Version: options.EtcdStorageConfig.OpenShiftStorageVersion} etcdHelper, err := NewEtcdStorage(etcdClient, groupVersion, options.EtcdStorageConfig.OpenShiftStoragePrefix) if err != nil { return nil, fmt.Errorf("Error setting up server storage: %v", err) } restOptsGetter := restoptions.NewConfigGetter(options) clientCAs, err := configapi.GetClientCertCAPool(options) if err != nil { return nil, err } apiClientCAs, err := configapi.GetAPIClientCertCAPool(options) if err != nil { return nil, err } privilegedLoopbackKubeClient, _, err := configapi.GetKubeClient(options.MasterClients.OpenShiftLoopbackKubeConfig) if err != nil { return nil, err } privilegedLoopbackOpenShiftClient, privilegedLoopbackClientConfig, err := configapi.GetOpenShiftClient(options.MasterClients.OpenShiftLoopbackKubeConfig) if err != nil { return nil, err } customListerWatchers := shared.DefaultListerWatcherOverrides{} if err := addAuthorizationListerWatchers(customListerWatchers, restOptsGetter); err != nil { return nil, err } informerFactory := shared.NewInformerFactory(privilegedLoopbackKubeClient, privilegedLoopbackOpenShiftClient, customListerWatchers, 10*time.Minute) imageTemplate := variable.NewDefaultImageTemplate() imageTemplate.Format = options.ImageConfig.Format imageTemplate.Latest = options.ImageConfig.Latest requestContextMapper := kapi.NewRequestContextMapper() groupStorage, err := groupstorage.NewREST(restOptsGetter) if err != nil { return nil, err } groupCache := usercache.NewGroupCache(groupregistry.NewRegistry(groupStorage)) projectCache := projectcache.NewProjectCache(privilegedLoopbackKubeClient.Namespaces(), options.ProjectConfig.DefaultNodeSelector) clusterQuotaMappingController := clusterquotamapping.NewClusterQuotaMappingController(informerFactory.Namespaces(), informerFactory.ClusterResourceQuotas()) kubeletClientConfig := configapi.GetKubeletClientConfig(options) // in-order list of plug-ins that should intercept admission decisions (origin only intercepts) admissionControlPluginNames := []string{ "ProjectRequestLimit", "OriginNamespaceLifecycle", "PodNodeConstraints", "JenkinsBootstrapper", "BuildByStrategy", imageadmission.PluginName, quotaadmission.PluginName, } if len(options.AdmissionConfig.PluginOrderOverride) > 0 { admissionControlPluginNames = options.AdmissionConfig.PluginOrderOverride } quotaRegistry := quota.NewOriginQuotaRegistry(privilegedLoopbackOpenShiftClient) ruleResolver := rulevalidation.NewDefaultRuleResolver( informerFactory.Policies().Lister(), informerFactory.PolicyBindings().Lister(), informerFactory.ClusterPolicies().Lister().ClusterPolicies(), informerFactory.ClusterPolicyBindings().Lister().ClusterPolicyBindings(), ) authorizer := newAuthorizer(ruleResolver, informerFactory, options.ProjectConfig.ProjectRequestMessage) pluginInitializer := oadmission.PluginInitializer{ OpenshiftClient: privilegedLoopbackOpenShiftClient, ProjectCache: projectCache, OriginQuotaRegistry: quotaRegistry, Authorizer: authorizer, JenkinsPipelineConfig: options.JenkinsPipelineConfig, RESTClientConfig: *privilegedLoopbackClientConfig, } plugins := []admission.Interface{} clientsetClient := clientadapter.FromUnversionedClient(privilegedLoopbackKubeClient) for _, pluginName := range admissionControlPluginNames { configFile, err := pluginconfig.GetPluginConfig(options.AdmissionConfig.PluginConfig[pluginName]) if err != nil { return nil, err } plugin := admission.InitPlugin(pluginName, clientsetClient, configFile) if plugin != nil { plugins = append(plugins, plugin) } } pluginInitializer.Initialize(plugins) // ensure that plugins have been properly initialized if err := oadmission.Validate(plugins); err != nil { return nil, err } admissionController := admission.NewChainHandler(plugins...) // TODO: look up storage by resource serviceAccountTokenGetter, err := newServiceAccountTokenGetter(options, etcdClient) if err != nil { return nil, err } authenticator, err := newAuthenticator(options, restOptsGetter, serviceAccountTokenGetter, apiClientCAs, groupCache) if err != nil { return nil, err } plug, plugStart := newControllerPlug(options, client) config := &MasterConfig{ Options: options, RESTOptionsGetter: restOptsGetter, RuleResolver: ruleResolver, Authenticator: authenticator, Authorizer: authorizer, AuthorizationAttributeBuilder: newAuthorizationAttributeBuilder(requestContextMapper), GroupCache: groupCache, ProjectAuthorizationCache: newProjectAuthorizationCache(authorizer, privilegedLoopbackKubeClient, informerFactory), ProjectCache: projectCache, ClusterQuotaMappingController: clusterQuotaMappingController, RequestContextMapper: requestContextMapper, AdmissionControl: admissionController, TLS: configapi.UseTLS(options.ServingInfo.ServingInfo), ControllerPlug: plug, ControllerPlugStart: plugStart, ImageFor: imageTemplate.ExpandOrDie, EtcdHelper: etcdHelper, KubeletClientConfig: kubeletClientConfig, ClientCAs: clientCAs, APIClientCAs: apiClientCAs, PluginInitializer: pluginInitializer, PrivilegedLoopbackClientConfig: *privilegedLoopbackClientConfig, PrivilegedLoopbackOpenShiftClient: privilegedLoopbackOpenShiftClient, PrivilegedLoopbackKubernetesClient: privilegedLoopbackKubeClient, Informers: informerFactory, } return config, nil }
// BuildMasterConfig builds and returns the OpenShift master configuration based on the // provided options func BuildMasterConfig(options configapi.MasterConfig) (*MasterConfig, error) { client, err := etcd.EtcdClient(options.EtcdClientInfo) if err != nil { return nil, err } etcdClient, err := etcd.MakeNewEtcdClient(options.EtcdClientInfo) if err != nil { return nil, err } groupVersion := unversioned.GroupVersion{Group: "", Version: options.EtcdStorageConfig.OpenShiftStorageVersion} etcdHelper, err := NewEtcdStorage(etcdClient, groupVersion, options.EtcdStorageConfig.OpenShiftStoragePrefix) if err != nil { return nil, fmt.Errorf("Error setting up server storage: %v", err) } clientCAs, err := configapi.GetClientCertCAPool(options) if err != nil { return nil, err } apiClientCAs, err := configapi.GetAPIClientCertCAPool(options) if err != nil { return nil, err } privilegedLoopbackKubeClient, _, err := configapi.GetKubeClient(options.MasterClients.OpenShiftLoopbackKubeConfig) if err != nil { return nil, err } privilegedLoopbackOpenShiftClient, privilegedLoopbackClientConfig, err := configapi.GetOpenShiftClient(options.MasterClients.OpenShiftLoopbackKubeConfig) if err != nil { return nil, err } imageTemplate := variable.NewDefaultImageTemplate() imageTemplate.Format = options.ImageConfig.Format imageTemplate.Latest = options.ImageConfig.Latest policyCache, policyClient := newReadOnlyCacheAndClient(etcdHelper) requestContextMapper := kapi.NewRequestContextMapper() groupCache := usercache.NewGroupCache(groupregistry.NewRegistry(groupstorage.NewREST(etcdHelper))) projectCache := projectcache.NewProjectCache(privilegedLoopbackKubeClient.Namespaces(), options.ProjectConfig.DefaultNodeSelector) kubeletClientConfig := configapi.GetKubeletClientConfig(options) // in-order list of plug-ins that should intercept admission decisions (origin only intercepts) admissionControlPluginNames := []string{"OriginNamespaceLifecycle", "BuildByStrategy"} if len(options.AdmissionConfig.PluginOrderOverride) > 0 { admissionControlPluginNames = options.AdmissionConfig.PluginOrderOverride } pluginInitializer := oadmission.PluginInitializer{ OpenshiftClient: privilegedLoopbackOpenShiftClient, ProjectCache: projectCache, } plugins := []admission.Interface{} for _, pluginName := range admissionControlPluginNames { configFile, err := pluginconfig.GetPluginConfig(options.AdmissionConfig.PluginConfig[pluginName]) if err != nil { return nil, err } plugin := admission.InitPlugin(pluginName, privilegedLoopbackKubeClient, configFile) if plugin != nil { plugins = append(plugins, plugin) } } pluginInitializer.Initialize(plugins) // ensure that plugins have been properly initialized if err := oadmission.Validate(plugins); err != nil { return nil, err } admissionController := admission.NewChainHandler(plugins...) serviceAccountTokenGetter, err := newServiceAccountTokenGetter(options, etcdClient) if err != nil { return nil, err } plug, plugStart := newControllerPlug(options, client) authorizer := newAuthorizer(policyClient, options.ProjectConfig.ProjectRequestMessage) config := &MasterConfig{ Options: options, Authenticator: newAuthenticator(options, etcdHelper, serviceAccountTokenGetter, apiClientCAs, groupCache), Authorizer: authorizer, AuthorizationAttributeBuilder: newAuthorizationAttributeBuilder(requestContextMapper), PolicyCache: policyCache, GroupCache: groupCache, ProjectAuthorizationCache: newProjectAuthorizationCache(authorizer, privilegedLoopbackKubeClient, policyClient), ProjectCache: projectCache, RequestContextMapper: requestContextMapper, AdmissionControl: admissionController, TLS: configapi.UseTLS(options.ServingInfo.ServingInfo), ControllerPlug: plug, ControllerPlugStart: plugStart, ImageFor: imageTemplate.ExpandOrDie, EtcdHelper: etcdHelper, KubeletClientConfig: kubeletClientConfig, ClientCAs: clientCAs, APIClientCAs: apiClientCAs, PrivilegedLoopbackClientConfig: *privilegedLoopbackClientConfig, PrivilegedLoopbackOpenShiftClient: privilegedLoopbackOpenShiftClient, PrivilegedLoopbackKubernetesClient: privilegedLoopbackKubeClient, } return config, nil }
// BuildMasterConfig builds and returns the OpenShift master configuration based on the // provided options func BuildMasterConfig(options configapi.MasterConfig) (*MasterConfig, error) { client, err := etcd.EtcdClient(options.EtcdClientInfo) if err != nil { return nil, err } etcdClient, err := etcd.MakeNewEtcdClient(options.EtcdClientInfo) if err != nil { return nil, err } groupVersion := unversioned.GroupVersion{Group: "", Version: options.EtcdStorageConfig.OpenShiftStorageVersion} etcdHelper, err := NewEtcdStorage(etcdClient, groupVersion, options.EtcdStorageConfig.OpenShiftStoragePrefix) if err != nil { return nil, fmt.Errorf("Error setting up server storage: %v", err) } restOptsGetter := restoptions.NewConfigGetter(options) clientCAs, err := configapi.GetClientCertCAPool(options) if err != nil { return nil, err } apiClientCAs, err := configapi.GetAPIClientCertCAPool(options) if err != nil { return nil, err } privilegedLoopbackKubeClient, _, err := configapi.GetKubeClient(options.MasterClients.OpenShiftLoopbackKubeConfig) if err != nil { return nil, err } privilegedLoopbackOpenShiftClient, privilegedLoopbackClientConfig, err := configapi.GetOpenShiftClient(options.MasterClients.OpenShiftLoopbackKubeConfig) if err != nil { return nil, err } customListerWatchers := shared.DefaultListerWatcherOverrides{} if err := addAuthorizationListerWatchers(customListerWatchers, restOptsGetter); err != nil { return nil, err } informerFactory := shared.NewInformerFactory(privilegedLoopbackKubeClient, privilegedLoopbackOpenShiftClient, customListerWatchers, 10*time.Minute) imageTemplate := variable.NewDefaultImageTemplate() imageTemplate.Format = options.ImageConfig.Format imageTemplate.Latest = options.ImageConfig.Latest requestContextMapper := kapi.NewRequestContextMapper() groupStorage, err := groupstorage.NewREST(restOptsGetter) if err != nil { return nil, err } groupCache := usercache.NewGroupCache(groupregistry.NewRegistry(groupStorage)) projectCache := projectcache.NewProjectCache(privilegedLoopbackKubeClient.Namespaces(), options.ProjectConfig.DefaultNodeSelector) clusterQuotaMappingController := clusterquotamapping.NewClusterQuotaMappingController(informerFactory.Namespaces(), informerFactory.ClusterResourceQuotas()) kubeletClientConfig := configapi.GetKubeletClientConfig(options) kubeClientSet := clientadapter.FromUnversionedClient(privilegedLoopbackKubeClient) quotaRegistry := quota.NewAllResourceQuotaRegistry(privilegedLoopbackOpenShiftClient, kubeClientSet) ruleResolver := rulevalidation.NewDefaultRuleResolver( informerFactory.Policies().Lister(), informerFactory.PolicyBindings().Lister(), informerFactory.ClusterPolicies().Lister().ClusterPolicies(), informerFactory.ClusterPolicyBindings().Lister().ClusterPolicyBindings(), ) authorizer := newAuthorizer(ruleResolver, informerFactory, options.ProjectConfig.ProjectRequestMessage) pluginInitializer := oadmission.PluginInitializer{ OpenshiftClient: privilegedLoopbackOpenShiftClient, ProjectCache: projectCache, OriginQuotaRegistry: quotaRegistry, Authorizer: authorizer, JenkinsPipelineConfig: options.JenkinsPipelineConfig, RESTClientConfig: *privilegedLoopbackClientConfig, Informers: informerFactory, ClusterQuotaMapper: clusterQuotaMappingController.GetClusterQuotaMapper(), } originAdmission, kubeAdmission, err := buildAdmissionChains(options, kubeClientSet, pluginInitializer) // TODO: look up storage by resource serviceAccountTokenGetter, err := newServiceAccountTokenGetter(options, etcdClient) if err != nil { return nil, err } authenticator, err := newAuthenticator(options, restOptsGetter, serviceAccountTokenGetter, apiClientCAs, groupCache) if err != nil { return nil, err } plug, plugStart := newControllerPlug(options, client) config := &MasterConfig{ Options: options, RESTOptionsGetter: restOptsGetter, RuleResolver: ruleResolver, Authenticator: authenticator, Authorizer: authorizer, AuthorizationAttributeBuilder: newAuthorizationAttributeBuilder(requestContextMapper), GroupCache: groupCache, ProjectAuthorizationCache: newProjectAuthorizationCache(authorizer, privilegedLoopbackKubeClient, informerFactory), ProjectCache: projectCache, ClusterQuotaMappingController: clusterQuotaMappingController, RequestContextMapper: requestContextMapper, AdmissionControl: originAdmission, KubeAdmissionControl: kubeAdmission, TLS: configapi.UseTLS(options.ServingInfo.ServingInfo), ControllerPlug: plug, ControllerPlugStart: plugStart, ImageFor: imageTemplate.ExpandOrDie, EtcdHelper: etcdHelper, KubeletClientConfig: kubeletClientConfig, ClientCAs: clientCAs, APIClientCAs: apiClientCAs, PrivilegedLoopbackClientConfig: *privilegedLoopbackClientConfig, PrivilegedLoopbackOpenShiftClient: privilegedLoopbackOpenShiftClient, PrivilegedLoopbackKubernetesClient: privilegedLoopbackKubeClient, Informers: informerFactory, } return config, nil }
func TestAdmission(t *testing.T) { var ( userAlice = userapi.User{ ObjectMeta: kapi.ObjectMeta{ Name: "Alice", Labels: map[string]string{"foo": "bar"}, }, } userAliceRef = kapi.ObjectReference{ Kind: authorizationapi.UserKind, Name: "Alice", } userBob = userapi.User{ ObjectMeta: kapi.ObjectMeta{Name: "Bob"}, Groups: []string{"group"}, } userBobRef = kapi.ObjectReference{ Kind: authorizationapi.UserKind, Name: "Bob", } group = userapi.Group{ ObjectMeta: kapi.ObjectMeta{ Name: "group", Labels: map[string]string{"baz": "quux"}, }, Users: []string{userBobRef.Name}, } groupRef = kapi.ObjectReference{ Kind: authorizationapi.GroupKind, Name: "group", } serviceaccount = kapi.ServiceAccount{ ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "serviceaccount", Labels: map[string]string{"xyzzy": "thud"}, }, } serviceaccountRef = kapi.ObjectReference{ Kind: authorizationapi.ServiceAccountKind, Namespace: "namespace", Name: "serviceaccount", } systemuserRef = kapi.ObjectReference{ Kind: authorizationapi.SystemUserKind, Name: "system user", } systemgroupRef = kapi.ObjectReference{ Kind: authorizationapi.SystemGroupKind, Name: "system group", } ) testCases := []struct { name string expectedErr string object runtime.Object oldObject runtime.Object kind unversioned.GroupVersionKind resource unversioned.GroupVersionResource namespace string subresource string objects []runtime.Object }{ { name: "ignore (allow) if subresource is nonempty", object: &authorizationapi.RoleBinding{ ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "rolebinding", }, Subjects: []kapi.ObjectReference{userAliceRef}, }, oldObject: &authorizationapi.RoleBinding{ ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "rolebinding", }, Subjects: []kapi.ObjectReference{}, }, kind: authorizationapi.Kind("RoleBinding").WithVersion("version"), resource: authorizationapi.Resource("rolebindings").WithVersion("version"), namespace: "namespace", subresource: "subresource", objects: []runtime.Object{ &kapi.Namespace{ ObjectMeta: kapi.ObjectMeta{ Name: "namespace", }, }, }, }, { name: "ignore (allow) cluster-scoped rolebinding", object: &authorizationapi.RoleBinding{ ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "rolebinding", }, Subjects: []kapi.ObjectReference{userAliceRef}, RoleRef: kapi.ObjectReference{Namespace: authorizationapi.PolicyName}, }, oldObject: &authorizationapi.RoleBinding{ ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "rolebinding", }, Subjects: []kapi.ObjectReference{}, }, kind: authorizationapi.Kind("RoleBinding").WithVersion("version"), resource: authorizationapi.Resource("rolebindings").WithVersion("version"), namespace: "", subresource: "", objects: []runtime.Object{ &kapi.Namespace{ ObjectMeta: kapi.ObjectMeta{ Name: "namespace", }, }, }, }, { name: "allow if the namespace has no rolebinding restrictions", object: &authorizationapi.RoleBinding{ ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "rolebinding", }, Subjects: []kapi.ObjectReference{ userAliceRef, userBobRef, groupRef, serviceaccountRef, systemgroupRef, systemuserRef, }, RoleRef: kapi.ObjectReference{Namespace: authorizationapi.PolicyName}, }, oldObject: &authorizationapi.RoleBinding{ ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "rolebinding", }, Subjects: []kapi.ObjectReference{}, RoleRef: kapi.ObjectReference{Namespace: authorizationapi.PolicyName}, }, kind: authorizationapi.Kind("RoleBinding").WithVersion("version"), resource: authorizationapi.Resource("rolebindings").WithVersion("version"), namespace: "namespace", subresource: "", objects: []runtime.Object{ &kapi.Namespace{ ObjectMeta: kapi.ObjectMeta{ Name: "namespace", }, }, }, }, { name: "allow if any rolebinding with the subject already exists", object: &authorizationapi.RoleBinding{ ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "rolebinding", }, Subjects: []kapi.ObjectReference{ userAliceRef, groupRef, serviceaccountRef, }, RoleRef: kapi.ObjectReference{Namespace: authorizationapi.PolicyName}, }, oldObject: &authorizationapi.RoleBinding{ ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "rolebinding", }, Subjects: []kapi.ObjectReference{ userAliceRef, groupRef, serviceaccountRef, }, RoleRef: kapi.ObjectReference{Namespace: authorizationapi.PolicyName}, }, kind: authorizationapi.Kind("RoleBinding").WithVersion("version"), resource: authorizationapi.Resource("rolebindings").WithVersion("version"), namespace: "namespace", subresource: "", objects: []runtime.Object{ &kapi.Namespace{ ObjectMeta: kapi.ObjectMeta{ Name: "namespace", }, }, &authorizationapi.RoleBindingRestriction{ ObjectMeta: kapi.ObjectMeta{ Name: "bogus-matcher", Namespace: "namespace", }, Spec: authorizationapi.RoleBindingRestrictionSpec{ UserRestriction: &authorizationapi.UserRestriction{}, }, }, }, }, { name: "allow a system user, system group, user, group, or service account in a rolebinding if a literal matches", object: &authorizationapi.RoleBinding{ ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "rolebinding", }, Subjects: []kapi.ObjectReference{ systemuserRef, systemgroupRef, userAliceRef, serviceaccountRef, groupRef, }, RoleRef: kapi.ObjectReference{Namespace: authorizationapi.PolicyName}, }, oldObject: &authorizationapi.RoleBinding{ ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "rolebinding", }, Subjects: []kapi.ObjectReference{}, RoleRef: kapi.ObjectReference{Namespace: authorizationapi.PolicyName}, }, kind: authorizationapi.Kind("RoleBinding").WithVersion("version"), resource: authorizationapi.Resource("rolebindings").WithVersion("version"), namespace: "namespace", subresource: "", objects: []runtime.Object{ &kapi.Namespace{ ObjectMeta: kapi.ObjectMeta{ Name: "namespace", }, }, &authorizationapi.RoleBindingRestriction{ ObjectMeta: kapi.ObjectMeta{ Name: "match-system-users", Namespace: "namespace", }, Spec: authorizationapi.RoleBindingRestrictionSpec{ UserRestriction: &authorizationapi.UserRestriction{ Users: []string{systemuserRef.Name}, }, }, }, &authorizationapi.RoleBindingRestriction{ ObjectMeta: kapi.ObjectMeta{ Name: "match-system-groups", Namespace: "namespace", }, Spec: authorizationapi.RoleBindingRestrictionSpec{ GroupRestriction: &authorizationapi.GroupRestriction{ Groups: []string{systemgroupRef.Name}, }, }, }, &authorizationapi.RoleBindingRestriction{ ObjectMeta: kapi.ObjectMeta{ Name: "match-users", Namespace: "namespace", }, Spec: authorizationapi.RoleBindingRestrictionSpec{ UserRestriction: &authorizationapi.UserRestriction{ Users: []string{userAlice.Name}, }, }, }, &authorizationapi.RoleBindingRestriction{ ObjectMeta: kapi.ObjectMeta{ Name: "match-groups", Namespace: "namespace", }, Spec: authorizationapi.RoleBindingRestrictionSpec{ GroupRestriction: &authorizationapi.GroupRestriction{ Groups: []string{group.Name}, }, }, }, &authorizationapi.RoleBindingRestriction{ ObjectMeta: kapi.ObjectMeta{ Name: "match-serviceaccounts", Namespace: "namespace", }, Spec: authorizationapi.RoleBindingRestrictionSpec{ ServiceAccountRestriction: &authorizationapi.ServiceAccountRestriction{ ServiceAccounts: []authorizationapi.ServiceAccountReference{ { Name: serviceaccount.Name, Namespace: serviceaccount.Namespace, }, }, }, }, }, }, }, { name: "prohibit user without a matching user literal", expectedErr: fmt.Sprintf("rolebindings to %s %q are not allowed", userAliceRef.Kind, userAliceRef.Name), object: &authorizationapi.RoleBinding{ ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "rolebinding", }, Subjects: []kapi.ObjectReference{ userAliceRef, }, RoleRef: kapi.ObjectReference{Namespace: authorizationapi.PolicyName}, }, oldObject: &authorizationapi.RoleBinding{ ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "rolebinding", }, Subjects: []kapi.ObjectReference{}, RoleRef: kapi.ObjectReference{Namespace: authorizationapi.PolicyName}, }, kind: authorizationapi.Kind("RoleBinding").WithVersion("version"), resource: authorizationapi.Resource("rolebindings").WithVersion("version"), namespace: "namespace", subresource: "", objects: []runtime.Object{ &kapi.Namespace{ ObjectMeta: kapi.ObjectMeta{ Name: "namespace", }, }, &userAlice, &userBob, &authorizationapi.RoleBindingRestriction{ ObjectMeta: kapi.ObjectMeta{ Name: "match-users-bob", Namespace: "namespace", }, Spec: authorizationapi.RoleBindingRestrictionSpec{ UserRestriction: &authorizationapi.UserRestriction{ Users: []string{userBobRef.Name}, }, }, }, }, }, { name: "allow users in a policybinding if a selector matches", object: &authorizationapi.PolicyBinding{ ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "policybinding", }, RoleBindings: map[string]*authorizationapi.RoleBinding{ "rolebinding": { ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "rolebinding", }, Subjects: []kapi.ObjectReference{ userAliceRef, userBobRef, }, RoleRef: kapi.ObjectReference{ Namespace: authorizationapi.PolicyName, }, }, }, PolicyRef: kapi.ObjectReference{ Namespace: authorizationapi.PolicyName, }, }, oldObject: &authorizationapi.PolicyBinding{ ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "policybinding", }, RoleBindings: map[string]*authorizationapi.RoleBinding{ "rolebinding": { ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "rolebinding", }, Subjects: []kapi.ObjectReference{}, RoleRef: kapi.ObjectReference{ Namespace: authorizationapi.PolicyName, }, }, }, PolicyRef: kapi.ObjectReference{ Namespace: authorizationapi.PolicyName, }, }, kind: authorizationapi.Kind("PolicyBinding").WithVersion("version"), resource: authorizationapi.Resource("policybindings").WithVersion("version"), namespace: "namespace", subresource: "", objects: []runtime.Object{ &kapi.Namespace{ ObjectMeta: kapi.ObjectMeta{ Name: "namespace", }, }, &authorizationapi.Policy{ ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: authorizationapi.PolicyName, }, Roles: map[string]*authorizationapi.Role{ "any": { ObjectMeta: kapi.ObjectMeta{ Namespace: kapi.NamespaceDefault, Name: "any", }, }, }, }, &userAlice, &userBob, &authorizationapi.RoleBindingRestriction{ ObjectMeta: kapi.ObjectMeta{ Name: "match-users-alice", Namespace: "namespace", }, Spec: authorizationapi.RoleBindingRestrictionSpec{ UserRestriction: &authorizationapi.UserRestriction{ Selectors: []unversioned.LabelSelector{ {MatchLabels: map[string]string{"foo": "bar"}}, }, }, }, }, &authorizationapi.RoleBindingRestriction{ ObjectMeta: kapi.ObjectMeta{ Name: "match-users-bob", Namespace: "namespace", }, Spec: authorizationapi.RoleBindingRestrictionSpec{ UserRestriction: &authorizationapi.UserRestriction{ Users: []string{userBob.Name}, }, }, }, }, }, { name: "prohibit a user in a policybinding without a matching selector", expectedErr: fmt.Sprintf("rolebindings to %s %q are not allowed", userBobRef.Kind, userBobRef.Name), object: &authorizationapi.PolicyBinding{ ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "policybinding", }, RoleBindings: map[string]*authorizationapi.RoleBinding{ "rolebinding": { ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "rolebinding", }, Subjects: []kapi.ObjectReference{ userAliceRef, userBobRef, }, RoleRef: kapi.ObjectReference{ Namespace: authorizationapi.PolicyName, }, }, }, PolicyRef: kapi.ObjectReference{ Namespace: authorizationapi.PolicyName, }, }, oldObject: &authorizationapi.PolicyBinding{ ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "policybinding", }, RoleBindings: map[string]*authorizationapi.RoleBinding{ "rolebinding": { ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "rolebinding", }, Subjects: []kapi.ObjectReference{}, RoleRef: kapi.ObjectReference{ Namespace: authorizationapi.PolicyName, }, }, }, PolicyRef: kapi.ObjectReference{ Namespace: authorizationapi.PolicyName, }, }, kind: authorizationapi.Kind("PolicyBinding").WithVersion("version"), resource: authorizationapi.Resource("policybindings").WithVersion("version"), namespace: "namespace", subresource: "", objects: []runtime.Object{ &kapi.Namespace{ ObjectMeta: kapi.ObjectMeta{ Name: "namespace", }, }, &authorizationapi.Policy{ ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: authorizationapi.PolicyName, }, Roles: map[string]*authorizationapi.Role{ "any": { ObjectMeta: kapi.ObjectMeta{ Namespace: kapi.NamespaceDefault, Name: "any", }, }, }, }, &userAlice, &userBob, &authorizationapi.RoleBindingRestriction{ ObjectMeta: kapi.ObjectMeta{ Name: "match-users-alice", Namespace: "namespace", }, Spec: authorizationapi.RoleBindingRestrictionSpec{ UserRestriction: &authorizationapi.UserRestriction{ Selectors: []unversioned.LabelSelector{ {MatchLabels: map[string]string{"foo": "bar"}}, }, }, }, }, }, }, } for _, tc := range testCases { kclientset := fake.NewSimpleClientset(tc.objects...) oclient := otestclient.NewSimpleFake(tc.objects...) plugin, err := NewRestrictUsersAdmission(kclientset) if err != nil { t.Errorf("unexpected error initializing admission plugin: %v", err) } plugins := []admission.Interface{plugin} plugin.(oadmission.WantsOpenshiftClient).SetOpenshiftClient(oclient) groupCache := usercache.NewGroupCache(&groupCache{[]userapi.Group{group}}) plugin.(oadmission.WantsGroupCache).SetGroupCache(groupCache) groupCache.Run() err = admission.Validate(plugins) if err != nil { t.Errorf("unexpected error validating admission plugin: %v", err) } attributes := admission.NewAttributesRecord( tc.object, tc.oldObject, tc.kind, tc.namespace, "name", tc.resource, tc.subresource, admission.Create, &user.DefaultInfo{}, ) err = plugin.Admit(attributes) switch { case len(tc.expectedErr) == 0 && err == nil: case len(tc.expectedErr) == 0 && err != nil: t.Errorf("%s: unexpected error: %v", tc.name, err) case len(tc.expectedErr) != 0 && err == nil: t.Errorf("%s: missing error: %v", tc.name, tc.expectedErr) case len(tc.expectedErr) != 0 && err != nil && !strings.Contains(err.Error(), tc.expectedErr): t.Errorf("%s: missing error: expected %v, got %v", tc.name, tc.expectedErr, err) } } }
// BuildMasterConfig builds and returns the OpenShift master configuration based on the // provided options func BuildMasterConfig(options configapi.MasterConfig) (*MasterConfig, error) { client, err := etcd.MakeEtcdClient(options.EtcdClientInfo) if err != nil { return nil, err } restOptsGetter := originrest.StorageOptions(options) clientCAs, err := configapi.GetClientCertCAPool(options) if err != nil { return nil, err } apiClientCAs, err := configapi.GetAPIClientCertCAPool(options) if err != nil { return nil, err } privilegedLoopbackKubeClient, _, err := configapi.GetKubeClient(options.MasterClients.OpenShiftLoopbackKubeConfig, options.MasterClients.OpenShiftLoopbackClientConnectionOverrides) if err != nil { return nil, err } privilegedLoopbackOpenShiftClient, privilegedLoopbackClientConfig, err := configapi.GetOpenShiftClient(options.MasterClients.OpenShiftLoopbackKubeConfig, options.MasterClients.OpenShiftLoopbackClientConnectionOverrides) if err != nil { return nil, err } customListerWatchers := shared.DefaultListerWatcherOverrides{} if err := addAuthorizationListerWatchers(customListerWatchers, restOptsGetter); err != nil { return nil, err } informerFactory := shared.NewInformerFactory(privilegedLoopbackKubeClient, privilegedLoopbackOpenShiftClient, customListerWatchers, 10*time.Minute) imageTemplate := variable.NewDefaultImageTemplate() imageTemplate.Format = options.ImageConfig.Format imageTemplate.Latest = options.ImageConfig.Latest defaultRegistry := env("OPENSHIFT_DEFAULT_REGISTRY", "${DOCKER_REGISTRY_SERVICE_HOST}:${DOCKER_REGISTRY_SERVICE_PORT}") svcCache := service.NewServiceResolverCache(privilegedLoopbackKubeClient.Services(kapi.NamespaceDefault).Get) defaultRegistryFunc, err := svcCache.Defer(defaultRegistry) if err != nil { return nil, fmt.Errorf("OPENSHIFT_DEFAULT_REGISTRY variable is invalid %q: %v", defaultRegistry, err) } requestContextMapper := kapi.NewRequestContextMapper() groupStorage, err := groupstorage.NewREST(restOptsGetter) if err != nil { return nil, err } groupCache := usercache.NewGroupCache(groupregistry.NewRegistry(groupStorage)) projectCache := projectcache.NewProjectCache(privilegedLoopbackKubeClient.Namespaces(), options.ProjectConfig.DefaultNodeSelector) clusterQuotaMappingController := clusterquotamapping.NewClusterQuotaMappingController(informerFactory.Namespaces(), informerFactory.ClusterResourceQuotas()) kubeletClientConfig := configapi.GetKubeletClientConfig(options) kubeClientSet := clientadapter.FromUnversionedClient(privilegedLoopbackKubeClient) quotaRegistry := quota.NewAllResourceQuotaRegistry(privilegedLoopbackOpenShiftClient, kubeClientSet) ruleResolver := rulevalidation.NewDefaultRuleResolver( informerFactory.Policies().Lister(), informerFactory.PolicyBindings().Lister(), informerFactory.ClusterPolicies().Lister().ClusterPolicies(), informerFactory.ClusterPolicyBindings().Lister().ClusterPolicyBindings(), ) authorizer := newAuthorizer(ruleResolver, informerFactory, options.ProjectConfig.ProjectRequestMessage) pluginInitializer := oadmission.PluginInitializer{ OpenshiftClient: privilegedLoopbackOpenShiftClient, ProjectCache: projectCache, OriginQuotaRegistry: quotaRegistry, Authorizer: authorizer, JenkinsPipelineConfig: options.JenkinsPipelineConfig, RESTClientConfig: *privilegedLoopbackClientConfig, Informers: informerFactory, ClusterQuotaMapper: clusterQuotaMappingController.GetClusterQuotaMapper(), DefaultRegistryFn: imageapi.DefaultRegistryFunc(defaultRegistryFunc), } originAdmission, kubeAdmission, err := buildAdmissionChains(options, kubeClientSet, pluginInitializer) if err != nil { return nil, err } serviceAccountTokenGetter, err := newServiceAccountTokenGetter(options) if err != nil { return nil, err } authenticator, err := newAuthenticator(options, restOptsGetter, serviceAccountTokenGetter, apiClientCAs, groupCache) if err != nil { return nil, err } plug, plugStart := newControllerPlug(options, client) config := &MasterConfig{ Options: options, RESTOptionsGetter: restOptsGetter, RuleResolver: ruleResolver, Authenticator: authenticator, Authorizer: authorizer, AuthorizationAttributeBuilder: newAuthorizationAttributeBuilder(requestContextMapper), GroupCache: groupCache, ProjectAuthorizationCache: newProjectAuthorizationCache(authorizer, privilegedLoopbackKubeClient, informerFactory), ProjectCache: projectCache, ClusterQuotaMappingController: clusterQuotaMappingController, RequestContextMapper: requestContextMapper, AdmissionControl: originAdmission, KubeAdmissionControl: kubeAdmission, TLS: configapi.UseTLS(options.ServingInfo.ServingInfo), ControllerPlug: plug, ControllerPlugStart: plugStart, ImageFor: imageTemplate.ExpandOrDie, RegistryNameFn: imageapi.DefaultRegistryFunc(defaultRegistryFunc), // TODO: migration of versions of resources stored in annotations must be sorted out ExternalVersionCodec: kapi.Codecs.LegacyCodec(unversioned.GroupVersion{Group: "", Version: "v1"}), KubeletClientConfig: kubeletClientConfig, ClientCAs: clientCAs, APIClientCAs: apiClientCAs, PrivilegedLoopbackClientConfig: *privilegedLoopbackClientConfig, PrivilegedLoopbackOpenShiftClient: privilegedLoopbackOpenShiftClient, PrivilegedLoopbackKubernetesClient: privilegedLoopbackKubeClient, Informers: informerFactory, } // ensure that the limit range informer will be started informer := config.Informers.LimitRanges().Informer() config.LimitVerifier = imageadmission.NewLimitVerifier(imageadmission.LimitRangesForNamespaceFunc(func(ns string) ([]*kapi.LimitRange, error) { list, err := config.Informers.LimitRanges().Lister().LimitRanges(ns).List(labels.Everything()) if err != nil { return nil, err } // the verifier must return an error if len(list) == 0 && len(informer.LastSyncResourceVersion()) == 0 { glog.V(4).Infof("LimitVerifier still waiting for ranges to load: %#v", informer) forbiddenErr := kapierrors.NewForbidden(unversioned.GroupResource{Resource: "limitranges"}, "", fmt.Errorf("the server is still loading limit information")) forbiddenErr.ErrStatus.Details.RetryAfterSeconds = 1 return nil, forbiddenErr } return list, nil })) return config, nil }
func TestSubjectCheckers(t *testing.T) { var ( userBobRef = kapi.ObjectReference{ Kind: authorizationapi.UserKind, Name: "Bob", } userAliceRef = kapi.ObjectReference{ Kind: authorizationapi.UserKind, Name: "Alice", } groupRef = kapi.ObjectReference{ Kind: authorizationapi.GroupKind, Name: "group", } serviceaccountRef = kapi.ObjectReference{ Kind: authorizationapi.ServiceAccountKind, Namespace: "namespace", Name: "serviceaccount", } systemuserRef = kapi.ObjectReference{ Kind: authorizationapi.SystemUserKind, Name: "system user", } systemgroupRef = kapi.ObjectReference{ Kind: authorizationapi.SystemGroupKind, Name: "system group", } group = userapi.Group{ ObjectMeta: kapi.ObjectMeta{ Name: "group", Labels: map[string]string{"baz": "quux"}, }, Users: []string{userBobRef.Name}, } objects = []runtime.Object{ &userapi.User{ ObjectMeta: kapi.ObjectMeta{ Name: "Alice", Labels: map[string]string{"foo": "bar"}, }, }, &userapi.User{ ObjectMeta: kapi.ObjectMeta{Name: "Bob"}, Groups: []string{"group"}, }, &group, &kapi.ServiceAccount{ ObjectMeta: kapi.ObjectMeta{ Namespace: "namespace", Name: "serviceaccount", Labels: map[string]string{"xyzzy": "thud"}, }, }, } ) testCases := []struct { name string checker SubjectChecker subject kapi.ObjectReference shouldAllow bool }{ { name: "allow system user by literal name match", checker: mustNewSubjectChecker(t, &authorizationapi.RoleBindingRestrictionSpec{ UserRestriction: &authorizationapi.UserRestriction{ Users: []string{systemuserRef.Name}, }, }), subject: systemuserRef, shouldAllow: true, }, { name: "allow system group by literal name match", checker: mustNewSubjectChecker(t, &authorizationapi.RoleBindingRestrictionSpec{ GroupRestriction: &authorizationapi.GroupRestriction{ Groups: []string{systemgroupRef.Name}, }, }), subject: systemgroupRef, shouldAllow: true, }, { name: "allow regular user by literal name match", checker: mustNewSubjectChecker(t, &authorizationapi.RoleBindingRestrictionSpec{ UserRestriction: &authorizationapi.UserRestriction{ Users: []string{userAliceRef.Name}, }, }), subject: userAliceRef, shouldAllow: true, }, { name: "allow regular user by group membership", checker: mustNewSubjectChecker(t, &authorizationapi.RoleBindingRestrictionSpec{ UserRestriction: &authorizationapi.UserRestriction{ Groups: []string{groupRef.Name}, }, }), subject: userBobRef, shouldAllow: true, }, { name: "prohibit regular user when another user matches on group membership", checker: mustNewSubjectChecker(t, &authorizationapi.RoleBindingRestrictionSpec{ UserRestriction: &authorizationapi.UserRestriction{ Groups: []string{groupRef.Name}, }, }), subject: userAliceRef, shouldAllow: false, }, { name: "allow regular user by label selector match", checker: mustNewSubjectChecker(t, &authorizationapi.RoleBindingRestrictionSpec{ UserRestriction: &authorizationapi.UserRestriction{ Selectors: []unversioned.LabelSelector{ {MatchLabels: map[string]string{"foo": "bar"}}, }, }, }), subject: userAliceRef, shouldAllow: true, }, { name: "prohibit regular user when another user matches on label selector", checker: mustNewSubjectChecker(t, &authorizationapi.RoleBindingRestrictionSpec{ UserRestriction: &authorizationapi.UserRestriction{ Selectors: []unversioned.LabelSelector{ {MatchLabels: map[string]string{"foo": "bar"}}, }, }, }), subject: userBobRef, shouldAllow: false, }, { name: "allow regular group by literal name match", checker: mustNewSubjectChecker(t, &authorizationapi.RoleBindingRestrictionSpec{ GroupRestriction: &authorizationapi.GroupRestriction{ Groups: []string{groupRef.Name}, }, }), subject: groupRef, shouldAllow: true, }, { name: "allow regular group by label selector match", checker: mustNewSubjectChecker(t, &authorizationapi.RoleBindingRestrictionSpec{ GroupRestriction: &authorizationapi.GroupRestriction{ Selectors: []unversioned.LabelSelector{ {MatchLabels: map[string]string{"baz": "quux"}}, }, }, }), subject: groupRef, shouldAllow: true, }, { name: "allow service account by literal name match", checker: mustNewSubjectChecker(t, &authorizationapi.RoleBindingRestrictionSpec{ ServiceAccountRestriction: &authorizationapi.ServiceAccountRestriction{ ServiceAccounts: []authorizationapi.ServiceAccountReference{ { Name: serviceaccountRef.Name, Namespace: serviceaccountRef.Namespace, }, }, }, }), subject: serviceaccountRef, shouldAllow: true, }, { name: "allow service account by literal name match with implicit namespace", checker: mustNewSubjectChecker(t, &authorizationapi.RoleBindingRestrictionSpec{ ServiceAccountRestriction: &authorizationapi.ServiceAccountRestriction{ ServiceAccounts: []authorizationapi.ServiceAccountReference{ {Name: serviceaccountRef.Name}, }, }, }), subject: serviceaccountRef, shouldAllow: true, }, { name: "allow service account by match on namespace", checker: mustNewSubjectChecker(t, &authorizationapi.RoleBindingRestrictionSpec{ ServiceAccountRestriction: &authorizationapi.ServiceAccountRestriction{ Namespaces: []string{serviceaccountRef.Namespace}, }, }), subject: serviceaccountRef, shouldAllow: true, }, } kclient := fake.NewSimpleClientset(objects...) oclient := otestclient.NewSimpleFake(objects...) groupCache := usercache.NewGroupCache(&groupCache{[]userapi.Group{group}}) groupCache.Run() // This is a terrible, horrible, no-good, very bad hack to avoid a race // condition between the test "allow regular user by group membership" // and the group cache's initialisation. for { if groups, _ := groupCache.GroupsFor(group.Users[0]); len(groups) == 1 { break } time.Sleep(10 * time.Millisecond) } ctx, err := NewRoleBindingRestrictionContext("namespace", kclient, oclient, groupCache) if err != nil { t.Errorf("unexpected error: %v", err) } for _, tc := range testCases { allowed, err := tc.checker.Allowed(tc.subject, ctx) if err != nil { t.Errorf("test case %v: unexpected error: %v", tc.name, err) } if allowed && !tc.shouldAllow { t.Errorf("test case %v: subject allowed but should be prohibited", tc.name) } if !allowed && tc.shouldAllow { t.Errorf("test case %v: subject prohibited but should be allowed", tc.name) } } }
func TestImpersonationFilter(t *testing.T) { testCases := []struct { name string user user.Info impersonationString string expectedUser user.Info expectedCode int }{ { name: "not-impersonating", user: &user.DefaultInfo{ Name: "tester", }, expectedUser: &user.DefaultInfo{ Name: "tester", }, expectedCode: http.StatusOK, }, { name: "impersonating-error", user: &user.DefaultInfo{ Name: "tester", }, impersonationString: "anyone", expectedUser: &user.DefaultInfo{ Name: "tester", }, expectedCode: http.StatusForbidden, }, { name: "allowed-systemusers-impersonation", user: &user.DefaultInfo{ Name: "dev", Groups: []string{"wheel"}, }, impersonationString: "system:admin", expectedUser: &user.DefaultInfo{ Name: "system:admin", Groups: []string{"system:authenticated"}, }, expectedCode: http.StatusOK, }, { name: "allowed-users-impersonation", user: &user.DefaultInfo{ Name: "dev", Groups: []string{"regular-impersonater"}, }, impersonationString: "tester", expectedUser: &user.DefaultInfo{ Name: "tester", Groups: []string{"system:authenticated", "system:authenticated:oauth"}, }, expectedCode: http.StatusOK, }, { name: "disallowed-impersonating", user: &user.DefaultInfo{ Name: "dev", Groups: []string{"sa-impersonater"}, }, impersonationString: "tester", expectedUser: &user.DefaultInfo{ Name: "dev", Groups: []string{"sa-impersonater"}, }, expectedCode: http.StatusForbidden, }, { name: "allowed-sa-impersonating", user: &user.DefaultInfo{ Name: "dev", Groups: []string{"sa-impersonater"}, }, impersonationString: "system:serviceaccount:foo:default", expectedUser: &user.DefaultInfo{ Name: "system:serviceaccount:foo:default", Groups: []string{"system:serviceaccounts", "system:serviceaccounts:foo", "system:authenticated"}, }, expectedCode: http.StatusOK, }, } config := MasterConfig{} config.RequestContextMapper = kapi.NewRequestContextMapper() config.Authorizer = impersonateAuthorizer{} config.GroupCache = usercache.NewGroupCache(&groupCache{}) var ctx kapi.Context var actualUser user.Info var lock sync.Mutex doNothingHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { currentCtx, _ := config.RequestContextMapper.Get(req) user, exists := kapi.UserFrom(currentCtx) if !exists { actualUser = nil return } actualUser = user }) handler := func(delegate http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { defer func() { if r := recover(); r != nil { t.Errorf("Recovered %v", r) } }() lock.Lock() defer lock.Unlock() config.RequestContextMapper.Update(req, ctx) currentCtx, _ := config.RequestContextMapper.Get(req) user, exists := kapi.UserFrom(currentCtx) if !exists { actualUser = nil return } else { actualUser = user } delegate.ServeHTTP(w, req) }) }(config.impersonationFilter(doNothingHandler)) handler, _ = kapi.NewRequestContextFilter(config.RequestContextMapper, handler) server := httptest.NewServer(handler) defer server.Close() for _, tc := range testCases { func() { lock.Lock() defer lock.Unlock() ctx = kapi.WithUser(kapi.NewContext(), tc.user) }() req, err := http.NewRequest("GET", server.URL, nil) if err != nil { t.Errorf("%s: unexpected error: %v", tc.name, err) continue } req.Header.Add(authenticationapi.ImpersonateUserHeader, tc.impersonationString) resp, err := http.DefaultClient.Do(req) if err != nil { t.Errorf("%s: unexpected error: %v", tc.name, err) continue } if resp.StatusCode != tc.expectedCode { t.Errorf("%s: expected %v, actual %v", tc.name, tc.expectedCode, resp.StatusCode) continue } if !reflect.DeepEqual(actualUser, tc.expectedUser) { t.Errorf("%s: expected %#v, actual %#v", tc.name, tc.expectedUser, actualUser) continue } } }