From 6f45fbdde79e81751205e867b353d70ec62127b9 Mon Sep 17 00:00:00 2001 From: Rokibul Hasan Date: Fri, 9 Aug 2024 20:00:47 +0600 Subject: [PATCH] Add Account controller (#19) Signed-off-by: Rokibul Hasan --- .../managedclusterrole_controller.go | 2 +- .../managedclusterrolebinding_controller.go | 7 +- pkg/cmds/manager/main.go | 6 +- pkg/common/constants.go | 2 + .../authentication/account_controller.go | 339 +++++++++++++++++ .../managedclusterrolebinding_controller.go | 340 ------------------ ...managedclustersetrolebinding_controller.go | 2 +- pkg/utils/util.go | 7 + 8 files changed, 357 insertions(+), 348 deletions(-) create mode 100644 pkg/manager/controller/authentication/account_controller.go delete mode 100644 pkg/manager/controller/authorization/managedclusterrolebinding_controller.go diff --git a/pkg/agent/controller/managedclusterrole_controller.go b/pkg/agent/controller/managedclusterrole_controller.go index 4f1ec8af..0fbf0082 100644 --- a/pkg/agent/controller/managedclusterrole_controller.go +++ b/pkg/agent/controller/managedclusterrole_controller.go @@ -53,7 +53,7 @@ func (r *ManagedClusterRoleReconciler) Reconcile(ctx context.Context, req ctrl.R managedClusterRole := &authorizationv1alpha1.ManagedClusterRole{} if err := r.HubClient.Get(ctx, req.NamespacedName, managedClusterRole); err != nil { - return reconcile.Result{}, err + return reconcile.Result{}, client.IgnoreNotFound(err) } // create clusterRole in spoke cluster diff --git a/pkg/agent/controller/managedclusterrolebinding_controller.go b/pkg/agent/controller/managedclusterrolebinding_controller.go index 76e86ef4..2568bdef 100644 --- a/pkg/agent/controller/managedclusterrolebinding_controller.go +++ b/pkg/agent/controller/managedclusterrolebinding_controller.go @@ -22,7 +22,6 @@ import ( authzv1alpah1 "github.com/kluster-manager/cluster-auth/apis/authorization/v1alpha1" "github.com/kluster-manager/cluster-auth/pkg/common" - "github.com/kluster-manager/cluster-auth/pkg/utils" rbac "k8s.io/api/rbac/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -62,9 +61,9 @@ func (r *ManagedClusterRoleBindingReconciler) Reconcile(ctx context.Context, req var managedCRB authzv1alpah1.ManagedClusterRoleBinding if err := r.HubClient.Get(ctx, req.NamespacedName, &managedCRB); err != nil { - return reconcile.Result{}, err + return reconcile.Result{}, client.IgnoreNotFound(err) } - _, hubOwnerID := utils.GetUserIDAndHubOwnerIDFromLabelValues(&managedCRB) + userName := managedCRB.Subjects[0].Name // Check if the managedCRB is marked for deletion @@ -91,7 +90,7 @@ func (r *ManagedClusterRoleBindingReconciler) Reconcile(ctx context.Context, req // impersonate clusterRole cr := rbac.ClusterRole{ ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("impersonate-%s-%s-%s", userName, hubOwnerID, rand.String(7)), + Name: fmt.Sprintf("ace.%s.impersonate.%s", userName, rand.String(10)), Labels: managedCRB.Labels, }, Rules: []rbac.PolicyRule{ diff --git a/pkg/cmds/manager/main.go b/pkg/cmds/manager/main.go index c0d7a489..fd5938cb 100644 --- a/pkg/cmds/manager/main.go +++ b/pkg/cmds/manager/main.go @@ -25,6 +25,7 @@ import ( authorizationv1alpha1 "github.com/kluster-manager/cluster-auth/apis/authorization/v1alpha1" "github.com/kluster-manager/cluster-auth/pkg/common" "github.com/kluster-manager/cluster-auth/pkg/manager" + "github.com/kluster-manager/cluster-auth/pkg/manager/controller/authentication" "github.com/kluster-manager/cluster-auth/pkg/manager/controller/authorization" permission "github.com/kluster-manager/cluster-auth/pkg/manager/rbac" @@ -145,13 +146,14 @@ func NewCmdManager() *cobra.Command { os.Exit(1) } - if err = (&authorization.ManagedClusterRoleBindingReconciler{ + if err = (&authentication.AccountReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "ManagedClusterRoleBinding") + setupLog.Error(err, "unable to create controller", "controller", "Account") os.Exit(1) } + if err = (&authorization.ManagedClusterSetRoleBindingReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), diff --git a/pkg/common/constants.go b/pkg/common/constants.go index 80b26206..e4cbd9da 100644 --- a/pkg/common/constants.go +++ b/pkg/common/constants.go @@ -28,4 +28,6 @@ const ( UserAuthLabel = "authentication.k8s.appscode.com/user" HubOwnerLabel = "authentication.k8s.appscode.com/hub-owner" HubClusterIdLabel = "cluster.k8s.appscode.com/cluster-id" + + ServiceAccountPrefix = "system:serviceaccount:" ) diff --git a/pkg/manager/controller/authentication/account_controller.go b/pkg/manager/controller/authentication/account_controller.go new file mode 100644 index 00000000..574133b7 --- /dev/null +++ b/pkg/manager/controller/authentication/account_controller.go @@ -0,0 +1,339 @@ +/* +Copyright AppsCode Inc. and Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package authentication + +import ( + "context" + "fmt" + "strings" + + authenticationv1alpha1 "github.com/kluster-manager/cluster-auth/apis/authentication/v1alpha1" + "github.com/kluster-manager/cluster-auth/pkg/common" + + core "k8s.io/api/core/v1" + rbac "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + kmapi "kmodules.xyz/client-go/api/v1" + cu "kmodules.xyz/client-go/client" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +type AccountReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +func (r *AccountReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + logger.Info("Start reconciling...") + + acc := &authenticationv1alpha1.Account{} + err := r.Client.Get(ctx, req.NamespacedName, acc) + if err != nil { + return reconcile.Result{}, client.IgnoreNotFound(err) + } + + // Update status to InProgress only if not already set + if acc.Status.Phase == "" { + acc.Status.Phase = authenticationv1alpha1.AccountPhaseInProgress + setAccountType(acc) + if err := r.Client.Status().Update(ctx, acc); err != nil { + return reconcile.Result{}, err + } + } + if err = r.createServiceAccount(ctx, acc); err != nil { + return reconcile.Result{}, r.setStatusFailed(ctx, acc, err) + } + + if err = r.createGatewayClusterRoleBindingForUser(ctx, acc); err != nil { + return reconcile.Result{}, r.setStatusFailed(ctx, acc, err) + } + + if err = r.createClusterRoleAndClusterRoleBindingToImpersonate(ctx, acc); err != nil { + return reconcile.Result{}, r.setStatusFailed(ctx, acc, err) + } + + // Set the status to success after successful reconciliation + if acc.Status.Phase != authenticationv1alpha1.AccountPhaseCurrent { + if err := r.setStatusSuccess(ctx, acc, "Reconciliation completed successfully."); err != nil { + return reconcile.Result{}, err + } + } + + return reconcile.Result{}, nil +} + +func (r *AccountReconciler) createServiceAccount(ctx context.Context, acc *authenticationv1alpha1.Account) error { + // create ns to store service-accounts and tokens of accounts + ns := core.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.AddonAgentInstallNamespace, + }, + } + + _, err := cu.CreateOrPatch(ctx, r.Client, &ns, func(obj client.Object, createOp bool) client.Object { + in := obj.(*core.Namespace) + in.ObjectMeta = ns.ObjectMeta + return in + }) + if err != nil { + return err + } + + // create service-account for user + sa := core.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: acc.Name, + Namespace: common.AddonAgentInstallNamespace, + OwnerReferences: []metav1.OwnerReference{ + *metav1.NewControllerRef(acc, authenticationv1alpha1.GroupVersion.WithKind("Account")), + }, + }, + } + + _, err = cu.CreateOrPatch(ctx, r.Client, &sa, func(obj client.Object, createOp bool) client.Object { + in := obj.(*core.ServiceAccount) + in.ObjectMeta = sa.ObjectMeta + return in + }) + if err != nil { + return err + } + + _, err = cu.GetServiceAccountTokenSecret(r.Client, types.NamespacedName{Name: sa.Name, Namespace: sa.Namespace}) + if err != nil { + return err + } + + return nil +} + +func (r *AccountReconciler) createGatewayClusterRoleBindingForUser(ctx context.Context, acc *authenticationv1alpha1.Account) error { + sub := []rbac.Subject{ + { + APIGroup: "", + Kind: "User", + Name: acc.Name, + }, + } + + if strings.Contains(acc.Spec.Username, common.ServiceAccountPrefix) { + sub = []rbac.Subject{ + { + APIGroup: "", + Kind: "ServiceAccount", + Name: acc.Name, + Namespace: common.AddonAgentInstallNamespace, + }, + } + } + + crb := rbac.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("ace.%s.proxy", acc.Spec.UID), + OwnerReferences: []metav1.OwnerReference{ + *metav1.NewControllerRef(acc, authenticationv1alpha1.GroupVersion.WithKind("Account")), + }, + }, + Subjects: sub, + RoleRef: rbac.RoleRef{ + APIGroup: rbac.GroupName, + Kind: "ClusterRole", + Name: common.GatewayProxyClusterRole, + }, + } + + if strings.Contains(acc.Spec.Username, common.ServiceAccountPrefix) { + crb.Name = fmt.Sprintf("ace.%s.proxy", acc.Spec.Username) + } + + _, err := cu.CreateOrPatch(ctx, r.Client, &crb, func(obj client.Object, createOp bool) client.Object { + in := obj.(*rbac.ClusterRoleBinding) + in.Subjects = crb.Subjects + in.RoleRef = crb.RoleRef + return in + }) + if err != nil { + return err + } + return nil +} + +func (r *AccountReconciler) createClusterRoleAndClusterRoleBindingToImpersonate(ctx context.Context, acc *authenticationv1alpha1.Account) error { + // impersonate clusterRole + cr := rbac.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("ace.%s.impersonate", acc.Spec.UID), + OwnerReferences: []metav1.OwnerReference{ + *metav1.NewControllerRef(acc, authenticationv1alpha1.GroupVersion.WithKind("Account")), + }, + }, + Rules: []rbac.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"users"}, + Verbs: []string{"impersonate"}, + ResourceNames: []string{acc.Name}, + }, + }, + } + + if strings.Contains(acc.Spec.Username, common.ServiceAccountPrefix) { + cr = rbac.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("ace.%s.impersonate", acc.Spec.Username), + OwnerReferences: []metav1.OwnerReference{ + *metav1.NewControllerRef(acc, authenticationv1alpha1.GroupVersion.WithKind("Account")), + }, + }, + Rules: []rbac.PolicyRule{ + { + APIGroups: []string{""}, + Resources: []string{"serviceaccounts"}, + Verbs: []string{"impersonate"}, + ResourceNames: []string{acc.Name}, + }, + }, + } + } + + _, err := cu.CreateOrPatch(ctx, r.Client, &cr, func(obj client.Object, createOp bool) client.Object { + in := obj.(*rbac.ClusterRole) + in.ObjectMeta = cr.ObjectMeta + in.Rules = cr.Rules + return in + }) + if err != nil { + return err + } + + sub := []rbac.Subject{ + { + APIGroup: "", + Kind: "ServiceAccount", + Name: acc.Name, + Namespace: common.AddonAgentInstallNamespace, + }, + } + + crb := rbac.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: cr.Name, // creating cluster-rolebinding name with the same name of cluster-role + OwnerReferences: []metav1.OwnerReference{ + *metav1.NewControllerRef(acc, authenticationv1alpha1.GroupVersion.WithKind("Account")), + }, + }, + Subjects: sub, + RoleRef: rbac.RoleRef{ + APIGroup: rbac.GroupName, + Kind: "ClusterRole", + Name: cr.Name, + }, + } + + _, err = cu.CreateOrPatch(context.Background(), r.Client, &crb, func(obj client.Object, createOp bool) client.Object { + in := obj.(*rbac.ClusterRoleBinding) + in.ObjectMeta = crb.ObjectMeta + in.Subjects = crb.Subjects + in.RoleRef = crb.RoleRef + return in + }) + if err != nil { + return err + } + + return nil +} + +// updateConditions adds or updates a condition in the conditions array. +func (r *AccountReconciler) updateConditions(conditions []kmapi.Condition, conditionType kmapi.ConditionType, message string) []kmapi.Condition { + now := metav1.Now() + for i, condition := range conditions { + if condition.Type == conditionType { + conditions[i].Status = metav1.ConditionStatus(core.ConditionTrue) + conditions[i].LastTransitionTime = now + conditions[i].Message = message + return conditions + } + } + + return append(conditions, kmapi.Condition{ + Type: conditionType, + Status: metav1.ConditionStatus(core.ConditionTrue), + LastTransitionTime: now, + Reason: string(conditionType), + Message: message, + }) +} + +func (r *AccountReconciler) setStatusFailed(ctx context.Context, acc *authenticationv1alpha1.Account, err error) error { + // Re-fetch the latest version of the Account object + if err := r.Client.Get(ctx, client.ObjectKeyFromObject(acc), acc); err != nil && !errors.IsNotFound(err) { + return fmt.Errorf("failed to get latest account object: %w", err) + } + + acc.Status.Phase = authenticationv1alpha1.AccountPhaseFailed + setAccountType(acc) + + acc.Status.Conditions = r.updateConditions(acc.Status.Conditions, "ReconciliationFailed", err.Error()) + if updateErr := r.Client.Status().Update(ctx, acc); updateErr != nil { + return fmt.Errorf("failed to update status to Failed: %w", updateErr) + } + return err +} + +func (r *AccountReconciler) setStatusSuccess(ctx context.Context, acc *authenticationv1alpha1.Account, message string) error { + // Re-fetch the latest version of the Account object + if err := r.Client.Get(ctx, client.ObjectKeyFromObject(acc), acc); err != nil && !errors.IsNotFound(err) { + return fmt.Errorf("failed to get latest account object: %w", err) + } + + acc.Status.Phase = authenticationv1alpha1.AccountPhaseCurrent + acc.Status.Conditions = r.updateConditions(acc.Status.Conditions, "ReconciliationSuccessful", message) + setAccountType(acc) + acc.Status.ServiceAccountRef = &core.LocalObjectReference{ + Name: acc.Name, + } + + // Update or add a successful condition + acc.Status.Conditions = r.updateConditions(acc.Status.Conditions, "ReconciliationSuccessful", message) + if updateErr := r.Client.Status().Update(ctx, acc); updateErr != nil { + return fmt.Errorf("failed to update status to Current: %w", updateErr) + } + return nil +} + +func setAccountType(acc *authenticationv1alpha1.Account) { + if strings.Contains(acc.Spec.Username, common.ServiceAccountPrefix) { + acc.Status.Type = authenticationv1alpha1.AccountTypeServiceAccount + } else { + acc.Status.Type = authenticationv1alpha1.AccountTypeUser + } +} + +// SetupWithManager sets up the controller with the Manager. +func (r *AccountReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&authenticationv1alpha1.Account{}). + Complete(r) +} diff --git a/pkg/manager/controller/authorization/managedclusterrolebinding_controller.go b/pkg/manager/controller/authorization/managedclusterrolebinding_controller.go deleted file mode 100644 index b6333f12..00000000 --- a/pkg/manager/controller/authorization/managedclusterrolebinding_controller.go +++ /dev/null @@ -1,340 +0,0 @@ -/* -Copyright AppsCode Inc. and Contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package authorization - -import ( - "context" - "errors" - "fmt" - - authorizationv1alpha1 "github.com/kluster-manager/cluster-auth/apis/authorization/v1alpha1" - "github.com/kluster-manager/cluster-auth/pkg/common" - "github.com/kluster-manager/cluster-auth/pkg/utils" - - core "k8s.io/api/core/v1" - rbac "k8s.io/api/rbac/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/rand" - cu "kmodules.xyz/client-go/client" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/reconcile" -) - -// ManagedClusterRoleBindingReconciler reconciles a ManagedClusterRoleBinding object -type ManagedClusterRoleBindingReconciler struct { - client.Client - Scheme *runtime.Scheme -} - -func (r *ManagedClusterRoleBindingReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - logger := log.FromContext(ctx) - logger.Info("Start reconciling") - - managedCRB := &authorizationv1alpha1.ManagedClusterRoleBinding{} - err := r.Client.Get(ctx, req.NamespacedName, managedCRB) - if err != nil { - return reconcile.Result{}, err - } - - // Check if the managedCRB is marked for deletion - if managedCRB.GetDeletionTimestamp() != nil { - if controllerutil.ContainsFinalizer(managedCRB, common.HubAuthorizationFinalizer) { - // Perform cleanup logic, e.g., delete related resources - if err := r.deleteAssociatedResources(managedCRB); err != nil { - return reconcile.Result{}, err - } - // Remove the finalizer - controllerutil.RemoveFinalizer(managedCRB, common.HubAuthorizationFinalizer) - if err := r.Client.Update(context.TODO(), managedCRB); err != nil { - return reconcile.Result{}, err - } - } - return reconcile.Result{}, nil - } - - // Add finalizer if not present - if err := r.addFinalizerIfNeeded(managedCRB); err != nil { - return reconcile.Result{}, err - } - - // create a service account for user - if err = r.createServiceAccountForUser(ctx, managedCRB); err != nil { - return reconcile.Result{}, err - } - - // give user the gateway permission - if err = r.createClusterRoleBindingForUser(ctx, managedCRB); err != nil { - return reconcile.Result{}, err - } - - if err = r.createClusterRoleAndClusterRoleBindingToImpersonate(ctx, managedCRB); err != nil { - return reconcile.Result{}, err - } - - return reconcile.Result{}, nil -} - -func (r *ManagedClusterRoleBindingReconciler) createServiceAccountForUser(ctx context.Context, managedCRB *authorizationv1alpha1.ManagedClusterRoleBinding) error { - // create ns to store service-accounts and tokens of users - ns := &core.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: common.AddonAgentInstallNamespace, - }, - } - - _, err := cu.CreateOrPatch(context.Background(), r.Client, ns, func(obj client.Object, createOp bool) client.Object { - in := obj.(*core.Namespace) - in.ObjectMeta = ns.ObjectMeta - return in - }) - if err != nil { - return err - } - - // create service-account for user - var saList core.ServiceAccountList - _ = r.Client.List(ctx, &saList, client.MatchingLabelsSelector{ - Selector: labels.SelectorFromSet(managedCRB.Labels), - }) - - sa := core.ServiceAccount{} - if len(saList.Items) == 0 { - sa.Name = "ace-sa-" + rand.String(7) - sa.Namespace = common.AddonAgentInstallNamespace - sa.Labels = managedCRB.Labels - } else { - sa = saList.Items[0] - } - - _, err = cu.CreateOrPatch(context.Background(), r.Client, &sa, func(obj client.Object, createOp bool) client.Object { - in := obj.(*core.ServiceAccount) - in.ObjectMeta = sa.ObjectMeta - return in - }) - if err != nil { - return err - } - - _, err = cu.GetServiceAccountTokenSecret(r.Client, types.NamespacedName{Name: sa.Name, Namespace: sa.Namespace}) - if err != nil { - return err - } - - return nil -} - -func (r *ManagedClusterRoleBindingReconciler) createClusterRoleBindingForUser(ctx context.Context, managedCRB *authorizationv1alpha1.ManagedClusterRoleBinding) error { - userID, hubOwnerID := utils.GetUserIDAndHubOwnerIDFromLabelValues(managedCRB) - - // name: userID-hubOwnerID-gatewaybinding - crb := rbac.ClusterRoleBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s-%s-gatewaybinding", userID, hubOwnerID), - Labels: map[string]string{ - common.UserAuthLabel: userID, // TODO: remove this cluster-rolebinding when user object is deleted from hub - }, - }, - Subjects: []rbac.Subject{ - { - APIGroup: "", - Kind: "User", - Name: managedCRB.Subjects[0].Name, - }, - }, - RoleRef: rbac.RoleRef{ - APIGroup: rbac.GroupName, - Kind: "ClusterRole", - Name: common.GatewayProxyClusterRole, - }, - } - - crbList := &rbac.ClusterRoleBindingList{} - _ = r.Client.List(ctx, crbList, client.MatchingLabelsSelector{ - Selector: labels.SelectorFromSet(managedCRB.Labels), - }) - - if len(crbList.Items) > 0 { - crb = crbList.Items[0] - } - _, err := cu.CreateOrPatch(ctx, r.Client, &crb, func(obj client.Object, createOp bool) client.Object { - in := obj.(*rbac.ClusterRoleBinding) - in.Subjects = crb.Subjects - in.RoleRef = crb.RoleRef - return in - }) - if err != nil { - return err - } - return nil -} - -func (r *ManagedClusterRoleBindingReconciler) createClusterRoleAndClusterRoleBindingToImpersonate(ctx context.Context, managedCRB *authorizationv1alpha1.ManagedClusterRoleBinding) error { - userID, hubOwnerID := utils.GetUserIDAndHubOwnerIDFromLabelValues(managedCRB) - userName := managedCRB.Subjects[0].Name - // name: userID-hubOwnerID-randomString - // impersonate clusterRole - cr := rbac.ClusterRole{ - ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s-%s-%s", userID, hubOwnerID, rand.String(7)), - Labels: managedCRB.Labels, - }, - Rules: []rbac.PolicyRule{ - { - APIGroups: []string{""}, - Resources: []string{"users"}, - Verbs: []string{"impersonate"}, - ResourceNames: []string{userName}, - }, - }, - } - - crList := &rbac.ClusterRoleList{} - _ = r.Client.List(ctx, crList, client.MatchingLabelsSelector{ - Selector: labels.SelectorFromSet(managedCRB.Labels), - }) - - if len(crList.Items) > 0 { - cr = crList.Items[0] - } - _, err := cu.CreateOrPatch(ctx, r.Client, &cr, func(obj client.Object, createOp bool) client.Object { - in := obj.(*rbac.ClusterRole) - in.ObjectMeta = cr.ObjectMeta - in.Rules = cr.Rules - return in - }) - if err != nil { - return err - } - - // now give the service-account permission to impersonate user - var saList core.ServiceAccountList - _ = r.Client.List(ctx, &saList, client.MatchingLabelsSelector{ - Selector: labels.SelectorFromSet(managedCRB.Labels), - }) - - if len(saList.Items) == 0 { - return errors.New("service account not found") - } - sub := []rbac.Subject{ - { - APIGroup: "", - Kind: "ServiceAccount", - Name: saList.Items[0].Name, - Namespace: common.AddonAgentInstallNamespace, - }, - } - - crb := rbac.ClusterRoleBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: cr.Name, // creating cluster-rolebinding name with the same name of cluster-role - Labels: managedCRB.Labels, - }, - Subjects: sub, - RoleRef: rbac.RoleRef{ - APIGroup: rbac.GroupName, - Kind: "ClusterRole", - Name: cr.Name, - }, - } - - crbList := &rbac.ClusterRoleBindingList{} - _ = r.Client.List(ctx, crbList, client.MatchingLabelsSelector{ - Selector: labels.SelectorFromSet(managedCRB.Labels), - }) - - if len(crbList.Items) > 0 { - crb = crbList.Items[0] - } - - _, err = cu.CreateOrPatch(context.Background(), r.Client, &crb, func(obj client.Object, createOp bool) client.Object { - in := obj.(*rbac.ClusterRoleBinding) - in.ObjectMeta = crb.ObjectMeta - in.Subjects = crb.Subjects - in.RoleRef = crb.RoleRef - return in - }) - if err != nil { - return err - } - - return nil -} - -// AddFinalizerIfNeeded adds a finalizer to the CRD instance if it doesn't already have one -func (r *ManagedClusterRoleBindingReconciler) addFinalizerIfNeeded(managedCRB *authorizationv1alpha1.ManagedClusterRoleBinding) error { - if !controllerutil.ContainsFinalizer(managedCRB, common.HubAuthorizationFinalizer) { - controllerutil.AddFinalizer(managedCRB, common.HubAuthorizationFinalizer) - if err := r.Client.Update(context.TODO(), managedCRB); err != nil { - return err - } - } - return nil -} - -func (r *ManagedClusterRoleBindingReconciler) deleteAssociatedResources(managedCRB *authorizationv1alpha1.ManagedClusterRoleBinding) error { - saList := core.ServiceAccountList{} - err := r.Client.List(context.TODO(), &saList, client.MatchingLabelsSelector{ - Selector: labels.SelectorFromSet(managedCRB.Labels), - }) - if err == nil { - for _, sa := range saList.Items { - if err := r.Client.Delete(context.TODO(), &sa); err != nil { - return err - } - } - } - - crList := rbac.ClusterRoleList{} - err = r.Client.List(context.TODO(), &crList, client.MatchingLabelsSelector{ - Selector: labels.SelectorFromSet(managedCRB.Labels), - }) - if err == nil { - for _, cr := range crList.Items { - if err := r.Client.Delete(context.TODO(), &cr); err != nil { - return err - } - } - } - - crbList := rbac.ClusterRoleBindingList{} - err = r.Client.List(context.TODO(), &crbList, client.MatchingLabelsSelector{ - Selector: labels.SelectorFromSet(managedCRB.Labels), - }) - if err == nil { - for _, crb := range crbList.Items { - if err := r.Client.Delete(context.TODO(), &crb); err != nil { - return err - } - } - } - - return nil -} - -// SetupWithManager sets up the controller with the Manager. -func (r *ManagedClusterRoleBindingReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&authorizationv1alpha1.ManagedClusterRoleBinding{}).Watches(&authorizationv1alpha1.ManagedClusterRoleBinding{}, &handler.EnqueueRequestForObject{}). - Complete(r) -} diff --git a/pkg/manager/controller/authorization/managedclustersetrolebinding_controller.go b/pkg/manager/controller/authorization/managedclustersetrolebinding_controller.go index b1c6af2e..4e88e9a6 100644 --- a/pkg/manager/controller/authorization/managedclustersetrolebinding_controller.go +++ b/pkg/manager/controller/authorization/managedclustersetrolebinding_controller.go @@ -59,7 +59,7 @@ func (r *ManagedClusterSetRoleBindingReconciler) Reconcile(ctx context.Context, managedCSRB := &authorizationv1alpha1.ManagedClusterSetRoleBinding{} err := r.Client.Get(ctx, req.NamespacedName, managedCSRB) if err != nil { - return reconcile.Result{}, err + return reconcile.Result{}, client.IgnoreNotFound(err) } // Check if the managedCRB is marked for deletion diff --git a/pkg/utils/util.go b/pkg/utils/util.go index 8c38c12e..2d347204 100644 --- a/pkg/utils/util.go +++ b/pkg/utils/util.go @@ -17,6 +17,8 @@ limitations under the License. package utils import ( + "strings" + authorizationv1alpha1 "github.com/kluster-manager/cluster-auth/apis/authorization/v1alpha1" ) @@ -33,3 +35,8 @@ func GetUserIDAndHubOwnerIDFromLabelValues(object *authorizationv1alpha1.Managed return userID, hubOwnerID } + +func ReplaceColonWithHyphen(input string) string { + parts := strings.Split(input, ":") + return strings.Join(parts, "-") +}