Skip to content

Commit

Permalink
Fix ClusterRole & ClusterRoleBinding hotreload (#92)
Browse files Browse the repository at this point in the history
* Watch every minute if a CustomGroup rule change and apply them

* Fix: OwnerRefInvalidNamespace when refering to a Service Account that doesn't exists in the same namespace

* fix conflict

* bump up helm chart patch version

* Fix: OwnerRefInvalidNamespace when refering to a Service Account that doesn't exists in the same namespace

* bump up helm chart patch version

* Fix ldap Service Account when email has `_`

* Fix ldap Service Account when email has `_`

Signed-off-by: Victor  Godoy Hernández <[email protected]>

* bump up helm chart

Signed-off-by: Victor  Godoy Hernández <[email protected]>

* Add Test for emails with special characters

Signed-off-by: Victor  Godoy Hernández <[email protected]>

* Fix ClusterRole & ClusterRoleBinding hot reload

Signed-off-by: Victor  Godoy Hernández <[email protected]>

* Delete garbage ClusterRoles

Signed-off-by: Victor  Godoy Hernández <[email protected]>

* Fix missing error handle

Signed-off-by: Victor  Godoy Hernández <[email protected]>

* Fix missing error handle

Signed-off-by: Victor  Godoy Hernández <[email protected]>

* Delete ClusterRoleBinding or RoleBinding that reference ClusterRole

Signed-off-by: Victor  Godoy Hernández <[email protected]>

* Fix Errors unhandled

Signed-off-by: Victor  Godoy Hernández <[email protected]>
  • Loading branch information
vigohe authored Nov 25, 2021
1 parent bb2185d commit b18369b
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 57 deletions.
4 changes: 2 additions & 2 deletions charts/jwt-to-rbac/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
apiVersion: v2
appVersion: 0.6.4
appVersion: 0.6.5
description: A Helm chart for Kubernetes
name: jwt-to-rbac
version: 0.6.4
version: 0.6.5
home: https://github.com/banzaicloud/jwt-to-rbac
maintainers:
- name: BanzaiCloud
Expand Down
162 changes: 107 additions & 55 deletions pkg/rbachandler/rbac_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,32 @@ func (rh *RBACHandler) listClusterroleBindings() ([]string, error) {
return cRoleBindList, nil
}

func (rh *RBACHandler) deleteLinkedCRoleBinding(crole string) error{
bindings := rh.rbacClientSet.ClusterRoleBindings()
labelSelector := fmt.Sprintf("crole=%s", crole)
listOptions := metav1.ListOptions{
LabelSelector: labelSelector,
}
err := bindings.DeleteCollection(&metav1.DeleteOptions{}, listOptions)
if err != nil {
return emperror.WrapWith(err, "unable to delete collection of clusterrolebindings", "ListOptions", metav1.ListOptions{})
}
return nil
}

func (rh *RBACHandler) deleteLinkedRoleBinding(crole string, namespace string) error{
bindings := rh.rbacClientSet.RoleBindings(namespace)
labelSelector := fmt.Sprintf("crole=%s", crole)
listOptions := metav1.ListOptions{
LabelSelector: labelSelector,
}
err := bindings.DeleteCollection(&metav1.DeleteOptions{}, listOptions)
if err != nil {
return emperror.WrapWith(err, "unable to delete collection of clusterrolebindings", "ListOptions", metav1.ListOptions{})
}
return nil
}

func (rh *RBACHandler) listClusterroles() ([]string, error) {
clusterRoles := rh.rbacClientSet.ClusterRoles()
labelSelect := fmt.Sprintf("%s=%s", defautlLabelKey, defaultLabel[defautlLabelKey])
Expand Down Expand Up @@ -245,9 +271,6 @@ func (rh *RBACHandler) createServiceAccount(sa *ServiceAccount) error {
}

func (rh *RBACHandler) createClusterRoleBinding(crb *clusterRoleBinding) error {
if err := rh.getAndCheckCRoleBinding(crb.name); err == nil {
return nil
}
var subjects []apirbacv1.Subject
for _, ns := range crb.nameSpace {
subject := apirbacv1.Subject{
Expand All @@ -258,6 +281,14 @@ func (rh *RBACHandler) createClusterRoleBinding(crb *clusterRoleBinding) error {
}
subjects = append(subjects, subject)
}

customLabels := map[string]string{
"crole": crb.roleName,
}
for key, value := range customLabels {
crb.labels[key] = value
}

bindObj := &apirbacv1.ClusterRoleBinding{
TypeMeta: metav1.TypeMeta{
Kind: "ClusterRoleBinding",
Expand Down Expand Up @@ -287,9 +318,6 @@ func (rh *RBACHandler) createClusterRoleBinding(crb *clusterRoleBinding) error {
}

func (rh *RBACHandler) createRoleBinding(rb *roleBinding) error {
if err := rh.getAndCheckRoleBinding(rb.name, rb.nameSpace); err == nil {
return nil
}
var subjects []apirbacv1.Subject
for _, ns := range rb.nameSpace {
subject := apirbacv1.Subject{
Expand All @@ -301,6 +329,13 @@ func (rh *RBACHandler) createRoleBinding(rb *roleBinding) error {
}
subjects = append(subjects, subject)

customLabels := map[string]string{
"crole": rb.roleName,
}
for key, value := range customLabels {
rb.labels[key] = value
}

bindObj := &apirbacv1.RoleBinding{
TypeMeta: metav1.TypeMeta{
Kind: "RoleBinding",
Expand Down Expand Up @@ -328,9 +363,6 @@ func (rh *RBACHandler) createRoleBinding(rb *roleBinding) error {
}

func (rh *RBACHandler) createClusterRole(cr *clusterRole) error {
if err := rh.getAndCheckCRole(cr.name); err == nil {
return nil
}
var rules []apirbacv1.PolicyRule
for _, rule := range cr.rules {
rule := apirbacv1.PolicyRule{
Expand Down Expand Up @@ -358,6 +390,20 @@ func (rh *RBACHandler) createClusterRole(cr *clusterRole) error {
return nil
}

func (rh *RBACHandler) getAndCheckCRole(CRName string) error {
cRole, err := rh.rbacClientSet.ClusterRoles().Get(CRName, metav1.GetOptions{})
if err == nil {
if label, ok := cRole.ObjectMeta.Labels[defautlLabelKey]; !ok || label != defaultLabel[defautlLabelKey] {
return emperror.WrapWith(errors.New("label mismatch in clusterrole"),
"there is a ClusterRole without required label",
defautlLabelKey, defaultLabel[defautlLabelKey],
"cluster_role", CRName)
}
return nil
}
return err
}

func generateRules(groupName string, config *Config) []rule {
var cRules []rule
for _, cGroup := range config.CustomGroups {
Expand Down Expand Up @@ -571,52 +617,6 @@ func (rh *RBACHandler) getAndCheckSA(saName string) (*apicorev1.ServiceAccount,
return saDetails, nil
}

func (rh *RBACHandler) getAndCheckCRole(CRName string) error {
cRole, err := rh.rbacClientSet.ClusterRoles().Get(CRName, metav1.GetOptions{})
if err == nil {
if label, ok := cRole.ObjectMeta.Labels[defautlLabelKey]; !ok || label != defaultLabel[defautlLabelKey] {
return emperror.WrapWith(errors.New("label mismatch in clusterrole"),
"there is a ClusterRole without required label",
defautlLabelKey, defaultLabel[defautlLabelKey],
"cluster_role", CRName)
}
return nil
}
return err
}

func (rh *RBACHandler) getAndCheckCRoleBinding(CRBindingName string) error {
cRoleBind, err := rh.rbacClientSet.ClusterRoleBindings().Get(CRBindingName, metav1.GetOptions{})
if err == nil {
if label, ok := cRoleBind.ObjectMeta.Labels[defautlLabelKey]; !ok || label != defaultLabel[defautlLabelKey] {
return emperror.WrapWith(errors.New("label mismatch in clusterrole"),
"there is a ClusterRoleBinding without required label",
defautlLabelKey, defaultLabel[defautlLabelKey],
"cluster_rolebinding", CRBindingName)
}
return nil
}
return err
}

func (rh *RBACHandler) getAndCheckRoleBinding(RBindingName string, NameSpaces []string) error {

for _, namespace := range NameSpaces {
roleBind, err := rh.rbacClientSet.RoleBindings(namespace).Get(RBindingName, metav1.GetOptions{})
if err == nil {
if label, ok := roleBind.ObjectMeta.Labels[defautlLabelKey]; !ok || label != defaultLabel[defautlLabelKey] {
return emperror.WrapWith(errors.New("label mismatch in clusterrole"),
"there is a RoleBinding without required label",
defautlLabelKey, defaultLabel[defautlLabelKey],
"rolebinding", RBindingName)
}
continue
}
return err
}
return nil
}

func (rh *RBACHandler) getSAReference(saName string) ([]metav1.OwnerReference, error) {
saDetails, err := rh.getAndCheckSA(saName)
if err != nil {
Expand All @@ -643,6 +643,20 @@ func (rh *RBACHandler) removeServiceAccount(saName string, logger logur.Logger)
return nil
}

func (rh *RBACHandler) removeClusterRole(clusterRole string, logger logur.Logger) error {
if err := rh.getAndCheckCRole(clusterRole); err != nil {
return err
}
deleteOptions := &metav1.DeleteOptions{}
bg := metav1.DeletePropagationBackground
deleteOptions.PropagationPolicy = &bg
err := rh.rbacClientSet.ClusterRoles().Delete(clusterRole, deleteOptions)
if err != nil {
return emperror.WrapWith(err, "unable to delete ClusterRole", "cluster_role", clusterRole)
}
return nil
}

// DeleteRBAC deletes RBAC resources
func DeleteRBAC(saName string, config *Config, logger logur.Logger) error {
rbacHandler, err := NewRBACHandler(config.KubeConfig, logger)
Expand All @@ -656,6 +670,19 @@ func DeleteRBAC(saName string, config *Config, logger logur.Logger) error {
return nil
}

// DeleteClusterRole deletes ClusterRole resources
func DeleteClusterRole(clusterRole string, config *Config, logger logur.Logger) error {
rbacHandler, err := NewRBACHandler(config.KubeConfig, logger)
if err != nil {
return err
}
if err := rbacHandler.removeClusterRole(clusterRole, logger); err != nil {
logger.Error(err.Error(), nil)
return err
}
return nil
}

// GetK8sToken getting serviceaccount secrets data
func GetK8sToken(saName string, config *Config, logger logur.Logger) ([]*SACredential, error) {
rbacHandler, err := NewRBACHandler(config.KubeConfig, logger)
Expand Down Expand Up @@ -808,3 +835,28 @@ func tokenRandString(n int) string {
}
return "-token-" + string(b)
}

func (rh *RBACHandler) listCustomGroups(config *Config) ([]string) {
var customGroups []string

for _, group := range config.CustomGroups {
customGroups = append(customGroups, group.GroupName)
}

return customGroups
}

func (rh *RBACHandler) listNamespaces() ([]string, error) {
var rnamespace []string
namespacelist, err :=rh.coreClientSet.Namespaces().List(metav1.ListOptions{})

if err != nil {
return nil, emperror.WrapWith(err, "List namespaces failed", "listNamespaces")
}

for _, namespace := range namespacelist.Items{
rnamespace = append(rnamespace, namespace.Name)
}

return rnamespace, nil
}
68 changes: 68 additions & 0 deletions pkg/rbachandler/rbac_watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package rbachandler

import (
"fmt"
"strings"
"time"

"github.com/goph/emperror"
Expand Down Expand Up @@ -63,6 +64,11 @@ func (rh *RBACHandler) evaluateClusterRoles(config *Config, logger logur.Logger,
return err
}

err = checkClusterRole(config, logger)
if err != nil {
return err
}

rbacResources, err := generateClusterRoleRBACResources(config, logger)
if err != nil {
return err
Expand Down Expand Up @@ -116,3 +122,65 @@ func (rh *RBACHandler) checkTTL(secretName string) error {
}
return nil
}

func checkClusterRole(config *Config, logger logur.Logger) error {
var existingCustomGroups []string
rbacHandler, err := NewRBACHandler(config.KubeConfig, logger)
if err != nil {
return err
}

existingClusterRoles, err := rbacHandler.listClusterroles()
if err != nil {
return err
}

for _, clusterRole := range existingClusterRoles {
existingCustomGroups = append(existingCustomGroups, strings.ReplaceAll(clusterRole, "-from-jwt", ""))
}

customGroups := rbacHandler.listCustomGroups(config)

err = removeCustomGroupsDifference(existingCustomGroups, customGroups, config, logger)
if err != nil {
return err
}

return nil
}

func removeCustomGroupsDifference(existingClusterRoles []string, configCustomGroups []string, config *Config, logger logur.Logger) error {
rbacHandler, err := NewRBACHandler(config.KubeConfig, logger)
if err != nil {
return err
}

mb := make(map[string]struct{}, len(configCustomGroups))
for _, x := range configCustomGroups {
mb[x] = struct{}{}
}

for _, x := range existingClusterRoles {
if _, found := mb[x]; !found {
err = rbacHandler.deleteLinkedCRoleBinding(x + "-from-jwt")
if err != nil {
return err
}
namespaces, err := rbacHandler.listNamespaces()
if err != nil {
return err
}
for _, namespace := range namespaces {
err = rbacHandler.deleteLinkedRoleBinding(x + "-from-jwt", namespace)
if err != nil {
return err
}
}
err = DeleteClusterRole(x + "-from-jwt", config, logger)
if err != nil {
return err
}
}
}
return nil
}

0 comments on commit b18369b

Please sign in to comment.