che-operator/pkg/controller/che/workspace_namespace_permiss...

379 lines
14 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

//
// Copyright (c) 2012-2019 Red Hat, Inc.
// This program and the accompanying materials are made
// available under the terms of the Eclipse Public License 2.0
// which is available at https://www.eclipse.org/legal/epl-2.0/
//
// SPDX-License-Identifier: EPL-2.0
//
// Contributors: // Contributors:
// Red Hat, Inc. - initial API and implementation // Red Hat, Inc. - initial API and implementation
//
package che
import (
"fmt"
"time"
orgv1 "github.com/eclipse/che-operator/pkg/apis/org/v1"
"github.com/eclipse/che-operator/pkg/deploy"
"github.com/eclipse/che-operator/pkg/util"
"github.com/sirupsen/logrus"
rbac "k8s.io/api/rbac/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
const (
// EditClusterRoleName - default "edit" cluster role. This role is pre-created on the cluster.
// See more: https://kubernetes.io/blog/2017/10/using-rbac-generally-available-18/#granting-access-to-users
EditClusterRoleName = "edit"
// EditRoleBindingName - "edit" rolebinding for che-server.
EditRoleBindingName = "che"
// CheWorkspacesServiceAccount - service account created for Che workspaces.
CheWorkspacesServiceAccount = "che-workspace"
// ViewRoleBindingName - "view" role for "che-workspace" service account.
ViewRoleBindingName = "che-workspace-view"
// ExecRoleBindingName - "exec" role for "che-workspace" service account.
ExecRoleBindingName = "che-workspace-exec"
// CheWorkspacesNamespaceClusterRoleNameTemplate - manage namespaces "cluster role" and "clusterrolebinding" template name
CheWorkspacesNamespaceClusterRoleNameTemplate = "%s-cheworkspaces-namespaces-clusterrole"
// CheWorkspacesClusterRoleNameTemplate - manage workspaces "cluster role" and "clusterrolebinding" template name
CheWorkspacesClusterRoleNameTemplate = "%s-cheworkspaces-clusterrole"
)
// delegateWorkspacePermissionsInTheSameNamespaceWithChe - creates "che-workspace" service account(for Che workspaces) and
// delegates "che-operator" SA permissions to the service accounts: "che" and "che-workspace".
// Also this method binds "edit" default k8s clusterrole using rolebinding to "che" SA.
func (r *ReconcileChe) delegateWorkspacePermissionsInTheSameNamespaceWithChe(deployContext *deploy.DeployContext) (reconcile.Result, error) {
tests := r.tests
// Create "che-workspace" service account.
// Che workspace components use this service account.
cheWorkspaceSA, err := deploy.SyncServiceAccountToCluster(deployContext, CheWorkspacesServiceAccount)
if cheWorkspaceSA == nil {
logrus.Infof("Waiting on service account '%s' to be created", CheWorkspacesServiceAccount)
if err != nil {
logrus.Error(err)
}
if !tests {
return reconcile.Result{}, err
}
}
// Create view role for "che-workspace" service account.
// This role used by exec terminals, tasks, metric che-theia plugin and so on.
viewRole, err := deploy.SyncViewRoleToCluster(deployContext)
if viewRole == nil {
logrus.Infof("Waiting on role '%s' to be created", deploy.ViewRoleName)
if err != nil {
logrus.Error(err)
}
if !tests {
return reconcile.Result{}, err
}
}
cheWSViewRoleBinding, err := deploy.SyncRoleBindingToCluster(deployContext, ViewRoleBindingName, CheWorkspacesServiceAccount, deploy.ViewRoleName, "Role")
if cheWSViewRoleBinding == nil {
logrus.Infof("Waiting on role binding '%s' to be created", ViewRoleBindingName)
if err != nil {
logrus.Error(err)
}
if !tests {
return reconcile.Result{}, err
}
}
// Create exec role for "che-workspaces" service account.
// This role used by exec terminals, tasks and so on.
execRole, err := deploy.SyncExecRoleToCluster(deployContext)
if execRole == nil {
logrus.Infof("Waiting on role '%s' to be created", deploy.ExecRoleName)
if err != nil {
logrus.Error(err)
}
if !tests {
return reconcile.Result{}, err
}
}
cheWSExecRoleBinding, err := deploy.SyncRoleBindingToCluster(deployContext, ExecRoleBindingName, CheWorkspacesServiceAccount, deploy.ExecRoleName, "Role")
if cheWSExecRoleBinding == nil {
logrus.Infof("Waiting on role binding '%s' to be created", ExecRoleBindingName)
if err != nil {
logrus.Error(err)
}
if !tests {
return reconcile.Result{}, err
}
}
// Bind "edit" cluster role for "che" service account.
// che-operator doesn't create "edit" role. This role is pre-created on the cluster.
// Warning: operator binds clusterrole using rolebinding(not clusterrolebinding).
// That's why "che" service account has got permissions only in the one namespace!
// So permissions are binding in "non-cluster" scope.
cheRoleBinding, err := deploy.SyncRoleBindingToCluster(deployContext, EditRoleBindingName, CheServiceAccountName, EditClusterRoleName, "ClusterRole")
if cheRoleBinding == nil {
logrus.Infof("Waiting on role binding '%s' to be created", EditRoleBindingName)
if err != nil {
logrus.Error(err)
}
if !tests {
return reconcile.Result{}, err
}
}
return reconcile.Result{}, nil
}
// Create cluster roles and cluster role bindings for "che" service account.
// che-server uses "che" service account for creation new workspaces and workspace components.
// Operator will create two cluster roles:
// - "<workspace-namespace/project-name>-cheworkspaces-namespaces-clusterrole" - cluster role to mange namespace(for Kubernetes platform)
// or project(for Openshift platform) for new workspace.
// - "<workspace-namespace/project-name>-cheworkspaces-clusterrole" - cluster role to create and manage k8s objects required for
// workspace components.
// Notice: After permission delegation che-server will create service account "che-workspace" ITSELF with
// "exec" and "view" roles for each new workspace.
func (r *ReconcileChe) delegateWorkspacePermissionsInTheDifferNamespaceThanChe(instance *orgv1.CheCluster, deployContext *deploy.DeployContext) (reconcile.Result, error) {
tests := r.tests
сheWorkspacesNamespaceClusterRoleName := fmt.Sprintf(CheWorkspacesNamespaceClusterRoleNameTemplate, instance.Namespace)
сheWorkspacesNamespaceClusterRoleBindingName := сheWorkspacesNamespaceClusterRoleName
// Create clusterrole "<workspace-namespace/project-name>-clusterrole-manage-namespaces" to manage namespace/projects for Che workspaces.
provisioned, err := deploy.SyncClusterRoleToCheCluster(deployContext, сheWorkspacesNamespaceClusterRoleName, getCheWorkspacesNamespacePolicy())
if !provisioned {
logrus.Infof("Waiting on clusterrole '%s' to be created", сheWorkspacesNamespaceClusterRoleName)
if err != nil {
logrus.Error(err)
}
if !tests {
return reconcile.Result{RequeueAfter: time.Second}, err
}
}
сheWorkspacesNamespaceClusterRoleBinding, err := deploy.SyncClusterRoleBindingToCluster(deployContext, сheWorkspacesNamespaceClusterRoleBindingName, CheServiceAccountName, сheWorkspacesNamespaceClusterRoleName)
if сheWorkspacesNamespaceClusterRoleBinding == nil {
logrus.Infof("Waiting on clusterrolebinding '%s' to be created", сheWorkspacesNamespaceClusterRoleBindingName)
if err != nil {
logrus.Error(err)
}
if !tests {
return reconcile.Result{RequeueAfter: time.Second}, err
}
}
сheWorkspacesClusterRoleName := fmt.Sprintf(CheWorkspacesClusterRoleNameTemplate, instance.Namespace)
сheWorkspacesClusterRoleBindingName := сheWorkspacesClusterRoleName
// Create clusterrole "<workspace-namespace/project-name>-cheworkspaces-namespaces-clusterrole" to create k8s components for Che workspaces.
provisioned, err = deploy.SyncClusterRoleToCheCluster(deployContext, сheWorkspacesClusterRoleName, getCheWorkspacesPolicy())
if !provisioned {
logrus.Infof("Waiting on clusterrole '%s' to be created", сheWorkspacesClusterRoleName)
if err != nil {
logrus.Error(err)
}
if !tests {
return reconcile.Result{RequeueAfter: time.Second}, err
}
}
cheManageNamespacesRolebinding, err := deploy.SyncClusterRoleBindingToCluster(deployContext, сheWorkspacesClusterRoleBindingName, CheServiceAccountName, сheWorkspacesClusterRoleName)
if cheManageNamespacesRolebinding == nil {
logrus.Infof("Waiting on clusterrolebinding '%s' to be created", сheWorkspacesClusterRoleBindingName)
if err != nil {
logrus.Error(err)
}
if !tests {
return reconcile.Result{RequeueAfter: time.Second}, err
}
}
return reconcile.Result{}, nil
}
// DeleteWorkspacesInSameNamespaceWithChePermissions - removes workspaces in same namespace with Che role and rolebindings.
func (r *ReconcileChe) DeleteWorkspacesInSameNamespaceWithChePermissions(instance *orgv1.CheCluster, cli client.Client) error {
if err := deploy.DeleteRole(deploy.ExecRoleName, instance.Namespace, cli); err != nil {
return err
}
if err := deploy.DeleteRoleBinding(ExecRoleBindingName, instance.Namespace, cli); err != nil {
return err
}
if err := deploy.DeleteRole(deploy.ViewRoleName, instance.Namespace, cli); err != nil {
return err
}
if err := deploy.DeleteRoleBinding(ViewRoleBindingName, instance.Namespace, cli); err != nil {
return err
}
if err := deploy.DeleteRoleBinding(EditRoleBindingName, instance.Namespace, cli); err != nil {
return err
}
return nil
}
func (r *ReconcileChe) reconcileWorkspacePermissionsFinalizer(instance *orgv1.CheCluster, deployContext *deploy.DeployContext) error {
if !util.IsOAuthEnabled(instance) {
if util.IsWorkspaceInSameNamespaceWithChe(instance) {
// Delete workspaces cluster permission set and finalizer from CR if deletion timestamp is not 0.
if err := r.RemoveCheWorkspacesClusterPermissions(instance); err != nil {
logrus.Errorf("workspace permissions finalizers was not removed from CR, cause %s", err.Error())
return err
}
} else {
// Delete permission set for configuration "same namespace for Che and workspaces".
if err := r.DeleteWorkspacesInSameNamespaceWithChePermissions(instance, deployContext.ClusterAPI.Client); err != nil {
logrus.Errorf("unable to delete workspaces in same namespace permission set, cause %s", err.Error())
return err
}
// Add workspaces cluster permission finalizer to the CR if deletion timestamp is 0.
// Or delete workspaces cluster permission set and finalizer from CR if deletion timestamp is not 0.
if err := r.ReconcileCheWorkspacesClusterPermissionsFinalizer(instance); err != nil {
logrus.Errorf("unable to add workspace permissions finalizers to the CR, cause %s", err.Error())
return err
}
}
} else {
// Delete workspaces cluster permission set and finalizer from CR if deletion timestamp is not 0.
if err := r.RemoveCheWorkspacesClusterPermissions(instance); err != nil {
logrus.Errorf("workspace permissions finalizers was not removed from CR, cause %s", err.Error())
return err
}
}
return nil
}
func getCheWorkspacesNamespacePolicy() []rbac.PolicyRule {
k8sPolicies := []rbac.PolicyRule{
{
APIGroups: []string{""},
Resources: []string{"namespaces"},
Verbs: []string{"get", "create", "update"},
},
}
openshiftPolicies := []rbac.PolicyRule{
{
APIGroups: []string{"project.openshift.io"},
Resources: []string{"projectrequests"},
Verbs: []string{"create", "update"},
},
{
APIGroups: []string{"project.openshift.io"},
Resources: []string{"projects"},
Verbs: []string{"get"},
},
}
if util.IsOpenShift {
return append(k8sPolicies, openshiftPolicies...)
}
return k8sPolicies
}
func getCheWorkspacesPolicy() []rbac.PolicyRule {
k8sPolicies := []rbac.PolicyRule{
{
APIGroups: []string{""},
Resources: []string{"serviceaccounts"},
Verbs: []string{"get", "create", "watch"},
},
{
APIGroups: []string{""},
Resources: []string{"pods/exec"},
Verbs: []string{"create"},
},
{
APIGroups: []string{""},
Resources: []string{"persistentvolumeclaims", "configmaps"},
Verbs: []string{"list"},
},
{
APIGroups: []string{""},
Resources: []string{"secrets"},
Verbs: []string{"list", "create", "delete"},
},
{
APIGroups: []string{""},
Resources: []string{"persistentvolumeclaims"},
Verbs: []string{"get", "create", "watch"},
},
{
APIGroups: []string{""},
Resources: []string{"pods"},
Verbs: []string{"get", "create", "list", "watch", "delete"},
},
{
APIGroups: []string{""},
Resources: []string{"services"},
Verbs: []string{"create", "list", "delete"},
},
{
APIGroups: []string{""},
Resources: []string{"configmaps"},
Verbs: []string{"get", "create", "delete"},
},
{
APIGroups: []string{""},
Resources: []string{"events"},
Verbs: []string{"watch"},
},
{
APIGroups: []string{"apps"},
Resources: []string{"secrets"},
Verbs: []string{"list"},
},
{
APIGroups: []string{"apps"},
Resources: []string{"deployments"},
Verbs: []string{"get", "create", "list", "watch", "patch", "delete"},
},
{
APIGroups: []string{"apps"},
Resources: []string{"replicasets"},
Verbs: []string{"list", "get", "patch", "delete"},
},
{
APIGroups: []string{"extensions"},
Resources: []string{"ingresses"},
Verbs: []string{"list", "create", "watch", "get", "delete"},
},
{
APIGroups: []string{"rbac.authorization.k8s.io"},
Resources: []string{"roles"},
Verbs: []string{"get", "create"},
},
{
APIGroups: []string{"rbac.authorization.k8s.io"},
Resources: []string{"rolebindings"},
Verbs: []string{"get", "update", "create"},
},
}
openshiftPolicies := []rbac.PolicyRule{
{
APIGroups: []string{"route.openshift.io"},
Resources: []string{"routes"},
Verbs: []string{"list", "create", "delete"},
},
{
APIGroups: []string{"authorization.openshift.io"},
Resources: []string{"roles"},
Verbs: []string{"get", "create"},
},
{
APIGroups: []string{"authorization.openshift.io"},
Resources: []string{"rolebindings"},
Verbs: []string{"get", "update", "create"},
},
}
if util.IsOpenShift {
return append(k8sPolicies, openshiftPolicies...)
}
return k8sPolicies
}