297 lines
9.1 KiB
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),
|
|
}
|
|
}
|