func GetBootstrapServiceAccountProjectRoleBindings(namespace string) []authorizationapi.RoleBinding { return []authorizationapi.RoleBinding{ { ObjectMeta: kapi.ObjectMeta{ Name: ImagePullerRoleBindingName, Namespace: namespace, }, RoleRef: kapi.ObjectReference{ Name: ImagePullerRoleName, }, Groups: util.NewStringSet(serviceaccount.MakeNamespaceGroupName(namespace)), }, { ObjectMeta: kapi.ObjectMeta{ Name: ImageBuilderRoleBindingName, Namespace: namespace, }, RoleRef: kapi.ObjectReference{ Name: ImageBuilderRoleName, }, Users: util.NewStringSet(serviceaccount.MakeUsername(namespace, BuilderServiceAccountName)), }, { ObjectMeta: kapi.ObjectMeta{ Name: DeployerRoleBindingName, Namespace: namespace, }, RoleRef: kapi.ObjectReference{ Name: DeployerRoleName, }, Users: util.NewStringSet(serviceaccount.MakeUsername(namespace, DeployerServiceAccountName)), }, } }
// GetBoostrapSCCAccess provides the default set of access that should be passed to GetBootstrapSecurityContextConstraints. func GetBoostrapSCCAccess(infraNamespace string) (map[string][]string, map[string][]string) { groups := map[string][]string{ SecurityContextConstraintPrivileged: {ClusterAdminGroup, NodesGroup}, SecurityContextConstraintsAnyUID: {ClusterAdminGroup}, SecurityContextConstraintRestricted: {AuthenticatedGroup}, } buildControllerUsername := serviceaccount.MakeUsername(infraNamespace, InfraBuildControllerServiceAccountName) pvControllerUsername := serviceaccount.MakeUsername(infraNamespace, InfraPersistentVolumeControllerServiceAccountName) users := map[string][]string{ SecurityContextConstraintPrivileged: {buildControllerUsername}, SecurityContextConstraintHostMount: {pvControllerUsername}, } return groups, users }
func getExpectedAccess() (map[string][]string, map[string][]string) { groups := map[string][]string{ SecurityContextConstraintPrivileged: {ClusterAdminGroup, NodesGroup}, SecurityContextConstraintsAnyUID: {ClusterAdminGroup}, SecurityContextConstraintRestricted: {AuthenticatedGroup}, } buildControllerUsername := serviceaccount.MakeUsername(DefaultOpenShiftInfraNamespace, InfraBuildControllerServiceAccountName) pvControllerUsername := serviceaccount.MakeUsername(DefaultOpenShiftInfraNamespace, InfraPersistentVolumeBinderControllerServiceAccountName) users := map[string][]string{ SecurityContextConstraintPrivileged: {buildControllerUsername}, SecurityContextConstraintHostMountAndAnyUID: {pvControllerUsername}, } return groups, users }
// StringSubjectsFor returns users and groups for comparison against user.Info. currentNamespace is used to // to create usernames for service accounts where namespace=="". func StringSubjectsFor(currentNamespace string, subjects []kapi.ObjectReference) ([]string, []string) { // these MUST be nil to indicate empty var users, groups []string for _, subject := range subjects { switch subject.Kind { case ServiceAccountKind: namespace := currentNamespace if len(subject.Namespace) > 0 { namespace = subject.Namespace } if len(namespace) > 0 { users = append(users, serviceaccount.MakeUsername(namespace, subject.Name)) } case UserKind, SystemUserKind: users = append(users, subject.Name) case GroupKind, SystemGroupKind: groups = append(groups, subject.Name) } } return users, groups }
func (c *MasterConfig) ensureDefaultSecurityContextConstraints() { sccList, err := c.KubeClient().SecurityContextConstraints().List(labels.Everything(), fields.Everything()) if err != nil { glog.Errorf("Unable to initialize security context constraints: %v. This may prevent the creation of pods", err) return } if len(sccList.Items) > 0 { return } glog.Infof("No security context constraints detected, adding defaults") // add the build user to the privileged SCC access ns := c.Options.PolicyConfig.OpenShiftInfrastructureNamespace buildControllerUsername := serviceaccount.MakeUsername(ns, c.BuildControllerServiceAccount) bootstrapSCCGroups, bootstrapSCCUsers := bootstrappolicy.GetBoostrapSCCAccess() bootstrapSCCUsers[bootstrappolicy.SecurityContextConstraintPrivileged] = append(bootstrapSCCUsers[bootstrappolicy.SecurityContextConstraintPrivileged], buildControllerUsername) for _, scc := range bootstrappolicy.GetBootstrapSecurityContextConstraints(bootstrapSCCGroups, bootstrapSCCUsers) { _, err = c.KubeClient().SecurityContextConstraints().Create(&scc) if err != nil { glog.Errorf("Unable to create default security context constraint %s. Got error: %v", scc.Name, err) } } }
func (o *RoleModificationOptions) CompleteUserWithSA(f *clientcmd.Factory, args []string, saNames util.StringList) error { if (len(args) < 2) && (len(saNames) == 0) { return errors.New("You must specify at least two arguments: <role> <user> [user]...") } o.RoleName = args[0] if len(args) > 1 { o.Users = append(o.Users, args[1:]...) } osClient, _, err := f.Clients() if err != nil { return err } roleBindingNamespace, _, err := f.DefaultNamespace() if err != nil { return err } o.RoleBindingAccessor = NewLocalRoleBindingAccessor(roleBindingNamespace, osClient) for _, sa := range saNames { o.Users = append(o.Users, serviceaccount.MakeUsername(roleBindingNamespace, sa)) } return nil }
// 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) } } }
// StringSubjectsFor returns users and groups for comparison against user.Info. currentNamespace is used to // to create usernames for service accounts where namespace=="". func StringSubjectsFor(currentNamespace string, subjects []kapi.ObjectReference) ([]string, []string) { users := []string{} groups := []string{} for _, subject := range subjects { switch subject.Kind { case ServiceAccountKind: namespace := currentNamespace if len(subject.Namespace) > 0 { namespace = subject.Namespace } users = append(users, serviceaccount.MakeUsername(namespace, subject.Name)) case UserKind, SystemUserKind: users = append(users, subject.Name) case GroupKind, SystemGroupKind: groups = append(groups, subject.Name) } } return users, groups }
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) } }
func TestLocalBindingOtherServiceAccountSubject(t *testing.T) { test := &authorizeTest{ context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), "adze"), &user.DefaultInfo{Name: serviceaccount.MakeUsername("other", "first")}), attributes: &DefaultAuthorizationAttributes{ Verb: "get", Resource: "pods", }, expectedAllowed: true, expectedReason: "allowed by rule in adze", } test.clusterPolicies = newDefaultClusterPolicies() test.policies = append(test.policies, newAdzePolicies()...) test.clusterBindings = newDefaultClusterPolicyBindings() test.bindings = append(test.bindings, newAdzeBindings()...) test.test(t) }
func TestClusterBindingServiceAccountSubject(t *testing.T) { test := &authorizeTest{ context: kapi.WithUser(kapi.WithNamespace(kapi.NewContext(), kapi.NamespaceNone), &user.DefaultInfo{Name: serviceaccount.MakeUsername("foo", "default")}), attributes: &DefaultAuthorizationAttributes{ Verb: "get", Resource: "users", ResourceName: "any", }, expectedAllowed: true, expectedReason: "allowed by cluster rule", } test.clusterPolicies = newDefaultClusterPolicies() test.clusterBindings = newDefaultClusterPolicyBindings() test.test(t) }