che-operator/pkg/deploy/server/rbac.go

297 lines
9.1 KiB
Go

//
// Copyright (c) 2019-2023 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:
// Red Hat, Inc. - initial API and implementation
//
package server
import (
"fmt"
"strings"
"github.com/devfile/devworkspace-operator/pkg/infrastructure"
util "github.com/eclipse-che/che-operator/pkg/common/utils"
"github.com/sirupsen/logrus"
"github.com/eclipse-che/che-operator/pkg/common/chetypes"
"github.com/eclipse-che/che-operator/pkg/common/constants"
"github.com/eclipse-che/che-operator/pkg/deploy"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/types"
)
const (
userCommonPermissionsTemplateName = "%s-cheworkspaces-clusterrole"
userDevWorkspacePermissionsTemplateName = "%s-cheworkspaces-devworkspace-clusterrole"
cheSASpecificPermissionsTemplateName = "%s-cheworkspaces-namespaces-clusterrole"
)
// Create ClusterRole and ClusterRoleBinding for "che" service account.
// che-server uses "che" service account for creation RBAC for a user in his namespace.
func (s *CheServerReconciler) syncPermissions(ctx *chetypes.DeployContext) (bool, error) {
policies := map[string][]rbacv1.PolicyRule{
fmt.Sprintf(userCommonPermissionsTemplateName, ctx.CheCluster.Namespace): s.getUserCommonPolicies(),
fmt.Sprintf(cheSASpecificPermissionsTemplateName, ctx.CheCluster.Namespace): s.getCheSASpecificPolicies(),
fmt.Sprintf(userDevWorkspacePermissionsTemplateName, ctx.CheCluster.Namespace): s.getUserDevWorkspacePolicies(),
}
for name, policy := range policies {
if done, err := deploy.SyncClusterRoleToCluster(ctx, name, policy); !done {
return false, err
}
if done, err := deploy.SyncClusterRoleBindingToCluster(ctx, name, constants.DefaultCheServiceAccountName, name); !done {
return false, err
}
}
for _, cheClusterRole := range ctx.CheCluster.Spec.Components.CheServer.ClusterRoles {
cheClusterRole := strings.TrimSpace(cheClusterRole)
if cheClusterRole != "" {
if done, err := deploy.SyncClusterRoleBindingToCluster(ctx, cheClusterRole, constants.DefaultCheServiceAccountName, cheClusterRole); !done {
return false, err
}
finalizer := s.getCRBFinalizerName(cheClusterRole)
if err := deploy.AppendFinalizer(ctx, finalizer); err != nil {
return false, err
}
}
}
// Delete abandoned CRBs
for _, finalizer := range ctx.CheCluster.Finalizers {
if strings.HasSuffix(finalizer, cheCRBFinalizerSuffix) {
cheClusterRole := strings.TrimSuffix(finalizer, cheCRBFinalizerSuffix)
if !util.Contains(ctx.CheCluster.Spec.Components.CheServer.ClusterRoles, cheClusterRole) {
if done, err := deploy.Delete(ctx, types.NamespacedName{Name: cheClusterRole}, &rbacv1.ClusterRoleBinding{}); !done {
return false, err
}
if err := deploy.DeleteFinalizer(ctx, finalizer); err != nil {
return false, err
}
}
}
}
return true, nil
}
func (s *CheServerReconciler) deletePermissions(ctx *chetypes.DeployContext) bool {
names := []string{
fmt.Sprintf(userCommonPermissionsTemplateName, ctx.CheCluster.Namespace),
fmt.Sprintf(cheSASpecificPermissionsTemplateName, ctx.CheCluster.Namespace),
fmt.Sprintf(userDevWorkspacePermissionsTemplateName, ctx.CheCluster.Namespace),
}
done := true
for _, name := range names {
if _, err := deploy.Delete(ctx, types.NamespacedName{Name: name}, &rbacv1.ClusterRole{}); err != nil {
done = false
logrus.Errorf("Failed to delete ClusterRole '%s', cause: %v", name, err)
}
if _, err := deploy.Delete(ctx, types.NamespacedName{Name: name}, &rbacv1.ClusterRoleBinding{}); err != nil {
done = false
logrus.Errorf("Failed to delete ClusterRoleBinding '%s', cause: %v", name, err)
}
}
for _, name := range ctx.CheCluster.Spec.Components.CheServer.ClusterRoles {
name := strings.TrimSpace(name)
if name != "" {
if _, err := deploy.Delete(ctx, types.NamespacedName{Name: name}, &rbacv1.ClusterRoleBinding{}); err != nil {
done = false
logrus.Errorf("Failed to delete ClusterRoleBinding '%s', cause: %v", name, err)
}
// Removes any legacy CRB https://github.com/eclipse/che/issues/19506
legacyName := ctx.CheCluster.Namespace + "-" + constants.DefaultCheServiceAccountName + "-" + name
if _, err := deploy.Delete(ctx, types.NamespacedName{Name: legacyName}, &rbacv1.ClusterRoleBinding{}); err != nil {
done = false
logrus.Errorf("Failed to delete ClusterRoleBinding '%s', cause: %v", legacyName, err)
}
}
}
return done
}
func (s *CheServerReconciler) getUserDevWorkspacePolicies() []rbacv1.PolicyRule {
k8sPolicies := []rbacv1.PolicyRule{
{
APIGroups: []string{"workspace.devfile.io"},
Resources: []string{"devworkspaces", "devworkspacetemplates"},
Verbs: []string{"get", "create", "delete", "list", "update", "patch", "watch"},
},
}
return k8sPolicies
}
func (s *CheServerReconciler) getCheSASpecificPolicies() []rbacv1.PolicyRule {
k8sPolicies := []rbacv1.PolicyRule{
{
APIGroups: []string{""},
Resources: []string{"namespaces"},
Verbs: []string{"get", "create", "update", "list"},
},
{
APIGroups: []string{""},
Resources: []string{"serviceaccounts"},
Verbs: []string{"get", "watch", "create"},
},
{
APIGroups: []string{"rbac.authorization.k8s.io"},
Resources: []string{"roles"},
Verbs: []string{"get", "create", "update"},
},
{
APIGroups: []string{"rbac.authorization.k8s.io"},
Resources: []string{"rolebindings"},
Verbs: []string{"get", "create", "update", "delete"},
},
}
openshiftPolicies := []rbacv1.PolicyRule{
{
APIGroups: []string{"project.openshift.io"},
Resources: []string{"projectrequests"},
Verbs: []string{"create", "update"},
},
{
APIGroups: []string{"project.openshift.io"},
Resources: []string{"projects"},
Verbs: []string{"get", "list"},
},
{
APIGroups: []string{"user.openshift.io"},
Resources: []string{"groups"},
Verbs: []string{"get"},
},
{
APIGroups: []string{"authorization.openshift.io"},
Resources: []string{"roles"},
Verbs: []string{"get", "create", "update"},
},
{
APIGroups: []string{"authorization.openshift.io"},
Resources: []string{"rolebindings"},
Verbs: []string{"get", "create", "update", "delete"},
},
}
if infrastructure.IsOpenShift() {
return append(k8sPolicies, openshiftPolicies...)
}
return k8sPolicies
}
func (s *CheServerReconciler) getUserCommonPolicies() []rbacv1.PolicyRule {
k8sPolicies := []rbacv1.PolicyRule{
{
APIGroups: []string{""},
Resources: []string{"pods/exec"},
Verbs: []string{"get", "create"},
},
{
APIGroups: []string{""},
Resources: []string{"pods/log"},
Verbs: []string{"get", "list", "watch"},
},
{
APIGroups: []string{""},
Resources: []string{"pods/portforward"},
Verbs: []string{"get", "list", "create"},
},
{
APIGroups: []string{""},
Resources: []string{"secrets"},
Verbs: []string{"get", "list", "create", "update", "patch", "delete"},
},
{
APIGroups: []string{""},
Resources: []string{"persistentvolumeclaims"},
Verbs: []string{"get", "list", "watch", "create", "delete", "update", "patch"},
},
{
APIGroups: []string{""},
Resources: []string{"pods"},
Verbs: []string{"get", "list", "watch", "create", "delete", "update", "patch"},
},
{
APIGroups: []string{""},
Resources: []string{"services"},
Verbs: []string{"get", "list", "create", "delete", "update", "patch"},
},
{
APIGroups: []string{""},
Resources: []string{"configmaps"},
Verbs: []string{"get", "list", "create", "update", "patch", "delete"},
},
{
APIGroups: []string{"apps"},
Resources: []string{"deployments"},
Verbs: []string{"get", "list", "watch", "create", "patch", "delete"},
},
{
APIGroups: []string{"apps"},
Resources: []string{"replicasets"},
Verbs: []string{"get", "list", "patch", "delete"},
},
{
APIGroups: []string{"networking.k8s.io"},
Resources: []string{"ingresses"},
Verbs: []string{"get", "list", "watch", "create", "delete"},
},
{
APIGroups: []string{"metrics.k8s.io"},
Resources: []string{"pods", "nodes"},
Verbs: []string{"get", "list", "watch"},
},
{
APIGroups: []string{""},
Resources: []string{"namespaces"},
Verbs: []string{"get", "list"},
},
{
APIGroups: []string{""},
Resources: []string{"events"},
Verbs: []string{"watch", "list"},
},
}
openshiftPolicies := []rbacv1.PolicyRule{
{
APIGroups: []string{"route.openshift.io"},
Resources: []string{"routes"},
Verbs: []string{"get", "list", "create", "delete"},
},
{
APIGroups: []string{"project.openshift.io"},
Resources: []string{"projects"},
Verbs: []string{"get"},
},
}
if infrastructure.IsOpenShift() {
return append(k8sPolicies, openshiftPolicies...)
}
return k8sPolicies
}
func (s *CheServerReconciler) getDefaultUserClusterRoles(ctx *chetypes.DeployContext) []string {
return []string{
fmt.Sprintf(userCommonPermissionsTemplateName, ctx.CheCluster.Namespace),
fmt.Sprintf(userDevWorkspacePermissionsTemplateName, ctx.CheCluster.Namespace),
}
}