Example #1
0
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
}
Example #2
0
// 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
}
Example #3
0
// 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
}
Example #4
0
// 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
}
Example #5
0
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)
		}
	}
}
Example #6
0
// 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
}
Example #7
0
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)
		}
	}
}
Example #8
0
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
		}
	}
}