From 1f0be9480caea1de2c87a7664dd009bbc8bcea9d Mon Sep 17 00:00:00 2001 From: testA113 Date: Fri, 4 Oct 2024 16:44:56 +1300 Subject: [PATCH] allow deleting cluster roles --- api/http/handler/kubernetes/cluster_roles.go | 35 ++++++++++++++++++ api/http/handler/kubernetes/handler.go | 1 + api/http/models/kubernetes/cluster_roles.go | 30 ++++++++++++---- api/kubernetes/cli/cluster_role.go | 37 ++++++++++++++++++-- 4 files changed, 94 insertions(+), 9 deletions(-) diff --git a/api/http/handler/kubernetes/cluster_roles.go b/api/http/handler/kubernetes/cluster_roles.go index 0cf226bd4..35541e2be 100644 --- a/api/http/handler/kubernetes/cluster_roles.go +++ b/api/http/handler/kubernetes/cluster_roles.go @@ -3,7 +3,9 @@ package kubernetes import ( "net/http" + models "github.com/portainer/portainer/api/http/models/kubernetes" httperror "github.com/portainer/portainer/pkg/libhttp/error" + "github.com/portainer/portainer/pkg/libhttp/request" "github.com/portainer/portainer/pkg/libhttp/response" "github.com/rs/zerolog/log" ) @@ -43,3 +45,36 @@ func (handler *Handler) getAllKubernetesClusterRoles(w http.ResponseWriter, r *h return response.JSON(w, clusterroles) } + +// @id deleteClusterRoles +// @summary Delete the provided cluster roles +// @description Delete the provided cluster roles for the given Kubernetes environment +// @description **Access policy**: administrator +// @tags rbac_enabled +// @security ApiKeyAuth +// @security jwt +// @produce text/plain +// @param id path int true "Environment(Endpoint) identifier" +// @param payload body models.K8sClusterRoleDeleteRequests true "Cluster roles to delete" +// @success 200 "Success" +// @failure 500 "Server error" +// @router /kubernetes/{id}/cluster_roles/delete [POST] +func (handler *Handler) deleteClusterRoles(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { + var payload models.K8sClusterRoleDeleteRequests + err := request.DecodeAndValidateJSONPayload(r, &payload) + if err != nil { + return httperror.BadRequest("Invalid request payload", err) + } + + cli, handlerErr := handler.getProxyKubeClient(r) + if handlerErr != nil { + return handlerErr + } + + err = cli.DeleteClusterRoles(payload) + if err != nil { + return httperror.InternalServerError("Failed to delete cluster roles", err) + } + + return nil +} diff --git a/api/http/handler/kubernetes/handler.go b/api/http/handler/kubernetes/handler.go index eb22a439c..bcb56d8a9 100644 --- a/api/http/handler/kubernetes/handler.go +++ b/api/http/handler/kubernetes/handler.go @@ -56,6 +56,7 @@ func NewHandler(bouncer security.BouncerService, authorizationService *authoriza endpointRouter.Handle("/configmaps", httperror.LoggerHandler(h.GetAllKubernetesConfigMaps)).Methods(http.MethodGet) endpointRouter.Handle("/configmaps/count", httperror.LoggerHandler(h.getAllKubernetesConfigMapsCount)).Methods(http.MethodGet) endpointRouter.Handle("/cluster_roles", httperror.LoggerHandler(h.getAllKubernetesClusterRoles)).Methods(http.MethodGet) + endpointRouter.Handle("/cluster_roles/delete", httperror.LoggerHandler(h.deleteClusterRoles)).Methods(http.MethodPost) endpointRouter.Handle("/cluster_role_bindings", httperror.LoggerHandler(h.getAllKubernetesClusterRoleBindings)).Methods(http.MethodGet) endpointRouter.Handle("/configmaps", httperror.LoggerHandler(h.GetAllKubernetesConfigMaps)).Methods(http.MethodGet) endpointRouter.Handle("/configmaps/count", httperror.LoggerHandler(h.getAllKubernetesConfigMapsCount)).Methods(http.MethodGet) diff --git a/api/http/models/kubernetes/cluster_roles.go b/api/http/models/kubernetes/cluster_roles.go index ef17890c8..a42b19474 100644 --- a/api/http/models/kubernetes/cluster_roles.go +++ b/api/http/models/kubernetes/cluster_roles.go @@ -1,10 +1,28 @@ package kubernetes -import "time" +import ( + "errors" + "net/http" + "time" -type K8sClusterRole struct { - Name string `json:"name"` - CreationDate time.Time `json:"creationDate"` - Uid string `json:"uid"` - IsSystem bool `json:"isSystem"` + "k8s.io/apimachinery/pkg/types" +) + +type ( + K8sClusterRole struct { + Name string `json:"name"` + UID types.UID `json:"uid"` + CreationDate time.Time `json:"creationDate"` + IsSystem bool `json:"isSystem"` + } + + K8sClusterRoleDeleteRequests []string +) + +func (r K8sClusterRoleDeleteRequests) Validate(request *http.Request) error { + if len(r) == 0 { + return errors.New("missing deletion request list in payload") + } + + return nil } diff --git a/api/kubernetes/cli/cluster_role.go b/api/kubernetes/cli/cluster_role.go index 8c46d9b3d..4aa3f9c75 100644 --- a/api/kubernetes/cli/cluster_role.go +++ b/api/kubernetes/cli/cluster_role.go @@ -6,8 +6,11 @@ import ( "strings" models "github.com/portainer/portainer/api/http/models/kubernetes" + "github.com/portainer/portainer/api/internal/errorlist" + "github.com/rs/zerolog/log" rbacv1 "k8s.io/api/rbac/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + meta "k8s.io/apimachinery/pkg/apis/meta/v1" ) // GetClusterRoles gets all the clusterRoles for at the cluster level in a k8s endpoint. @@ -22,7 +25,7 @@ func (kcl *KubeClient) GetClusterRoles() ([]models.K8sClusterRole, error) { // fetchClusterRoles returns a list of all Roles in the specified namespace. func (kcl *KubeClient) fetchClusterRoles() ([]models.K8sClusterRole, error) { - clusterRoles, err := kcl.cli.RbacV1().ClusterRoles().List(context.TODO(), metav1.ListOptions{}) + clusterRoles, err := kcl.cli.RbacV1().ClusterRoles().List(context.TODO(), meta.ListOptions{}) if err != nil { return nil, err } @@ -40,11 +43,39 @@ func parseClusterRole(clusterRole rbacv1.ClusterRole) models.K8sClusterRole { return models.K8sClusterRole{ Name: clusterRole.Name, CreationDate: clusterRole.CreationTimestamp.Time, - Uid: string(clusterRole.UID), + UID: clusterRole.UID, IsSystem: isSystemClusterRole(&clusterRole), } } +func (kcl *KubeClient) DeleteClusterRoles(req models.K8sClusterRoleDeleteRequests) error { + var errors []error + for _, name := range req { + client := kcl.cli.RbacV1().ClusterRoles() + + clusterRole, err := client.Get(context.Background(), name, meta.GetOptions{}) + if err != nil { + if k8serrors.IsNotFound(err) { + continue + } + // this is a more serious error to do with the client so we return right away + return err + } + + if isSystemClusterRole(clusterRole) { + log.Warn().Str("role_name", name).Msg("ignoring delete of 'system' cluster role, not allowed") + } + + err = client.Delete(context.Background(), name, meta.DeleteOptions{}) + if err != nil { + log.Err(err).Str("role_name", name).Msg("unable to delete the cluster role") + errors = append(errors, err) + } + } + + return errorlist.Combine(errors) +} + func isSystemClusterRole(role *rbacv1.ClusterRole) bool { if role.Namespace == "kube-system" || role.Namespace == "kube-public" || role.Namespace == "kube-node-lease" || role.Namespace == "portainer" {