From 37247f608138e63726ddc6a5a6d222818e33288e Mon Sep 17 00:00:00 2001 From: lonelyCZ <531187475@qq.com> Date: Wed, 17 Aug 2022 14:39:20 +0800 Subject: [PATCH] Set open cluster-info to distribute root CA certificates Signed-off-by: lonelyCZ <531187475@qq.com> --- go.mod | 2 +- .../bootstraptoken/clusterinfo/clusterinfo.go | 99 +++++++++++++++++++ pkg/karmadactl/cmdinit/karmada/deploy.go | 18 ++++ pkg/karmadactl/cmdinit/utils/configmap.go | 28 ++++++ pkg/karmadactl/cmdinit/utils/rbac.go | 32 ++++++ 5 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 pkg/karmadactl/cmdinit/bootstraptoken/clusterinfo/clusterinfo.go create mode 100644 pkg/karmadactl/cmdinit/utils/configmap.go diff --git a/go.mod b/go.mod index 5633c3e34..b00a051f5 100644 --- a/go.mod +++ b/go.mod @@ -28,6 +28,7 @@ require ( k8s.io/apiserver v0.24.2 k8s.io/cli-runtime v0.24.2 k8s.io/client-go v0.24.2 + k8s.io/cluster-bootstrap v0.24.2 k8s.io/code-generator v0.24.2 k8s.io/component-base v0.24.2 k8s.io/component-helpers v0.24.2 @@ -161,7 +162,6 @@ require ( gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect - k8s.io/cluster-bootstrap v0.24.2 // indirect k8s.io/gengo v0.0.0-20211129171323-c02415ce4185 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30 // indirect sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect diff --git a/pkg/karmadactl/cmdinit/bootstraptoken/clusterinfo/clusterinfo.go b/pkg/karmadactl/cmdinit/bootstraptoken/clusterinfo/clusterinfo.go new file mode 100644 index 000000000..f4689ed8c --- /dev/null +++ b/pkg/karmadactl/cmdinit/bootstraptoken/clusterinfo/clusterinfo.go @@ -0,0 +1,99 @@ +package clusterinfo + +import ( + "fmt" + + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apiserver/pkg/authentication/user" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + bootstrapapi "k8s.io/cluster-bootstrap/token/api" + "k8s.io/klog/v2" + + "github.com/karmada-io/karmada/pkg/karmadactl/cmdinit/utils" +) + +const ( + // BootstrapSignerClusterRoleName sets the name for the ClusterRole that allows access to ConfigMaps in the kube-public ns + BootstrapSignerClusterRoleName = "karmada:bootstrap-signer-clusterinfo" +) + +// CreateBootstrapConfigMapIfNotExists creates the kube-public ConfigMap if it doesn't exist already +func CreateBootstrapConfigMapIfNotExists(clientSet *kubernetes.Clientset, file string) error { + klog.V(1).Infoln("[bootstrap-token] loading karmada admin kubeconfig") + adminConfig, err := clientcmd.LoadFromFile(file) + if err != nil { + return fmt.Errorf("failed to load admin kubeconfig, %w", err) + } + if err = clientcmdapi.FlattenConfig(adminConfig); err != nil { + return err + } + + adminCluster := adminConfig.Contexts[adminConfig.CurrentContext].Cluster + // Copy the cluster from admin.conf to the bootstrap kubeconfig, contains the CA cert and the server URL + klog.V(1).Infoln("[bootstrap-token] copying the cluster from admin.conf to the bootstrap kubeconfig") + bootstrapConfig := &clientcmdapi.Config{ + Clusters: map[string]*clientcmdapi.Cluster{ + "": adminConfig.Clusters[adminCluster], + }, + } + bootstrapBytes, err := clientcmd.Write(*bootstrapConfig) + if err != nil { + return err + } + + // Create or update the ConfigMap in the kube-public namespace + klog.V(1).Infoln("[bootstrap-token] creating/updating ConfigMap in kube-public namespace") + return utils.CreateOrUpdateConfigMap(clientSet, &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: bootstrapapi.ConfigMapClusterInfo, + Namespace: metav1.NamespacePublic, + }, + Data: map[string]string{ + bootstrapapi.KubeConfigKey: string(bootstrapBytes), + }, + }) +} + +// CreateClusterInfoRBACRules creates the RBAC rules for exposing the cluster-info ConfigMap in the kube-public namespace to unauthenticated users +func CreateClusterInfoRBACRules(clientSet *kubernetes.Clientset) error { + klog.V(1).Infoln("creating the RBAC rules for exposing the cluster-info ConfigMap in the kube-public namespace") + err := utils.CreateOrUpdateRole(clientSet, &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: BootstrapSignerClusterRoleName, + Namespace: metav1.NamespacePublic, + }, + Rules: []rbacv1.PolicyRule{ + { + Verbs: []string{"get"}, + APIGroups: []string{""}, + Resources: []string{"configmaps"}, + ResourceNames: []string{bootstrapapi.ConfigMapClusterInfo}, + }, + }, + }) + if err != nil { + return err + } + + return utils.CreateOrUpdateRoleBinding(clientSet, &rbacv1.RoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: BootstrapSignerClusterRoleName, + Namespace: metav1.NamespacePublic, + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: rbacv1.GroupName, + Kind: "Role", + Name: BootstrapSignerClusterRoleName, + }, + Subjects: []rbacv1.Subject{ + { + Kind: rbacv1.UserKind, + Name: user.Anonymous, + }, + }, + }) +} diff --git a/pkg/karmadactl/cmdinit/karmada/deploy.go b/pkg/karmadactl/cmdinit/karmada/deploy.go index d515f5a48..0fdc0469f 100644 --- a/pkg/karmadactl/cmdinit/karmada/deploy.go +++ b/pkg/karmadactl/cmdinit/karmada/deploy.go @@ -22,6 +22,7 @@ import ( apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" "sigs.k8s.io/yaml" + "github.com/karmada-io/karmada/pkg/karmadactl/cmdinit/bootstraptoken/clusterinfo" "github.com/karmada-io/karmada/pkg/karmadactl/cmdinit/options" "github.com/karmada-io/karmada/pkg/karmadactl/cmdinit/utils" ) @@ -98,11 +99,28 @@ func InitKarmadaResources(dir, caBase64, systemNamespace string) error { klog.Exitln(err) } + if err = createExtralResources(clientSet, dir); err != nil { + klog.Exitln(err) + } + + return nil +} + +func createExtralResources(clientSet *kubernetes.Clientset, dir string) error { // grant proxy permission to "system:admin". if err := grantProxyPermissionToAdmin(clientSet); err != nil { return err } + // Create the cluster-info ConfigMap with the associated RBAC rules + if err := clusterinfo.CreateBootstrapConfigMapIfNotExists(clientSet, filepath.Join(dir, options.KarmadaKubeConfigName)); err != nil { + return fmt.Errorf("error creating bootstrap ConfigMap: %v", err) + } + + if err := clusterinfo.CreateClusterInfoRBACRules(clientSet); err != nil { + return fmt.Errorf("error creating clusterinfo RBAC rules: %v", err) + } + return nil } diff --git a/pkg/karmadactl/cmdinit/utils/configmap.go b/pkg/karmadactl/cmdinit/utils/configmap.go new file mode 100644 index 000000000..bbe9c079e --- /dev/null +++ b/pkg/karmadactl/cmdinit/utils/configmap.go @@ -0,0 +1,28 @@ +package utils + +import ( + "context" + "fmt" + + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/klog/v2" +) + +// CreateOrUpdateConfigMap creates a ConfigMap if the target resource doesn't exist. If the resource exists already, this function will update the resource instead. +func CreateOrUpdateConfigMap(clientSet *kubernetes.Clientset, cm *corev1.ConfigMap) error { + if _, err := clientSet.CoreV1().ConfigMaps(cm.ObjectMeta.Namespace).Create(context.TODO(), cm, metav1.CreateOptions{}); err != nil { + if !apierrors.IsAlreadyExists(err) { + return fmt.Errorf("unable to create ConfigMap: %v", err) + } + + if _, err := clientSet.CoreV1().ConfigMaps(cm.ObjectMeta.Namespace).Update(context.TODO(), cm, metav1.UpdateOptions{}); err != nil { + return fmt.Errorf("unable to update ConfigMap: %v", err) + } + } + klog.Infof("ConfigMap %s/%s has been created or updated.", cm.ObjectMeta.Namespace, cm.ObjectMeta.Name) + + return nil +} diff --git a/pkg/karmadactl/cmdinit/utils/rbac.go b/pkg/karmadactl/cmdinit/utils/rbac.go index 142c730ec..8edbf6ff2 100644 --- a/pkg/karmadactl/cmdinit/utils/rbac.go +++ b/pkg/karmadactl/cmdinit/utils/rbac.go @@ -87,3 +87,35 @@ func CreateIfNotExistClusterRoleBinding(clientSet *kubernetes.Clientset, binding return nil } + +// CreateOrUpdateRole creates a Role if the target resource doesn't exist. If the resource exists already, this function will update the resource instead. +func CreateOrUpdateRole(clientSet *kubernetes.Clientset, role *rbacv1.Role) error { + if _, err := clientSet.RbacV1().Roles(role.ObjectMeta.Namespace).Create(context.TODO(), role, metav1.CreateOptions{}); err != nil { + if !apierrors.IsAlreadyExists(err) { + return fmt.Errorf("unable to create RBAC role: %v", err) + } + + if _, err := clientSet.RbacV1().Roles(role.ObjectMeta.Namespace).Update(context.TODO(), role, metav1.UpdateOptions{}); err != nil { + return fmt.Errorf("unable to update RBAC role: %v", err) + } + } + klog.Infof("Role %s%s has been created or updated.", role.ObjectMeta.Namespace, role.ObjectMeta.Name) + + return nil +} + +// CreateOrUpdateRoleBinding creates a RoleBinding if the target resource doesn't exist. If the resource exists already, this function will update the resource instead. +func CreateOrUpdateRoleBinding(clientSet *kubernetes.Clientset, roleBinding *rbacv1.RoleBinding) error { + if _, err := clientSet.RbacV1().RoleBindings(roleBinding.ObjectMeta.Namespace).Create(context.TODO(), roleBinding, metav1.CreateOptions{}); err != nil { + if !apierrors.IsAlreadyExists(err) { + return fmt.Errorf("unable to create RBAC rolebinding: %v", err) + } + + if _, err := clientSet.RbacV1().RoleBindings(roleBinding.ObjectMeta.Namespace).Update(context.TODO(), roleBinding, metav1.UpdateOptions{}); err != nil { + return fmt.Errorf("unable to update RBAC rolebinding: %v", err) + } + } + klog.Infof("RoleBinding %s/%s has been created or updated.", roleBinding.ObjectMeta.Namespace, roleBinding.ObjectMeta.Name) + + return nil +}