func TestSimpleImageChangeBuildTriggerFromImageStreamTagCustomWithConfigChange(t *testing.T) { defer testutil.DumpEtcdOnFailure(t) projectAdminClient, _ := setup(t) clusterAdminClient, err := testutil.GetClusterAdminClient(testutil.GetBaseDir() + "/openshift.local.config/master/admin.kubeconfig") if err != nil { t.Fatalf("unexpected error: %v", err) } clusterRoleBindingAccessor := policy.NewClusterRoleBindingAccessor(clusterAdminClient) subjects := []kapi.ObjectReference{ { Kind: authorizationapi.SystemGroupKind, Name: bootstrappolicy.AuthenticatedGroup, }, } options := policy.RoleModificationOptions{ RoleNamespace: testutil.Namespace(), RoleName: bootstrappolicy.BuildStrategyCustomRoleName, RoleBindingAccessor: clusterRoleBindingAccessor, Subjects: subjects, } options.AddRole() if err := testutil.WaitForPolicyUpdate(projectAdminClient, testutil.Namespace(), "create", buildapi.Resource(authorizationapi.CustomBuildResource), true); err != nil { t.Fatal(err) } imageStream := mockImageStream2(tag) imageStreamMapping := mockImageStreamMapping(imageStream.Name, "someimage", tag, "registry:8080/openshift/test-image-trigger:"+tag) strategy := customStrategy("ImageStreamTag", streamName+":"+tag) config := imageChangeBuildConfigWithConfigChange("custom-imagestreamtag", strategy) runTest(t, "SimpleImageChangeBuildTriggerFromImageStreamTagCustom", projectAdminClient, imageStream, imageStreamMapping, config, tag) }
func removeBuildStrategyRoleResources(t *testing.T, clusterAdminClient, projectAdminClient, projectEditorClient *client.Client) { // remove resources from role so that certain build strategies are forbidden for _, role := range []string{bootstrappolicy.BuildStrategyCustomRoleName, bootstrappolicy.BuildStrategyDockerRoleName, bootstrappolicy.BuildStrategySourceRoleName, bootstrappolicy.BuildStrategyJenkinsPipelineRoleName} { options := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: role, RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(clusterAdminClient), Groups: []string{"system:authenticated"}, } if err := options.RemoveRole(); err != nil { t.Fatalf("unexpected error: %v", err) } } if err := testutil.WaitForPolicyUpdate(projectEditorClient, testutil.Namespace(), "create", buildapi.Resource(authorizationapi.DockerBuildResource), false); err != nil { t.Fatal(err) } if err := testutil.WaitForPolicyUpdate(projectEditorClient, testutil.Namespace(), "create", buildapi.Resource(authorizationapi.SourceBuildResource), false); err != nil { t.Fatal(err) } if err := testutil.WaitForPolicyUpdate(projectEditorClient, testutil.Namespace(), "create", buildapi.Resource(authorizationapi.CustomBuildResource), false); err != nil { t.Fatal(err) } if err := testutil.WaitForPolicyUpdate(projectEditorClient, testutil.Namespace(), "create", buildapi.Resource(authorizationapi.JenkinsPipelineBuildResource), false); err != nil { t.Fatal(err) } }
// simulates: oadm policy add-cluster-role-to-user roleName userName func addClusterRoleToUser(c *oclient.Client, f *cmdutil.Factory, roleName string, userName string) error { options := policy.RoleModificationOptions{ RoleName: roleName, RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(c), Users: []string{userName}, } return options.AddRole() }
func AddClusterRole(osClient client.Interface, role, user string) error { clusterRoleBindingAccessor := policy.NewClusterRoleBindingAccessor(osClient) addClusterReaderRole := policy.RoleModificationOptions{ RoleName: role, RoleBindingAccessor: clusterRoleBindingAccessor, Users: []string{user}, } return addClusterReaderRole.AddRole() }
// ensureOpenShiftInfraNamespace is called as part of global policy initialization to ensure infra namespace exists func (c *MasterConfig) ensureOpenShiftInfraNamespace() { ns := c.Options.PolicyConfig.OpenShiftInfrastructureNamespace // Ensure namespace exists namespace, err := c.KubeClient().Namespaces().Create(&kapi.Namespace{ObjectMeta: kapi.ObjectMeta{Name: ns}}) if kapierror.IsAlreadyExists(err) { // Get the persisted namespace namespace, err = c.KubeClient().Namespaces().Get(ns) if err != nil { glog.Errorf("Error getting namespace %s: %v", ns, err) return } } else if err != nil { glog.Errorf("Error creating namespace %s: %v", ns, err) return } // Ensure service accounts exist serviceAccounts := []string{ c.BuildControllerServiceAccount, c.DeploymentControllerServiceAccount, c.ReplicationControllerServiceAccount, c.JobControllerServiceAccount, c.HPAControllerServiceAccount, c.PersistentVolumeControllerServiceAccount, } for _, serviceAccountName := range serviceAccounts { _, err := c.KubeClient().ServiceAccounts(ns).Create(&kapi.ServiceAccount{ObjectMeta: kapi.ObjectMeta{Name: serviceAccountName}}) if err != nil && !kapierror.IsAlreadyExists(err) { glog.Errorf("Error creating service account %s/%s: %v", ns, serviceAccountName, err) } } // Ensure service account cluster role bindings exist clusterRolesToSubjects := map[string][]kapi.ObjectReference{ bootstrappolicy.BuildControllerRoleName: {{Namespace: ns, Name: c.BuildControllerServiceAccount, Kind: "ServiceAccount"}}, bootstrappolicy.DeploymentControllerRoleName: {{Namespace: ns, Name: c.DeploymentControllerServiceAccount, Kind: "ServiceAccount"}}, bootstrappolicy.ReplicationControllerRoleName: {{Namespace: ns, Name: c.ReplicationControllerServiceAccount, Kind: "ServiceAccount"}}, bootstrappolicy.JobControllerRoleName: {{Namespace: ns, Name: c.JobControllerServiceAccount, Kind: "ServiceAccount"}}, bootstrappolicy.HPAControllerRoleName: {{Namespace: ns, Name: c.HPAControllerServiceAccount, Kind: "ServiceAccount"}}, bootstrappolicy.PersistentVolumeControllerRoleName: {{Namespace: ns, Name: c.PersistentVolumeControllerServiceAccount, Kind: "ServiceAccount"}}, } roleAccessor := policy.NewClusterRoleBindingAccessor(c.ServiceAccountRoleBindingClient()) for clusterRole, subjects := range clusterRolesToSubjects { addRole := &policy.RoleModificationOptions{ RoleName: clusterRole, RoleBindingAccessor: roleAccessor, Subjects: subjects, } if err := addRole.AddRole(); err != nil { glog.Errorf("Could not add %v subjects to the %v cluster role: %v\n", subjects, clusterRole, err) } else { glog.V(2).Infof("Added %v subjects to the %v cluster role: %v\n", subjects, clusterRole, err) } } c.ensureNamespaceServiceAccountRoleBindings(namespace) }
func TestAuthorizationOnlyResolveRolesForBindingsThatMatter(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } addValerie := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.ViewRoleName, RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(clusterAdminClient), Users: []string{"valerie"}, } if err := addValerie.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } if err = clusterAdminClient.ClusterRoles().Delete(bootstrappolicy.ViewRoleName); err != nil { t.Fatalf("unexpected error: %v", err) } addEdgar := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.EditRoleName, RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(clusterAdminClient), Users: []string{"edgar"}, } if err := addEdgar.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } // try to add Valerie to a non-existent role if err := addValerie.AddRole(); !kapierror.IsNotFound(err) { t.Fatalf("unexpected error: %v", err) } }
func setupUserPodNodeConstraintsTest(t *testing.T, pluginConfig *pluginapi.PodNodeConstraintsConfig, user string) (*client.Client, *kclientset.Clientset) { testutil.RequireEtcd(t) masterConfig, err := testserver.DefaultMasterOptions() if err != nil { t.Fatalf("error creating config: %v", err) } cfg := map[string]configapi.AdmissionPluginConfig{ "PodNodeConstraints": { Configuration: pluginConfig, }, } masterConfig.AdmissionConfig.PluginConfig = cfg masterConfig.KubernetesMasterConfig.AdmissionConfig.PluginConfig = cfg kubeConfigFile, err := testserver.StartConfiguredMaster(masterConfig) if err != nil { t.Fatalf("error starting server: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(kubeConfigFile) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(kubeConfigFile) if err != nil { t.Fatalf("unexpected error: %v", err) } userClient, userkubeClientset, _, err := testutil.GetClientForUser(*clusterAdminClientConfig, user) if err != nil { t.Fatalf("error getting user/kube client: %v", err) } kubeClientset, err := testutil.GetClusterAdminKubeClient(kubeConfigFile) if err != nil { t.Fatalf("error getting kube client: %v", err) } ns := &kapi.Namespace{} ns.Name = testutil.Namespace() _, err = kubeClientset.Core().Namespaces().Create(ns) if err != nil { t.Fatalf("error creating namespace: %v", err) } if err := testserver.WaitForServiceAccounts(kubeClientset, testutil.Namespace(), []string{bootstrappolicy.DefaultServiceAccountName}); err != nil { t.Fatalf("unexpected error: %v", err) } addUser := &policy.RoleModificationOptions{ RoleNamespace: ns.Name, RoleName: bootstrappolicy.AdminRoleName, RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(clusterAdminClient), Users: []string{user}, } if err := addUser.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } return userClient, userkubeClientset }
func makeUserAnImageSigner(clusterAdminClient *client.Client, userClient *client.Client, userName string) error { // give bob permissions to update image signatures addImageSignerRole := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.ImageSignerRoleName, RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(clusterAdminClient), Users: []string{userName}, } if err := addImageSignerRole.AddRole(); err != nil { return err } return testutil.WaitForClusterPolicyUpdate(userClient, "create", kapi.Resource("imagesignatures"), true) }
// ensureOpenShiftInfraNamespace is called as part of global policy initialization to ensure infra namespace exists func (c *MasterConfig) ensureOpenShiftInfraNamespace() { ns := c.Options.PolicyConfig.OpenShiftInfrastructureNamespace // Ensure namespace exists namespace, err := c.KubeClient().Namespaces().Create(&kapi.Namespace{ObjectMeta: kapi.ObjectMeta{Name: ns}}) if kapierror.IsAlreadyExists(err) { // Get the persisted namespace namespace, err = c.KubeClient().Namespaces().Get(ns) if err != nil { glog.Errorf("Error getting namespace %s: %v", ns, err) return } } else if err != nil { glog.Errorf("Error creating namespace %s: %v", ns, err) return } roleAccessor := policy.NewClusterRoleBindingAccessor(c.ServiceAccountRoleBindingClient()) for _, saName := range bootstrappolicy.InfraSAs.GetServiceAccounts() { _, err := c.KubeClient().ServiceAccounts(ns).Create(&kapi.ServiceAccount{ObjectMeta: kapi.ObjectMeta{Name: saName}}) if err != nil && !kapierror.IsAlreadyExists(err) { glog.Errorf("Error creating service account %s/%s: %v", ns, saName, err) } role, _ := bootstrappolicy.InfraSAs.RoleFor(saName) reconcileRole := &policy.ReconcileClusterRolesOptions{ RolesToReconcile: []string{role.Name}, Confirmed: true, Union: true, Out: ioutil.Discard, RoleClient: c.PrivilegedLoopbackOpenShiftClient.ClusterRoles(), } if err := reconcileRole.RunReconcileClusterRoles(nil, nil); err != nil { glog.Errorf("Could not reconcile %v: %v\n", role.Name, err) } addRole := &policy.RoleModificationOptions{ RoleName: role.Name, RoleBindingAccessor: roleAccessor, Subjects: []kapi.ObjectReference{{Namespace: ns, Name: saName, Kind: "ServiceAccount"}}, } if err := kclient.RetryOnConflict(kclient.DefaultRetry, func() error { return addRole.AddRole() }); err != nil { glog.Errorf("Could not add %v service accounts to the %v cluster role: %v\n", saName, role.Name, err) } else { glog.V(2).Infof("Added %v service accounts to the %v cluster role: %v\n", saName, role.Name, err) } } c.ensureNamespaceServiceAccountRoleBindings(namespace) }
func grantRestrictedBuildStrategyRoleResources(t *testing.T, clusterAdminClient, projectAdminClient, projectEditorClient *client.Client) { // grant resources to role so that restricted build strategies are available for _, role := range []string{bootstrappolicy.BuildStrategyCustomRoleName} { options := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: role, RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(clusterAdminClient), Groups: []string{"system:authenticated"}, } if err := options.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } } if err := testutil.WaitForPolicyUpdate(projectEditorClient, testutil.Namespace(), "create", buildapi.Resource(authorizationapi.CustomBuildResource), true); err != nil { t.Fatal(err) } }
// ensureOpenShiftInfraNamespace is called as part of global policy initialization to ensure infra namespace exists func (c *MasterConfig) ensureOpenShiftInfraNamespace() { ns := c.Options.PolicyConfig.OpenShiftInfrastructureNamespace // Ensure namespace exists _, err := c.KubeClient().Namespaces().Create(&kapi.Namespace{ObjectMeta: kapi.ObjectMeta{Name: ns}}) if err != nil && !kapierror.IsAlreadyExists(err) { glog.Errorf("Error creating namespace %s: %v", ns, err) } // Ensure service accounts exist serviceAccounts := []string{c.BuildControllerServiceAccount, c.DeploymentControllerServiceAccount, c.ReplicationControllerServiceAccount} for _, serviceAccountName := range serviceAccounts { _, err := c.KubeClient().ServiceAccounts(ns).Create(&kapi.ServiceAccount{ObjectMeta: kapi.ObjectMeta{Name: serviceAccountName}}) if err != nil && !kapierror.IsAlreadyExists(err) { glog.Errorf("Error creating service account %s/%s: %v", ns, serviceAccountName, err) } } // Ensure service account cluster role bindings exist clusterRolesToUsernames := map[string][]string{ bootstrappolicy.BuildControllerRoleName: {serviceaccount.MakeUsername(ns, c.BuildControllerServiceAccount)}, bootstrappolicy.DeploymentControllerRoleName: {serviceaccount.MakeUsername(ns, c.DeploymentControllerServiceAccount)}, bootstrappolicy.ReplicationControllerRoleName: {serviceaccount.MakeUsername(ns, c.ReplicationControllerServiceAccount)}, } roleAccessor := policy.NewClusterRoleBindingAccessor(c.ServiceAccountRoleBindingClient()) for clusterRole, usernames := range clusterRolesToUsernames { addRole := &policy.RoleModificationOptions{ RoleName: clusterRole, RoleBindingAccessor: roleAccessor, Users: usernames, } if err := addRole.AddRole(); err != nil { glog.Errorf("Could not add %v users to the %v cluster role: %v\n", usernames, clusterRole, err) } else { glog.V(2).Infof("Added %v users to the %v cluster role: %v\n", usernames, clusterRole, err) } } }
func TestAuthorizationResolution(t *testing.T) { _, clusterAdminKubeConfig, err := testserver.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } addValerie := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.ViewRoleName, RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(clusterAdminClient), Users: []string{"valerie"}, } if err := addValerie.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } if err = clusterAdminClient.ClusterRoles().Delete(bootstrappolicy.ViewRoleName); err != nil { t.Fatalf("unexpected error: %v", err) } addEdgar := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.EditRoleName, RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(clusterAdminClient), Users: []string{"edgar"}, } if err := addEdgar.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } // try to add Valerie to a non-existent role if err := addValerie.AddRole(); !kapierror.IsNotFound(err) { t.Fatalf("unexpected error: %v", err) } roleWithGroup := &authorizationapi.ClusterRole{} roleWithGroup.Name = "with-group" roleWithGroup.Rules = append(roleWithGroup.Rules, authorizationapi.PolicyRule{ Verbs: sets.NewString("list"), Resources: sets.NewString(authorizationapi.BuildGroupName), }) if _, err := clusterAdminClient.ClusterRoles().Create(roleWithGroup); err != nil { t.Fatalf("unexpected error: %v", err) } addBuildLister := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: "with-group", RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(clusterAdminClient), Users: []string{"build-lister"}, } if err := addBuildLister.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } buildListerClient, _, _, err := testutil.GetClientForUser(*clusterAdminConfig, "build-lister") if err != nil { t.Fatalf("unexpected error: %v", err) } if _, err := buildListerClient.Builds(kapi.NamespaceDefault).List(labels.Everything(), fields.Everything()); err != nil { t.Fatalf("unexpected error: %v", err) } if _, err := buildListerClient.DeploymentConfigs(kapi.NamespaceDefault).List(labels.Everything(), fields.Everything()); !kapierror.IsForbidden(err) { t.Errorf("expected forbidden, got %v", err) } }
func TestAuthorizationSubjectAccessReview(t *testing.T) { _, clusterAdminKubeConfig, err := testserver.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } haroldClient, err := testserver.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "hammer-project", "harold") if err != nil { t.Fatalf("unexpected error: %v", err) } markClient, err := testserver.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "mallet-project", "mark") if err != nil { t.Fatalf("unexpected error: %v", err) } dannyClient, _, dannyConfig, err := testutil.GetClientForUser(*clusterAdminClientConfig, "danny") if err != nil { t.Fatalf("error requesting token: %v", err) } addDanny := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.ViewRoleName, RoleBindingAccessor: policy.NewLocalRoleBindingAccessor("default", clusterAdminClient), Users: []string{"danny"}, } if err := addDanny.AddRole(); err != nil { t.Errorf("unexpected error: %v", err) } askCanDannyGetProject := &authorizationapi.SubjectAccessReview{ User: "******", Action: authorizationapi.AuthorizationAttributes{Verb: "get", Resource: "projects"}, } subjectAccessReviewTest{ description: "cluster admin told danny can get project default", localInterface: clusterAdminClient.LocalSubjectAccessReviews("default"), localReview: &authorizationapi.LocalSubjectAccessReview{ User: "******", Action: authorizationapi.AuthorizationAttributes{Verb: "get", Resource: "projects"}, }, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by rule in default", Namespace: "default", }, }.run(t) subjectAccessReviewTest{ description: "cluster admin told danny cannot get projects cluster-wide", clusterInterface: clusterAdminClient.SubjectAccessReviews(), clusterReview: askCanDannyGetProject, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "danny" cannot get projects at the cluster scope`, Namespace: "", }, }.run(t) subjectAccessReviewTest{ description: "as danny, can I make cluster subject access reviews", clusterInterface: dannyClient.SubjectAccessReviews(), clusterReview: askCanDannyGetProject, err: `User "danny" cannot create subjectaccessreviews at the cluster scope`, }.run(t) addValerie := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.ViewRoleName, RoleBindingAccessor: policy.NewLocalRoleBindingAccessor("hammer-project", haroldClient), Users: []string{"valerie"}, } if err := addValerie.AddRole(); err != nil { t.Errorf("unexpected error: %v", err) } addEdgar := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.EditRoleName, RoleBindingAccessor: policy.NewLocalRoleBindingAccessor("mallet-project", markClient), Users: []string{"edgar"}, } if err := addEdgar.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } askCanValerieGetProject := &authorizationapi.LocalSubjectAccessReview{ User: "******", Action: authorizationapi.AuthorizationAttributes{Verb: "get", Resource: "projects"}, } subjectAccessReviewTest{ description: "harold told valerie can get project hammer-project", localInterface: haroldClient.LocalSubjectAccessReviews("hammer-project"), localReview: askCanValerieGetProject, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by rule in hammer-project", Namespace: "hammer-project", }, }.run(t) subjectAccessReviewTest{ description: "mark told valerie cannot get project mallet-project", localInterface: markClient.LocalSubjectAccessReviews("mallet-project"), localReview: askCanValerieGetProject, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "valerie" cannot get projects in project "mallet-project"`, Namespace: "mallet-project", }, }.run(t) askCanEdgarDeletePods := &authorizationapi.LocalSubjectAccessReview{ User: "******", Action: authorizationapi.AuthorizationAttributes{Verb: "delete", Resource: "pods"}, } subjectAccessReviewTest{ description: "mark told edgar can delete pods in mallet-project", localInterface: markClient.LocalSubjectAccessReviews("mallet-project"), localReview: askCanEdgarDeletePods, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by rule in mallet-project", Namespace: "mallet-project", }, }.run(t) subjectAccessReviewTest{ description: "harold denied ability to run subject access review in project mallet-project", localInterface: haroldClient.LocalSubjectAccessReviews("mallet-project"), localReview: askCanEdgarDeletePods, err: `User "harold" cannot create localsubjectaccessreviews in project "mallet-project"`, }.run(t) askCanHaroldUpdateProject := &authorizationapi.LocalSubjectAccessReview{ User: "******", Action: authorizationapi.AuthorizationAttributes{Verb: "update", Resource: "projects"}, } subjectAccessReviewTest{ description: "harold told harold can update project hammer-project", localInterface: haroldClient.LocalSubjectAccessReviews("hammer-project"), localReview: askCanHaroldUpdateProject, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by rule in hammer-project", Namespace: "hammer-project", }, }.run(t) askCanClusterAdminsCreateProject := &authorizationapi.SubjectAccessReview{ Groups: sets.NewString("system:cluster-admins"), Action: authorizationapi.AuthorizationAttributes{Verb: "create", Resource: "projects"}, } subjectAccessReviewTest{ description: "cluster admin told cluster admins can create projects", clusterInterface: clusterAdminClient.SubjectAccessReviews(), clusterReview: askCanClusterAdminsCreateProject, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by cluster rule", Namespace: "", }, }.run(t) subjectAccessReviewTest{ description: "harold denied ability to run cluster subject access review", clusterInterface: haroldClient.SubjectAccessReviews(), clusterReview: askCanClusterAdminsCreateProject, err: `User "harold" cannot create subjectaccessreviews at the cluster scope`, }.run(t) askCanICreatePods := &authorizationapi.LocalSubjectAccessReview{ Action: authorizationapi.AuthorizationAttributes{Verb: "create", Resource: "pods"}, } subjectAccessReviewTest{ description: "harold told he can create pods in project hammer-project", localInterface: haroldClient.LocalSubjectAccessReviews("hammer-project"), localReview: askCanICreatePods, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: "allowed by rule in hammer-project", Namespace: "hammer-project", }, }.run(t) askCanICreatePolicyBindings := &authorizationapi.LocalSubjectAccessReview{ Action: authorizationapi.AuthorizationAttributes{Verb: "create", Resource: "policybindings"}, } subjectAccessReviewTest{ description: "harold told he can create policybindings in project hammer-project", localInterface: haroldClient.LocalSubjectAccessReviews("hammer-project"), localReview: askCanICreatePolicyBindings, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "harold" cannot create policybindings in project "hammer-project"`, Namespace: "hammer-project", }, }.run(t) // impersonate SAR tests // impersonated empty token SAR shouldn't be allowed at all // impersonated danny token SAR shouldn't be allowed to see pods in hammer or in cluster // impersonated danny token SAR should be allowed to see pods in default // we need a token client for overriding if err != nil { t.Fatalf("unexpected error: %v", err) } otherAdminClient, _, _, err := testutil.GetClientForUser(*clusterAdminClientConfig, "other-admin") if err != nil { t.Fatalf("error requesting token: %v", err) } addOtherAdmin := &policy.RoleModificationOptions{ RoleNamespace: "", RoleName: bootstrappolicy.ClusterAdminRoleName, RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(clusterAdminClient), Users: []string{"other-admin"}, } if err := addOtherAdmin.AddRole(); err != nil { t.Errorf("unexpected error: %v", err) } subjectAccessReviewTest{ description: "empty token impersonate can't see pods in namespace", localInterface: otherAdminClient.ImpersonateLocalSubjectAccessReviews("hammer-project", ""), localReview: &authorizationapi.LocalSubjectAccessReview{ Action: authorizationapi.AuthorizationAttributes{Verb: "list", Resource: "pods"}, }, err: `impersonating token may not be empty`, }.run(t) subjectAccessReviewTest{ description: "empty token impersonate can't see pods in cluster", clusterInterface: otherAdminClient.ImpersonateSubjectAccessReviews(""), clusterReview: &authorizationapi.SubjectAccessReview{ Action: authorizationapi.AuthorizationAttributes{Verb: "list", Resource: "pods"}, }, err: `impersonating token may not be empty`, }.run(t) subjectAccessReviewTest{ description: "danny impersonate can't see pods in hammer namespace", localInterface: otherAdminClient.ImpersonateLocalSubjectAccessReviews("hammer-project", dannyConfig.BearerToken), localReview: &authorizationapi.LocalSubjectAccessReview{ Action: authorizationapi.AuthorizationAttributes{Verb: "list", Resource: "pods"}, }, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "danny" cannot list pods in project "hammer-project"`, Namespace: "hammer-project", }, }.run(t) subjectAccessReviewTest{ description: "danny impersonate can't see pods in cluster", clusterInterface: otherAdminClient.ImpersonateSubjectAccessReviews(dannyConfig.BearerToken), clusterReview: &authorizationapi.SubjectAccessReview{ Action: authorizationapi.AuthorizationAttributes{Verb: "list", Resource: "pods"}, }, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "danny" cannot list all pods in the cluster`, }, }.run(t) subjectAccessReviewTest{ description: "danny impersonate can see pods in default", localInterface: otherAdminClient.ImpersonateLocalSubjectAccessReviews("default", dannyConfig.BearerToken), localReview: &authorizationapi.LocalSubjectAccessReview{ Action: authorizationapi.AuthorizationAttributes{Verb: "list", Resource: "pods"}, }, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: true, Reason: `allowed by rule in default`, Namespace: "default", }, }.run(t) }
func TestInvalidRoleRefs(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) _, clusterAdminKubeConfig, err := testserver.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } bobClient, _, _, err := testutil.GetClientForUser(*clusterAdminClientConfig, "bob") if err != nil { t.Fatalf("unexpected error: %v", err) } aliceClient, _, _, err := testutil.GetClientForUser(*clusterAdminClientConfig, "alice") if err != nil { t.Fatalf("unexpected error: %v", err) } if _, err := testserver.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "foo", "bob"); err != nil { t.Fatalf("unexpected error: %v", err) } if _, err := testserver.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "bar", "alice"); err != nil { t.Fatalf("unexpected error: %v", err) } roleName := "missing-role" if _, err := clusterAdminClient.ClusterRoles().Create(&authorizationapi.ClusterRole{ObjectMeta: kapi.ObjectMeta{Name: roleName}}); err != nil { t.Fatalf("unexpected error: %v", err) } modifyRole := &policy.RoleModificationOptions{RoleName: roleName, Users: []string{"someuser"}} // mess up rolebindings in "foo" modifyRole.RoleBindingAccessor = policy.NewLocalRoleBindingAccessor("foo", clusterAdminClient) if err := modifyRole.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } // mess up rolebindings in "bar" modifyRole.RoleBindingAccessor = policy.NewLocalRoleBindingAccessor("bar", clusterAdminClient) if err := modifyRole.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } // mess up clusterrolebindings modifyRole.RoleBindingAccessor = policy.NewClusterRoleBindingAccessor(clusterAdminClient) if err := modifyRole.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } // Orphan the rolebindings by deleting the role if err := clusterAdminClient.ClusterRoles().Delete(roleName); err != nil { t.Fatalf("unexpected error: %v", err) } // wait for evaluation errors to show up in both namespaces and at cluster scope if err := wait.PollImmediate(100*time.Millisecond, 10*time.Second, func() (bool, error) { review := &authorizationapi.ResourceAccessReview{Action: authorizationapi.Action{Verb: "get", Resource: "pods"}} review.Action.Namespace = "foo" if resp, err := clusterAdminClient.ResourceAccessReviews().Create(review); err != nil || resp.EvaluationError == "" { return false, err } review.Action.Namespace = "bar" if resp, err := clusterAdminClient.ResourceAccessReviews().Create(review); err != nil || resp.EvaluationError == "" { return false, err } review.Action.Namespace = "" if resp, err := clusterAdminClient.ResourceAccessReviews().Create(review); err != nil || resp.EvaluationError == "" { return false, err } return true, nil }); err != nil { t.Fatalf("unexpected error: %v", err) } // Make sure bob still sees his project (and only his project) if projects, err := bobClient.Projects().List(kapi.ListOptions{}); err != nil { t.Fatalf("unexpected error: %v", err) } else if hasErr := hasExactlyTheseProjects(projects, sets.NewString("foo")); hasErr != nil { t.Error(hasErr) } // Make sure alice still sees her project (and only her project) if projects, err := aliceClient.Projects().List(kapi.ListOptions{}); err != nil { t.Fatalf("unexpected error: %v", err) } else if hasErr := hasExactlyTheseProjects(projects, sets.NewString("bar")); hasErr != nil { t.Error(hasErr) } // Make sure cluster admin still sees all projects if projects, err := clusterAdminClient.Projects().List(kapi.ListOptions{}); err != nil { t.Fatalf("unexpected error: %v", err) } else { projectNames := sets.NewString() for _, project := range projects.Items { projectNames.Insert(project.Name) } if !projectNames.HasAll("foo", "bar", "openshift-infra", "openshift", "default") { t.Errorf("Expected projects foo and bar, got %v", projectNames.List()) } } }
// InstallMetrics checks whether metrics is installed and installs it if not already installed func (h *Helper) InstallMetrics(f *clientcmd.Factory, hostName, imagePrefix, imageVersion string) error { osClient, kubeClient, err := f.Clients() if err != nil { return errors.NewError("cannot obtain API clients").WithCause(err).WithDetails(h.OriginLog()) } _, err = kubeClient.Services(infraNamespace).Get(svcMetrics) if err == nil { // If there's no error, the metrics service already exists return nil } if !apierrors.IsNotFound(err) { return errors.NewError("error retrieving metrics service").WithCause(err).WithDetails(h.OriginLog()) } // Create metrics deployer service account routerSA := &kapi.ServiceAccount{} routerSA.Name = metricsDeployerSA _, err = kubeClient.ServiceAccounts(infraNamespace).Create(routerSA) if err != nil { return errors.NewError("cannot create metrics deployer service account").WithCause(err).WithDetails(h.OriginLog()) } // Add edit role to deployer service account roleBindingAccessor := policy.NewLocalRoleBindingAccessor(infraNamespace, osClient) addEditRole := policy.RoleModificationOptions{ RoleName: "edit", RoleBindingAccessor: roleBindingAccessor, Subjects: []kapi.ObjectReference{ { Namespace: infraNamespace, Name: metricsDeployerSA, Kind: "ServiceAccount", }, }, } if err = addEditRole.AddRole(); err != nil { return errors.NewError("cannot add edit role to metrics deployer service account").WithCause(err).WithDetails(h.OriginLog()) } // Add cluster reader role to heapster service account clusterRoleBindingAccessor := policy.NewClusterRoleBindingAccessor(osClient) addClusterReaderRole := policy.RoleModificationOptions{ RoleName: "cluster-reader", RoleBindingAccessor: clusterRoleBindingAccessor, Users: []string{"system:serviceaccount:openshift-infra:heapster"}, } if err = addClusterReaderRole.AddRole(); err != nil { return errors.NewError("cannot add cluster reader role to heapster service account").WithCause(err).WithDetails(h.OriginLog()) } // Create metrics deployer secret deployerSecret := &kapi.Secret{} deployerSecret.Name = metricsDeployerSecret deployerSecret.Data = map[string][]byte{"nothing": []byte("/dev/null")} if _, err = kubeClient.Secrets(infraNamespace).Create(deployerSecret); err != nil { return errors.NewError("cannot create metrics deployer secret").WithCause(err).WithDetails(h.OriginLog()) } // Create deployer Pod deployerPod := metricsDeployerPod(hostName, imagePrefix, imageVersion) if _, err = kubeClient.Pods(infraNamespace).Create(deployerPod); err != nil { return errors.NewError("cannot create metrics deployer pod").WithCause(err).WithDetails(h.OriginLog()) } return nil }
func TestNodeAuth(t *testing.T) { // Server config masterConfig, nodeConfig, adminKubeConfigFile, err := testserver.StartTestAllInOne() if err != nil { t.Fatalf("unexpected error: %v", err) } // Cluster admin clients and client configs adminClient, err := testutil.GetClusterAdminKubeClient(adminKubeConfigFile) if err != nil { t.Fatalf("unexpected error: %v", err) } originAdminClient, err := testutil.GetClusterAdminClient(adminKubeConfigFile) if err != nil { t.Fatalf("unexpected error: %v", err) } adminConfig, err := testutil.GetClusterAdminClientConfig(adminKubeConfigFile) if err != nil { t.Fatalf("unexpected error: %v", err) } // Client configs for lesser users masterKubeletClientConfig := configapi.GetKubeletClientConfig(*masterConfig) anonymousConfig := clientcmd.AnonymousClientConfig(*adminConfig) badTokenConfig := clientcmd.AnonymousClientConfig(*adminConfig) badTokenConfig.BearerToken = "bad-token" bobClient, _, bobConfig, err := testutil.GetClientForUser(*adminConfig, "bob") _, _, aliceConfig, err := testutil.GetClientForUser(*adminConfig, "alice") sa1Client, _, sa1Config, err := testutil.GetClientForServiceAccount(adminClient, *adminConfig, "default", "sa1") _, _, sa2Config, err := testutil.GetClientForServiceAccount(adminClient, *adminConfig, "default", "sa2") // Grant Bob system:node-reader, which should let them read metrics and stats addBob := &policy.RoleModificationOptions{ RoleName: bootstrappolicy.NodeReaderRoleName, RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(originAdminClient), Subjects: []kapi.ObjectReference{{Kind: "User", Name: "bob"}}, } if err := addBob.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } // Grant sa1 system:cluster-reader, which should let them read metrics and stats addSA1 := &policy.RoleModificationOptions{ RoleName: bootstrappolicy.ClusterReaderRoleName, RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(originAdminClient), Subjects: []kapi.ObjectReference{{Kind: "ServiceAccount", Namespace: "default", Name: "sa1"}}, } if err := addSA1.AddRole(); err != nil { t.Fatalf("unexpected error: %v", err) } // Wait for policy cache if err := testutil.WaitForClusterPolicyUpdate(bobClient, "get", "nodes/metrics", true); err != nil { t.Fatalf("unexpected error: %v", err) } if err := testutil.WaitForClusterPolicyUpdate(sa1Client, "get", "nodes/metrics", true); err != nil { t.Fatalf("unexpected error: %v", err) } _, nodePort, err := net.SplitHostPort(nodeConfig.ServingInfo.BindAddress) if err != nil { t.Fatalf("unexpected error: %v", err) } nodePortInt, err := strconv.ParseInt(nodePort, 0, 0) if err != nil { t.Fatalf("unexpected error: %v", err) } nodeTLS := configapi.UseTLS(nodeConfig.ServingInfo) kubeletClientConfig := func(config *kclient.Config) *kubeletclient.KubeletClientConfig { return &kubeletclient.KubeletClientConfig{ Port: uint(nodePortInt), EnableHttps: nodeTLS, TLSClientConfig: config.TLSClientConfig, BearerToken: config.BearerToken, } } testCases := map[string]struct { KubeletClientConfig *kubeletclient.KubeletClientConfig Forbidden bool NodeViewer bool NodeAdmin bool }{ "bad token": { KubeletClientConfig: kubeletClientConfig(&badTokenConfig), }, "anonymous": { KubeletClientConfig: kubeletClientConfig(&anonymousConfig), Forbidden: true, }, "cluster admin": { KubeletClientConfig: kubeletClientConfig(adminConfig), NodeAdmin: true, }, "master kubelet client": { KubeletClientConfig: masterKubeletClientConfig, NodeAdmin: true, }, "bob": { KubeletClientConfig: kubeletClientConfig(bobConfig), NodeViewer: true, }, "alice": { KubeletClientConfig: kubeletClientConfig(aliceConfig), Forbidden: true, }, "sa1": { KubeletClientConfig: kubeletClientConfig(sa1Config), NodeViewer: true, }, "sa2": { KubeletClientConfig: kubeletClientConfig(sa2Config), Forbidden: true, }, } for k, tc := range testCases { var ( // expected result for requests a viewer should be able to make viewResult int // expected result for requests an admin should be able to make (that can actually complete with a 200 in our tests) adminResultOK int // expected result for requests an admin should be able to make (that return a 404 in this test if the authn/authz layer is completed) adminResultMissing int ) switch { case tc.NodeAdmin: viewResult = http.StatusOK adminResultOK = http.StatusOK adminResultMissing = http.StatusNotFound case tc.NodeViewer: viewResult = http.StatusOK adminResultOK = http.StatusForbidden adminResultMissing = http.StatusForbidden case tc.Forbidden: viewResult = http.StatusForbidden adminResultOK = http.StatusForbidden adminResultMissing = http.StatusForbidden default: viewResult = http.StatusUnauthorized adminResultOK = http.StatusUnauthorized adminResultMissing = http.StatusUnauthorized } requests := []testRequest{ // Responses to invalid paths are the same for all users {"GET", "/", http.StatusNotFound}, {"GET", "/stats", http.StatusMovedPermanently}, // ServeMux redirects to the directory {"GET", "/logs", http.StatusMovedPermanently}, // ServeMux redirects to the directory {"GET", "/invalid", http.StatusNotFound}, // viewer requests {"GET", "/metrics", viewResult}, {"GET", "/stats/", viewResult}, {"POST", "/stats/", viewResult}, // stats requests can be POSTs which contain query options // successful admin requests {"GET", "/healthz", adminResultOK}, {"GET", "/pods", adminResultOK}, {"GET", "/logs/", adminResultOK}, // not found admin requests {"GET", "/containerLogs/mynamespace/mypod/mycontainer", adminResultMissing}, {"POST", "/exec/mynamespace/mypod/mycontainer", adminResultMissing}, {"POST", "/run/mynamespace/mypod/mycontainer", adminResultMissing}, {"POST", "/attach/mynamespace/mypod/mycontainer", adminResultMissing}, {"POST", "/portForward/mynamespace/mypod/mycontainer", adminResultMissing}, // GET is supported in origin on /exec and /attach for backwards compatibility // make sure node admin permissions are required {"GET", "/exec/mynamespace/mypod/mycontainer", adminResultMissing}, {"GET", "/attach/mynamespace/mypod/mycontainer", adminResultMissing}, } rt, err := kubeletclient.MakeTransport(tc.KubeletClientConfig) if err != nil { t.Errorf("%s: unexpected error: %v", k, err) continue } for _, r := range requests { req, err := http.NewRequest(r.Method, "https://"+nodeConfig.NodeName+":10250"+r.Path, nil) if err != nil { t.Errorf("%s: %s: unexpected error: %v", k, r.Path, err) continue } resp, err := rt.RoundTrip(req) if err != nil { t.Errorf("%s: %s: unexpected error: %v", k, r.Path, err) continue } resp.Body.Close() if resp.StatusCode != r.Result { t.Errorf("%s: %s: expected %d, got %d", k, r.Path, r.Result, resp.StatusCode) continue } } } }
func TestServiceAccountAuthorization(t *testing.T) { saNamespace := api.NamespaceDefault saName := serviceaccountadmission.DefaultServiceAccountName saUsername := serviceaccount.MakeUsername(saNamespace, saName) // Start one OpenShift master as "cluster1" to play the external kube server cluster1MasterConfig, cluster1AdminConfigFile, err := testserver.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } cluster1AdminConfig, err := testutil.GetClusterAdminClientConfig(cluster1AdminConfigFile) if err != nil { t.Fatalf("unexpected error: %v", err) } cluster1AdminKubeClient, err := testutil.GetClusterAdminKubeClient(cluster1AdminConfigFile) if err != nil { t.Fatalf("unexpected error: %v", err) } cluster1AdminOSClient, err := testutil.GetClusterAdminClient(cluster1AdminConfigFile) if err != nil { t.Fatalf("unexpected error: %v", err) } // Get a service account token and build a client saToken, err := waitForServiceAccountToken(cluster1AdminKubeClient, saNamespace, saName, 20, time.Second) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(saToken) == 0 { t.Fatalf("token was not created") } cluster1SAClientConfig := kclient.Config{ Host: cluster1AdminConfig.Host, Prefix: cluster1AdminConfig.Prefix, BearerToken: saToken, TLSClientConfig: kclient.TLSClientConfig{ CAFile: cluster1AdminConfig.CAFile, CAData: cluster1AdminConfig.CAData, }, } cluster1SAKubeClient, err := kclient.New(&cluster1SAClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // Make sure the service account doesn't have access failNS := &api.Namespace{ObjectMeta: api.ObjectMeta{Name: "test-fail"}} if _, err := cluster1SAKubeClient.Namespaces().Create(failNS); !errors.IsForbidden(err) { t.Fatalf("expected forbidden error, got %v", err) } // Make the service account a cluster admin on cluster1 addRoleOptions := &policy.RoleModificationOptions{ RoleName: bootstrappolicy.ClusterAdminRoleName, RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(cluster1AdminOSClient), Users: []string{saUsername}, } if err := addRoleOptions.AddRole(); err != nil { t.Fatalf("could not add role to service account") } // Give the policy cache a second to catch it's breath time.Sleep(time.Second) // Make sure the service account now has access // This tests authentication using the etcd-based token getter passNS := &api.Namespace{ObjectMeta: api.ObjectMeta{Name: "test-pass"}} if _, err := cluster1SAKubeClient.Namespaces().Create(passNS); err != nil { t.Fatalf("unexpected error: %v", err) } // Create a kubeconfig from the serviceaccount config cluster1SAKubeConfigFile, err := ioutil.TempFile(testutil.GetBaseDir(), "cluster1-service-account.kubeconfig") if err != nil { t.Fatalf("error creating tmpfile: %v", err) } defer os.Remove(cluster1SAKubeConfigFile.Name()) if err := writeClientConfigToKubeConfig(cluster1SAClientConfig, cluster1SAKubeConfigFile.Name()); err != nil { t.Fatalf("error creating kubeconfig: %v", err) } // Set up cluster 2 to run against cluster 1 as external kubernetes cluster2MasterConfig, err := testserver.DefaultMasterOptions() if err != nil { t.Fatalf("unexpected error: %v", err) } // Don't start kubernetes in process cluster2MasterConfig.KubernetesMasterConfig = nil // Connect to cluster1 using the service account credentials cluster2MasterConfig.MasterClients.ExternalKubernetesKubeConfig = cluster1SAKubeConfigFile.Name() // Don't start etcd cluster2MasterConfig.EtcdConfig = nil // Use the same credentials as cluster1 to connect to existing etcd cluster2MasterConfig.EtcdClientInfo = cluster1MasterConfig.EtcdClientInfo // Set a custom etcd prefix to make sure data is getting sent to cluster1 cluster2MasterConfig.EtcdStorageConfig.KubernetesStoragePrefix += "2" cluster2MasterConfig.EtcdStorageConfig.OpenShiftStoragePrefix += "2" // Don't manage any names in cluster2 cluster2MasterConfig.ServiceAccountConfig.ManagedNames = []string{} // Don't create any service account tokens in cluster2 cluster2MasterConfig.ServiceAccountConfig.PrivateKeyFile = "" // Use the same public keys to validate tokens as cluster1 cluster2MasterConfig.ServiceAccountConfig.PublicKeyFiles = cluster1MasterConfig.ServiceAccountConfig.PublicKeyFiles // don't try to start second dns server cluster2MasterConfig.DNSConfig = nil // Start cluster 2 (without clearing etcd) and get admin client configs and clients cluster2Options := testserver.TestOptions{DeleteAllEtcdKeys: false} cluster2AdminConfigFile, err := testserver.StartConfiguredMasterWithOptions(cluster2MasterConfig, cluster2Options) if err != nil { t.Fatalf("unexpected error: %v", err) } cluster2AdminConfig, err := testutil.GetClusterAdminClientConfig(cluster2AdminConfigFile) if err != nil { t.Fatalf("unexpected error: %v", err) } cluster2AdminOSClient, err := testutil.GetClusterAdminClient(cluster2AdminConfigFile) if err != nil { t.Fatalf("unexpected error: %v", err) } // Build a client to use the same service account token against cluster2 cluster2SAClientConfig := cluster1SAClientConfig cluster2SAClientConfig.Host = cluster2AdminConfig.Host cluster2SAKubeClient, err := kclient.New(&cluster2SAClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // Make sure the service account doesn't have access // A forbidden error makes sure the token was recognized, and policy denied us // This exercises the client-based token getter // It also makes sure we don't loop back through the cluster2 kube proxy which would cause an auth loop failNS2 := &api.Namespace{ObjectMeta: api.ObjectMeta{Name: "test-fail2"}} if _, err := cluster2SAKubeClient.Namespaces().Create(failNS2); !errors.IsForbidden(err) { t.Fatalf("expected forbidden error, got %v", err) } // Make the service account a cluster admin on cluster2 addRoleOptions2 := &policy.RoleModificationOptions{ RoleName: bootstrappolicy.ClusterAdminRoleName, RoleBindingAccessor: policy.NewClusterRoleBindingAccessor(cluster2AdminOSClient), Users: []string{saUsername}, } if err := addRoleOptions2.AddRole(); err != nil { t.Fatalf("could not add role to service account") } // Give the policy cache a second to catch it's breath time.Sleep(time.Second) // Make sure the service account now has access to cluster2 passNS2 := &api.Namespace{ObjectMeta: api.ObjectMeta{Name: "test-pass2"}} if _, err := cluster2SAKubeClient.Namespaces().Create(passNS2); err != nil { t.Fatalf("unexpected error: %v", err) } // Make sure the ns actually got created in cluster1 if _, err := cluster1SAKubeClient.Namespaces().Get(passNS2.Name); err != nil { t.Fatalf("unexpected error: %v", err) } }