mirror of https://github.com/portainer/portainer
roles and role bindings delete and isSystem
parent
9ddebf91f2
commit
61bea1b06d
|
@ -18,7 +18,7 @@ import (
|
||||||
// @security ApiKeyAuth || jwt
|
// @security ApiKeyAuth || jwt
|
||||||
// @produce json
|
// @produce json
|
||||||
// @param id path int true "Environment identifier"
|
// @param id path int true "Environment identifier"
|
||||||
// @success 200 {array} models.K8sClusterRoleBinding "Success"
|
// @success 200 {array} kubernetes.K8sClusterRoleBinding "Success"
|
||||||
// @failure 400 "Invalid request payload, such as missing required fields or fields not meeting validation criteria."
|
// @failure 400 "Invalid request payload, such as missing required fields or fields not meeting validation criteria."
|
||||||
// @failure 401 "Unauthorized access - the user is not authenticated or does not have the necessary permissions. Ensure that you have provided a valid API key or JWT token, and that you have the required permissions."
|
// @failure 401 "Unauthorized access - the user is not authenticated or does not have the necessary permissions. Ensure that you have provided a valid API key or JWT token, and that you have the required permissions."
|
||||||
// @failure 403 "Permission denied - the user is authenticated but does not have the necessary permissions to access the requested resource or perform the specified operation. Check your user roles and permissions."
|
// @failure 403 "Permission denied - the user is authenticated but does not have the necessary permissions to access the requested resource or perform the specified operation. Check your user roles and permissions."
|
||||||
|
@ -55,7 +55,7 @@ func (handler *Handler) getAllKubernetesClusterRoleBindings(w http.ResponseWrite
|
||||||
// @security jwt
|
// @security jwt
|
||||||
// @produce text/plain
|
// @produce text/plain
|
||||||
// @param id path int true "Environment(Endpoint) identifier"
|
// @param id path int true "Environment(Endpoint) identifier"
|
||||||
// @param payload body models.K8sClusterRoleBindingDeleteRequests true "Cluster role bindings to delete"
|
// @param payload body kubernetes.K8sClusterRoleBindingDeleteRequests true "Cluster role bindings to delete"
|
||||||
// @success 200 "Success"
|
// @success 200 "Success"
|
||||||
// @failure 500 "Server error"
|
// @failure 500 "Server error"
|
||||||
// @router /kubernetes/{id}/cluster_role_bindings/delete [POST]
|
// @router /kubernetes/{id}/cluster_role_bindings/delete [POST]
|
||||||
|
|
|
@ -55,7 +55,7 @@ func (handler *Handler) getAllKubernetesClusterRoles(w http.ResponseWriter, r *h
|
||||||
// @security jwt
|
// @security jwt
|
||||||
// @produce text/plain
|
// @produce text/plain
|
||||||
// @param id path int true "Environment(Endpoint) identifier"
|
// @param id path int true "Environment(Endpoint) identifier"
|
||||||
// @param payload body models.K8sClusterRoleDeleteRequests true "Cluster roles to delete"
|
// @param payload body kubernetes.K8sClusterRoleDeleteRequests true "Cluster roles to delete"
|
||||||
// @success 200 "Success"
|
// @success 200 "Success"
|
||||||
// @failure 500 "Server error"
|
// @failure 500 "Server error"
|
||||||
// @router /kubernetes/{id}/cluster_roles/delete [POST]
|
// @router /kubernetes/{id}/cluster_roles/delete [POST]
|
||||||
|
|
|
@ -74,16 +74,12 @@ func NewHandler(bouncer security.BouncerService, authorizationService *authoriza
|
||||||
endpointRouter.Handle("/ingresses/delete", httperror.LoggerHandler(h.deleteKubernetesIngresses)).Methods(http.MethodPost)
|
endpointRouter.Handle("/ingresses/delete", httperror.LoggerHandler(h.deleteKubernetesIngresses)).Methods(http.MethodPost)
|
||||||
endpointRouter.Handle("/ingresses", httperror.LoggerHandler(h.GetAllKubernetesClusterIngresses)).Methods(http.MethodGet)
|
endpointRouter.Handle("/ingresses", httperror.LoggerHandler(h.GetAllKubernetesClusterIngresses)).Methods(http.MethodGet)
|
||||||
endpointRouter.Handle("/ingresses/count", httperror.LoggerHandler(h.getAllKubernetesClusterIngressesCount)).Methods(http.MethodGet)
|
endpointRouter.Handle("/ingresses/count", httperror.LoggerHandler(h.getAllKubernetesClusterIngressesCount)).Methods(http.MethodGet)
|
||||||
endpointRouter.Handle("/service_accounts", httperror.LoggerHandler(h.getAllKubernetesServiceAccounts)).Methods(http.MethodGet)
|
|
||||||
endpointRouter.Handle("/services", httperror.LoggerHandler(h.GetAllKubernetesServices)).Methods(http.MethodGet)
|
endpointRouter.Handle("/services", httperror.LoggerHandler(h.GetAllKubernetesServices)).Methods(http.MethodGet)
|
||||||
endpointRouter.Handle("/services/count", httperror.LoggerHandler(h.getAllKubernetesServicesCount)).Methods(http.MethodGet)
|
endpointRouter.Handle("/services/count", httperror.LoggerHandler(h.getAllKubernetesServicesCount)).Methods(http.MethodGet)
|
||||||
endpointRouter.Handle("/secrets", httperror.LoggerHandler(h.GetAllKubernetesSecrets)).Methods(http.MethodGet)
|
endpointRouter.Handle("/secrets", httperror.LoggerHandler(h.GetAllKubernetesSecrets)).Methods(http.MethodGet)
|
||||||
endpointRouter.Handle("/secrets/count", httperror.LoggerHandler(h.getAllKubernetesSecretsCount)).Methods(http.MethodGet)
|
endpointRouter.Handle("/secrets/count", httperror.LoggerHandler(h.getAllKubernetesSecretsCount)).Methods(http.MethodGet)
|
||||||
endpointRouter.Handle("/services/delete", httperror.LoggerHandler(h.deleteKubernetesServices)).Methods(http.MethodPost)
|
endpointRouter.Handle("/services/delete", httperror.LoggerHandler(h.deleteKubernetesServices)).Methods(http.MethodPost)
|
||||||
endpointRouter.Handle("/service_accounts/delete", httperror.LoggerHandler(h.deleteKubernetesServiceAccounts)).Methods(http.MethodPost)
|
|
||||||
endpointRouter.Handle("/rbac_enabled", httperror.LoggerHandler(h.getKubernetesRBACStatus)).Methods(http.MethodGet)
|
endpointRouter.Handle("/rbac_enabled", httperror.LoggerHandler(h.getKubernetesRBACStatus)).Methods(http.MethodGet)
|
||||||
endpointRouter.Handle("/roles", httperror.LoggerHandler(h.getAllKubernetesRoles)).Methods(http.MethodGet)
|
|
||||||
endpointRouter.Handle("/role_bindings", httperror.LoggerHandler(h.getAllKubernetesRoleBindings)).Methods(http.MethodGet)
|
|
||||||
endpointRouter.Handle("/namespaces", httperror.LoggerHandler(h.createKubernetesNamespace)).Methods(http.MethodPost)
|
endpointRouter.Handle("/namespaces", httperror.LoggerHandler(h.createKubernetesNamespace)).Methods(http.MethodPost)
|
||||||
endpointRouter.Handle("/namespaces", httperror.LoggerHandler(h.updateKubernetesNamespace)).Methods(http.MethodPut)
|
endpointRouter.Handle("/namespaces", httperror.LoggerHandler(h.updateKubernetesNamespace)).Methods(http.MethodPut)
|
||||||
endpointRouter.Handle("/namespaces", httperror.LoggerHandler(h.deleteKubernetesNamespace)).Methods(http.MethodDelete)
|
endpointRouter.Handle("/namespaces", httperror.LoggerHandler(h.deleteKubernetesNamespace)).Methods(http.MethodDelete)
|
||||||
|
@ -92,6 +88,16 @@ func NewHandler(bouncer security.BouncerService, authorizationService *authoriza
|
||||||
endpointRouter.Handle("/namespaces/{namespace}", httperror.LoggerHandler(h.getKubernetesNamespace)).Methods(http.MethodGet)
|
endpointRouter.Handle("/namespaces/{namespace}", httperror.LoggerHandler(h.getKubernetesNamespace)).Methods(http.MethodGet)
|
||||||
endpointRouter.Handle("/volumes", httperror.LoggerHandler(h.GetAllKubernetesVolumes)).Methods(http.MethodGet)
|
endpointRouter.Handle("/volumes", httperror.LoggerHandler(h.GetAllKubernetesVolumes)).Methods(http.MethodGet)
|
||||||
endpointRouter.Handle("/volumes/count", httperror.LoggerHandler(h.getAllKubernetesVolumesCount)).Methods(http.MethodGet)
|
endpointRouter.Handle("/volumes/count", httperror.LoggerHandler(h.getAllKubernetesVolumesCount)).Methods(http.MethodGet)
|
||||||
|
endpointRouter.Handle("/service_accounts", httperror.LoggerHandler(h.getAllKubernetesServiceAccounts)).Methods(http.MethodGet)
|
||||||
|
endpointRouter.Handle("/service_accounts/delete", httperror.LoggerHandler(h.deleteKubernetesServiceAccounts)).Methods(http.MethodPost)
|
||||||
|
endpointRouter.Handle("/roles", httperror.LoggerHandler(h.getAllKubernetesRoles)).Methods(http.MethodGet)
|
||||||
|
endpointRouter.Handle("/roles/delete", httperror.LoggerHandler(h.deleteRoles)).Methods(http.MethodPost)
|
||||||
|
endpointRouter.Handle("/role_bindings", httperror.LoggerHandler(h.getAllKubernetesRoleBindings)).Methods(http.MethodGet)
|
||||||
|
endpointRouter.Handle("/role_bindings/delete", httperror.LoggerHandler(h.deleteRoleBindings)).Methods(http.MethodPost)
|
||||||
|
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("/cluster_role_bindings/delete", httperror.LoggerHandler(h.deleteClusterRoleBindings)).Methods(http.MethodPost)
|
||||||
|
|
||||||
// namespaces
|
// namespaces
|
||||||
// in the future this piece of code might be in another package (or a few different packages - namespaces/namespace?)
|
// in the future this piece of code might be in another package (or a few different packages - namespaces/namespace?)
|
||||||
|
|
|
@ -3,7 +3,9 @@ package kubernetes
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
models "github.com/portainer/portainer/api/http/models/kubernetes"
|
||||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||||
|
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
@ -38,3 +40,35 @@ func (handler *Handler) getAllKubernetesRoleBindings(w http.ResponseWriter, r *h
|
||||||
|
|
||||||
return response.JSON(w, rolebindings)
|
return response.JSON(w, rolebindings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @id DeleteRoleBindings
|
||||||
|
// @summary Delete the provided role bindings
|
||||||
|
// @description Delete the provided role bindings 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.K8sRoleDeleteRequests true "Role bindings to delete"
|
||||||
|
// @success 200 "Success"
|
||||||
|
// @failure 500 "Server error"
|
||||||
|
// @router /kubernetes/{id}/role_bindings/delete [POST]
|
||||||
|
func (h *Handler) deleteRoleBindings(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||||
|
var payload models.K8sRoleBindingDeleteRequests
|
||||||
|
|
||||||
|
if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
|
||||||
|
return httperror.BadRequest("Invalid request payload", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cli, handlerErr := h.getProxyKubeClient(r)
|
||||||
|
if handlerErr != nil {
|
||||||
|
return handlerErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cli.DeleteRoleBindings(payload); err != nil {
|
||||||
|
return httperror.InternalServerError("Failed to delete role bindings", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,9 @@ package kubernetes
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
models "github.com/portainer/portainer/api/http/models/kubernetes"
|
||||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||||
|
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
@ -38,3 +40,36 @@ func (handler *Handler) getAllKubernetesRoles(w http.ResponseWriter, r *http.Req
|
||||||
|
|
||||||
return response.JSON(w, roles)
|
return response.JSON(w, roles)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @id DeleteRoles
|
||||||
|
// @summary Delete the provided roles
|
||||||
|
// @description Delete the provided 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 kubernetes.K8sRoleDeleteRequests true "Roles to delete "
|
||||||
|
// @success 200 "Success"
|
||||||
|
// @failure 500 "Server error"
|
||||||
|
// @router /kubernetes/{id}/roles/delete [POST]
|
||||||
|
func (h *Handler) deleteRoles(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||||
|
var payload models.K8sRoleDeleteRequests
|
||||||
|
err := request.DecodeAndValidateJSONPayload(r, &payload)
|
||||||
|
if err != nil {
|
||||||
|
return httperror.BadRequest("Invalid request payload", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cli, handlerErr := h.getProxyKubeClient(r)
|
||||||
|
if handlerErr != nil {
|
||||||
|
return handlerErr
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cli.DeleteRoles(payload)
|
||||||
|
if err != nil {
|
||||||
|
return httperror.InternalServerError("Failed to delete roles", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ func (handler *Handler) getAllKubernetesServiceAccounts(w http.ResponseWriter, r
|
||||||
// @security jwt
|
// @security jwt
|
||||||
// @produce text/plain
|
// @produce text/plain
|
||||||
// @param id path int true "Environment(Endpoint) identifier"
|
// @param id path int true "Environment(Endpoint) identifier"
|
||||||
// @param payload body models.K8sServiceAccountDeleteRequests true "Service accounts to delete "
|
// @param payload body kubernetes.K8sServiceAccountDeleteRequests true "Service accounts to delete "
|
||||||
// @success 200 "Success"
|
// @success 200 "Success"
|
||||||
// @failure 500 "Server error"
|
// @failure 500 "Server error"
|
||||||
// @router /kubernetes/{id}/service_accounts/delete [POST]
|
// @router /kubernetes/{id}/service_accounts/delete [POST]
|
||||||
|
|
|
@ -13,6 +13,7 @@ type (
|
||||||
K8sClusterRoleBinding struct {
|
K8sClusterRoleBinding struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
UID types.UID `json:"uid"`
|
UID types.UID `json:"uid"`
|
||||||
|
Namespace string `json:"namespace"`
|
||||||
RoleRef rbacv1.RoleRef `json:"roleRef"`
|
RoleRef rbacv1.RoleRef `json:"roleRef"`
|
||||||
Subjects []rbacv1.Subject `json:"subjects"`
|
Subjects []rbacv1.Subject `json:"subjects"`
|
||||||
CreationDate time.Time `json:"creationDate"`
|
CreationDate time.Time `json:"creationDate"`
|
||||||
|
|
|
@ -1,17 +1,38 @@
|
||||||
package kubernetes
|
package kubernetes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
K8sRoleBinding struct {
|
K8sRoleBinding struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
UID types.UID `json:"uid"`
|
||||||
Namespace string `json:"namespace"`
|
Namespace string `json:"namespace"`
|
||||||
RoleRef rbacv1.RoleRef `json:"roleRef"`
|
RoleRef rbacv1.RoleRef `json:"roleRef"`
|
||||||
Subjects []rbacv1.Subject `json:"subjects"`
|
Subjects []rbacv1.Subject `json:"subjects"`
|
||||||
CreationDate time.Time `json:"creationDate"`
|
CreationDate time.Time `json:"creationDate"`
|
||||||
|
IsSystem bool `json:"isSystem"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// K8sRoleBindingDeleteRequests is a mapping of namespace names to a slice of role bindings.
|
||||||
|
K8sRoleBindingDeleteRequests map[string][]string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (r K8sRoleBindingDeleteRequests) Validate(request *http.Request) error {
|
||||||
|
if len(r) == 0 {
|
||||||
|
return errors.New("missing deletion request list in payload")
|
||||||
|
}
|
||||||
|
|
||||||
|
for ns := range r {
|
||||||
|
if len(ns) == 0 {
|
||||||
|
return errors.New("deletion given with empty namespace")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,36 @@
|
||||||
package kubernetes
|
package kubernetes
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
type K8sRole struct {
|
"k8s.io/apimachinery/pkg/types"
|
||||||
Name string `json:"name"`
|
)
|
||||||
Namespace string `json:"namespace"`
|
|
||||||
CreationDate time.Time `json:"creationDate"`
|
type (
|
||||||
|
K8sRole struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
UID types.UID `json:"uid"`
|
||||||
|
Namespace string `json:"namespace"`
|
||||||
|
CreationDate time.Time `json:"creationDate"`
|
||||||
|
// isSystem is true if prefixed with "system:" or exists in the kube-system namespace
|
||||||
|
// or is one of the portainer roles
|
||||||
|
IsSystem bool `json:"isSystem"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// K8sRoleDeleteRequests is a mapping of namespace names to a slice of roles.
|
||||||
|
K8sRoleDeleteRequests map[string][]string
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r K8sRoleDeleteRequests) Validate(request *http.Request) error {
|
||||||
|
if len(r) == 0 {
|
||||||
|
return errors.New("missing deletion request list in payload")
|
||||||
|
}
|
||||||
|
for ns := range r {
|
||||||
|
if len(ns) == 0 {
|
||||||
|
return errors.New("deletion given with empty namespace")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ func parseClusterRoleBinding(clusterRoleBinding rbacv1.ClusterRoleBinding) model
|
||||||
return models.K8sClusterRoleBinding{
|
return models.K8sClusterRoleBinding{
|
||||||
Name: clusterRoleBinding.Name,
|
Name: clusterRoleBinding.Name,
|
||||||
UID: clusterRoleBinding.UID,
|
UID: clusterRoleBinding.UID,
|
||||||
|
Namespace: clusterRoleBinding.Namespace,
|
||||||
RoleRef: clusterRoleBinding.RoleRef,
|
RoleRef: clusterRoleBinding.RoleRef,
|
||||||
Subjects: clusterRoleBinding.Subjects,
|
Subjects: clusterRoleBinding.Subjects,
|
||||||
CreationDate: clusterRoleBinding.CreationTimestamp.Time,
|
CreationDate: clusterRoleBinding.CreationTimestamp.Time,
|
||||||
|
|
|
@ -2,11 +2,15 @@ package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
models "github.com/portainer/portainer/api/http/models/kubernetes"
|
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"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetRoles gets all the roles for either at the cluster level or a given namespace in a k8s endpoint.
|
// GetRoles gets all the roles for either at the cluster level or a given namespace in a k8s endpoint.
|
||||||
|
@ -48,18 +52,20 @@ func (kcl *KubeClient) fetchRoles(namespace string) ([]models.K8sRole, error) {
|
||||||
|
|
||||||
results := make([]models.K8sRole, 0)
|
results := make([]models.K8sRole, 0)
|
||||||
for _, role := range roles.Items {
|
for _, role := range roles.Items {
|
||||||
results = append(results, parseRole(role))
|
results = append(results, kcl.parseRole(role))
|
||||||
}
|
}
|
||||||
|
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseRole converts a rbacv1.Role object to a models.K8sRole object.
|
// parseRole converts a rbacv1.Role object to a models.K8sRole object.
|
||||||
func parseRole(role rbacv1.Role) models.K8sRole {
|
func (kcl *KubeClient) parseRole(role rbacv1.Role) models.K8sRole {
|
||||||
return models.K8sRole{
|
return models.K8sRole{
|
||||||
Name: role.Name,
|
Name: role.Name,
|
||||||
|
UID: role.UID,
|
||||||
Namespace: role.Namespace,
|
Namespace: role.Namespace,
|
||||||
CreationDate: role.CreationTimestamp.Time,
|
CreationDate: role.CreationTimestamp.Time,
|
||||||
|
IsSystem: kcl.isSystemRole(&role),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,3 +120,42 @@ func getPortainerDefaultK8sRoleNames() []string {
|
||||||
string(portainerUserCRName),
|
string(portainerUserCRName),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (kcl *KubeClient) isSystemRole(role *rbacv1.Role) bool {
|
||||||
|
if strings.HasPrefix(role.Name, "system:") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return kcl.isSystemNamespace(role.Namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRoles processes a K8sServiceDeleteRequest by deleting each role
|
||||||
|
// in its given namespace.
|
||||||
|
func (kcl *KubeClient) DeleteRoles(reqs models.K8sRoleDeleteRequests) error {
|
||||||
|
var errors []error
|
||||||
|
for namespace := range reqs {
|
||||||
|
for _, name := range reqs[namespace] {
|
||||||
|
client := kcl.cli.RbacV1().Roles(namespace)
|
||||||
|
|
||||||
|
role, err := client.Get(context.Background(), name, v1.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 kcl.isSystemRole(role) {
|
||||||
|
log.Error().Str("role_name", name).Msg("ignoring delete of 'system' role, not allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := client.Delete(context.TODO(), name, metav1.DeleteOptions{}); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errorlist.Combine(errors)
|
||||||
|
}
|
||||||
|
|
|
@ -2,10 +2,16 @@ package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
models "github.com/portainer/portainer/api/http/models/kubernetes"
|
models "github.com/portainer/portainer/api/http/models/kubernetes"
|
||||||
|
"github.com/portainer/portainer/api/internal/errorlist"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
corev1 "k8s.io/api/rbac/v1"
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
|
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetRoleBindings gets all the roleBindings for either at the cluster level or a given namespace in a k8s endpoint.
|
// GetRoleBindings gets all the roleBindings for either at the cluster level or a given namespace in a k8s endpoint.
|
||||||
|
@ -47,19 +53,82 @@ func (kcl *KubeClient) fetchRoleBindings(namespace string) ([]models.K8sRoleBind
|
||||||
|
|
||||||
results := make([]models.K8sRoleBinding, 0)
|
results := make([]models.K8sRoleBinding, 0)
|
||||||
for _, roleBinding := range roleBindings.Items {
|
for _, roleBinding := range roleBindings.Items {
|
||||||
results = append(results, parseRoleBinding(roleBinding))
|
results = append(results, kcl.parseRoleBinding(roleBinding))
|
||||||
}
|
}
|
||||||
|
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseRoleBinding converts a rbacv1.RoleBinding object to a models.K8sRoleBinding object.
|
// parseRoleBinding converts a rbacv1.RoleBinding object to a models.K8sRoleBinding object.
|
||||||
func parseRoleBinding(roleBinding rbacv1.RoleBinding) models.K8sRoleBinding {
|
func (kcl *KubeClient) parseRoleBinding(roleBinding rbacv1.RoleBinding) models.K8sRoleBinding {
|
||||||
return models.K8sRoleBinding{
|
return models.K8sRoleBinding{
|
||||||
Name: roleBinding.Name,
|
Name: roleBinding.Name,
|
||||||
|
UID: roleBinding.UID,
|
||||||
Namespace: roleBinding.Namespace,
|
Namespace: roleBinding.Namespace,
|
||||||
RoleRef: roleBinding.RoleRef,
|
RoleRef: roleBinding.RoleRef,
|
||||||
Subjects: roleBinding.Subjects,
|
Subjects: roleBinding.Subjects,
|
||||||
CreationDate: roleBinding.CreationTimestamp.Time,
|
CreationDate: roleBinding.CreationTimestamp.Time,
|
||||||
|
IsSystem: kcl.isSystemRoleBinding(&roleBinding),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (kcl *KubeClient) isSystemRoleBinding(rb *rbacv1.RoleBinding) bool {
|
||||||
|
if strings.HasPrefix(rb.Name, "system:") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if rb.Labels != nil {
|
||||||
|
if rb.Labels["kubernetes.io/bootstrapping"] == "rbac-defaults" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rb.RoleRef.Name != "" {
|
||||||
|
role, err := kcl.getRole(rb.Namespace, rb.RoleRef.Name)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Linked to a role that is marked a system role
|
||||||
|
if kcl.isSystemRole(role) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kcl *KubeClient) getRole(namespace, name string) (*corev1.Role, error) {
|
||||||
|
client := kcl.cli.RbacV1().Roles(namespace)
|
||||||
|
return client.Get(context.Background(), name, metav1.GetOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRoleBindings processes a K8sServiceDeleteRequest by deleting each service
|
||||||
|
// in its given namespace.
|
||||||
|
func (kcl *KubeClient) DeleteRoleBindings(reqs models.K8sRoleBindingDeleteRequests) error {
|
||||||
|
var errors []error
|
||||||
|
for namespace := range reqs {
|
||||||
|
for _, name := range reqs[namespace] {
|
||||||
|
client := kcl.cli.RbacV1().RoleBindings(namespace)
|
||||||
|
|
||||||
|
roleBinding, err := client.Get(context.Background(), name, v1.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 kcl.isSystemRoleBinding(roleBinding) {
|
||||||
|
log.Error().Str("role_name", name).Msg("ignoring delete of 'system' role binding, not allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := client.Delete(context.Background(), name, v1.DeleteOptions{}); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errorlist.Combine(errors)
|
||||||
|
}
|
||||||
|
|
|
@ -1498,6 +1498,8 @@ type (
|
||||||
SetupUserServiceAccount(userID int, teamIDs []int, restrictDefaultNamespace bool) error
|
SetupUserServiceAccount(userID int, teamIDs []int, restrictDefaultNamespace bool) error
|
||||||
IsRBACEnabled() (bool, error)
|
IsRBACEnabled() (bool, error)
|
||||||
GetPortainerUserServiceAccount(tokendata *TokenData) (*corev1.ServiceAccount, error)
|
GetPortainerUserServiceAccount(tokendata *TokenData) (*corev1.ServiceAccount, error)
|
||||||
|
GetServiceAccounts(namespace string) ([]models.K8sServiceAccount, error)
|
||||||
|
DeleteServiceAccounts(reqs models.K8sServiceAccountDeleteRequests) error
|
||||||
GetServiceAccountBearerToken(userID int) (string, error)
|
GetServiceAccountBearerToken(userID int) (string, error)
|
||||||
CreateUserShellPod(ctx context.Context, serviceAccountName, shellPodImage string) (*KubernetesShellPod, error)
|
CreateUserShellPod(ctx context.Context, serviceAccountName, shellPodImage string) (*KubernetesShellPod, error)
|
||||||
StartExecProcess(token string, useAdminToken bool, namespace, podName, containerName string, command []string, stdin io.Reader, stdout io.Writer, errChan chan error)
|
StartExecProcess(token string, useAdminToken bool, namespace, podName, containerName string, command []string, stdin io.Reader, stdout io.Writer, errChan chan error)
|
||||||
|
@ -1531,6 +1533,16 @@ type (
|
||||||
CreateRegistrySecret(registry *Registry, namespace string) error
|
CreateRegistrySecret(registry *Registry, namespace string) error
|
||||||
IsRegistrySecret(namespace, secretName string) (bool, error)
|
IsRegistrySecret(namespace, secretName string) (bool, error)
|
||||||
ToggleSystemState(namespace string, isSystem bool) error
|
ToggleSystemState(namespace string, isSystem bool) error
|
||||||
|
|
||||||
|
GetClusterRoles() ([]models.K8sClusterRole, error)
|
||||||
|
DeleteClusterRoles(models.K8sClusterRoleDeleteRequests) error
|
||||||
|
GetClusterRoleBindings() ([]models.K8sClusterRoleBinding, error)
|
||||||
|
DeleteClusterRoleBindings(models.K8sClusterRoleBindingDeleteRequests) error
|
||||||
|
|
||||||
|
GetRoles(namespace string) ([]models.K8sRole, error)
|
||||||
|
DeleteRoles(models.K8sRoleDeleteRequests) error
|
||||||
|
GetRoleBindings(namespace string) ([]models.K8sRoleBinding, error)
|
||||||
|
DeleteRoleBindings(models.K8sRoleBindingDeleteRequests) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// KubernetesDeployer represents a service to deploy a manifest inside a Kubernetes environment(endpoint)
|
// KubernetesDeployer represents a service to deploy a manifest inside a Kubernetes environment(endpoint)
|
||||||
|
|
Loading…
Reference in New Issue