From 38d581939ab35bd76f8e9160b7195664356431dc Mon Sep 17 00:00:00 2001 From: Fabian Kramm Date: Fri, 27 Sep 2024 14:19:07 +0200 Subject: [PATCH] refactor: use regular access keys for all vClusters --- cmd/vclusterctl/cmd/platform/access_key.go | 47 +------ pkg/cli/config/config.go | 1 - pkg/cli/connect_platform.go | 2 +- pkg/platform/clihelper/clihelper.go | 4 +- pkg/platform/helper.go | 151 +-------------------- pkg/platform/kubeconfig/kubeconfig.go | 115 ++-------------- 6 files changed, 26 insertions(+), 294 deletions(-) diff --git a/cmd/vclusterctl/cmd/platform/access_key.go b/cmd/vclusterctl/cmd/platform/access_key.go index 18d2b5f892..59f88b8482 100644 --- a/cmd/vclusterctl/cmd/platform/access_key.go +++ b/cmd/vclusterctl/cmd/platform/access_key.go @@ -25,8 +25,11 @@ var ( type AccessKeyCmd struct { *flags.GlobalFlags - Project string - VirtualCluster string + // deprecated: all of these flags are deprecated + Project string + // deprecated: all of these flags are deprecated + VirtualCluster string + // deprecated: all of these flags are deprecated DirectClusterEndpoint bool log log.Logger @@ -73,14 +76,7 @@ func (cmd *AccessKeyCmd) Run(ctx context.Context) error { return err } - tokenFunc := getToken - - if cmd.Project != "" && cmd.VirtualCluster != "" { - cmd.log.Debug("project and virtual cluster set, attempting fetch virtual cluster certificate data") - tokenFunc = getCertificate - } - - return tokenFunc(cmd, platformClient) + return getToken(cmd, platformClient) } func getToken(_ *AccessKeyCmd, platformClient platform.Client) error { @@ -118,34 +114,3 @@ func printToken(token string) error { _, err = os.Stdout.Write(bytes) return err } - -func getCertificate(cmd *AccessKeyCmd, platformClient platform.Client) error { - certificateData, keyData, err := platform.VirtualClusterAccessPointCertificate(platformClient, cmd.Project, cmd.VirtualCluster, false) - if err != nil { - return err - } - - return printCertificate(certificateData, keyData) -} - -func printCertificate(certificateData, keyData string) error { - // Print certificate-based exec credential to stdout - response := &v1beta1.ExecCredential{ - TypeMeta: metav1.TypeMeta{ - Kind: "ExecCredential", - APIVersion: v1beta1.SchemeGroupVersion.String(), - }, - Status: &v1beta1.ExecCredentialStatus{ - ClientCertificateData: certificateData, - ClientKeyData: keyData, - }, - } - - bytes, err := json.Marshal(response) - if err != nil { - return fmt.Errorf("json marshal: %w", err) - } - - _, err = os.Stdout.Write(bytes) - return err -} diff --git a/pkg/cli/config/config.go b/pkg/cli/config/config.go index bb29fed96f..9593015bf8 100644 --- a/pkg/cli/config/config.go +++ b/pkg/cli/config/config.go @@ -34,7 +34,6 @@ func New() *CLI { Kind: "Config", APIVersion: "storage.loft.sh/v1", }, - VirtualClusterAccessPointCertificates: make(map[string]VirtualClusterCertificatesEntry), }, } } diff --git a/pkg/cli/connect_platform.go b/pkg/cli/connect_platform.go index f971474495..36812b1100 100644 --- a/pkg/cli/connect_platform.go +++ b/pkg/cli/connect_platform.go @@ -112,7 +112,7 @@ func (cmd *connectPlatform) getVClusterKubeConfig(ctx context.Context, platformC } // make sure access key is set - if contextOptions.Token == "" && len(contextOptions.ClientCertificateData) == 0 && len(contextOptions.ClientKeyData) == 0 { + if contextOptions.Token == "" { contextOptions.Token = platformClient.Config().Platform.AccessKey } diff --git a/pkg/platform/clihelper/clihelper.go b/pkg/platform/clihelper/clihelper.go index 3e13c20653..f50e359b5f 100644 --- a/pkg/platform/clihelper/clihelper.go +++ b/pkg/platform/clihelper/clihelper.go @@ -112,10 +112,8 @@ func GetProKubeConfig(options kubeconfig.ContextOptions) (*clientcmdapi.Config, cluster.InsecureSkipTLSVerify = options.InsecureSkipTLSVerify authInfo := clientcmdapi.NewAuthInfo() - if options.Token != "" || options.ClientCertificateData != nil || options.ClientKeyData != nil { + if options.Token != "" { authInfo.Token = options.Token - authInfo.ClientKeyData = options.ClientKeyData - authInfo.ClientCertificateData = options.ClientCertificateData } config := clientcmdapi.NewConfig() diff --git a/pkg/platform/helper.go b/pkg/platform/helper.go index d50dc884a6..a0765d955b 100644 --- a/pkg/platform/helper.go +++ b/pkg/platform/helper.go @@ -17,23 +17,18 @@ import ( "github.com/loft-sh/api/v4/pkg/clientset/versioned/scheme" "github.com/loft-sh/log" "github.com/loft-sh/log/survey" - "github.com/loft-sh/vcluster/pkg/cli/config" "github.com/loft-sh/vcluster/pkg/platform/clihelper" "github.com/loft-sh/vcluster/pkg/platform/kube" "github.com/loft-sh/vcluster/pkg/platform/kubeconfig" "github.com/loft-sh/vcluster/pkg/platform/sleepmode" "github.com/loft-sh/vcluster/pkg/projectutil" "github.com/loft-sh/vcluster/pkg/util" - perrors "github.com/pkg/errors" "gopkg.in/yaml.v2" authorizationv1 "k8s.io/api/authorization/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/client-go/tools/clientcmd" - clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "k8s.io/kubectl/pkg/util/term" - "k8s.io/utils/ptr" crclient "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -690,44 +685,14 @@ func CreateVirtualClusterInstanceOptions(ctx context.Context, client Client, con ConfigPath: config, SetActive: setActive, } - if virtualClusterInstance.Status.VirtualCluster != nil && virtualClusterInstance.Status.VirtualCluster.AccessPoint.Ingress.Enabled { - kubeConfig, err := getVirtualClusterInstanceAccessConfig(ctx, client, virtualClusterInstance) - if err != nil { - return kubeconfig.ContextOptions{}, fmt.Errorf("retrieve kube config %w", err) - } - - // get server - for _, val := range kubeConfig.Clusters { - if val != nil { - contextOptions.Server = val.Server - } - } - - if len(kubeConfig.AuthInfos) == 0 { - return kubeconfig.ContextOptions{}, errors.New("ingress access is configured but no credentials were present in the kubeconfig") - } - // find the first user and fill cert data with it - for _, v := range kubeConfig.AuthInfos { - contextOptions.ClientCertificateData = v.ClientCertificateData - contextOptions.ClientKeyData = v.ClientKeyData - break - } - if contextOptions.Server == "" { - return kubeconfig.ContextOptions{}, errors.New("could not determine server url") - } - - contextOptions.InsecureSkipTLSVerify = true - contextOptions.VirtualClusterAccessPointEnabled = true - } else { - contextOptions.Server = client.Config().Platform.Host + "/kubernetes/project/" + projectName + "/virtualcluster/" + virtualClusterInstance.Name - contextOptions.InsecureSkipTLSVerify = client.Config().Platform.Insecure + contextOptions.Server = client.Config().Platform.Host + "/kubernetes/project/" + projectName + "/virtualcluster/" + virtualClusterInstance.Name + contextOptions.InsecureSkipTLSVerify = client.Config().Platform.Insecure - data, err := RetrieveCaData(cluster) - if err != nil { - return kubeconfig.ContextOptions{}, err - } - contextOptions.CaData = data + data, err := RetrieveCaData(cluster) + if err != nil { + return kubeconfig.ContextOptions{}, err } + contextOptions.CaData = data return contextOptions, nil } @@ -754,59 +719,6 @@ func CreateSpaceInstanceOptions(ctx context.Context, client Client, config strin return contextOptions, nil } -func VirtualClusterAccessPointCertificate(client Client, project, virtualCluster string, forceRefresh bool) (string, string, error) { - contextName := kubeconfig.VirtualClusterInstanceContextName(project, virtualCluster) - - // see if we have stored cert data for this vci - now := metav1.Now() - cachedVirtualClusterAccessPointCertificate, ok := client.Config().Platform.VirtualClusterAccessPointCertificates[contextName] - if !forceRefresh && ok && cachedVirtualClusterAccessPointCertificate.LastRequested.Add(RefreshToken).After(now.Time) && cachedVirtualClusterAccessPointCertificate.ExpirationTime.After(now.Time) { - return cachedVirtualClusterAccessPointCertificate.CertificateData, cachedVirtualClusterAccessPointCertificate.KeyData, nil - } - - // refresh token - managementClient, err := client.Management() - if err != nil { - return "", "", err - } - - kubeConfigResponse, err := managementClient.Loft().ManagementV1().VirtualClusterInstances(projectutil.ProjectNamespace(project)).GetKubeConfig( - context.Background(), - virtualCluster, - &managementv1.VirtualClusterInstanceKubeConfig{ - Spec: managementv1.VirtualClusterInstanceKubeConfigSpec{ - CertificateTTL: ptr.To[int32](86_400), - }, - }, - metav1.CreateOptions{}, - ) - if err != nil { - return "", "", perrors.Wrap(err, "fetch certificate data") - } - - certificateData, keyData, err := getCertificateAndKeyDataFromKubeConfig(kubeConfigResponse.Status.KubeConfig) - if err != nil { - return "", "", err - } - - if client.Config().Platform.VirtualClusterAccessPointCertificates == nil { - client.Config().Platform.VirtualClusterAccessPointCertificates = make(map[string]config.VirtualClusterCertificatesEntry) - } - client.Config().Platform.VirtualClusterAccessPointCertificates[contextName] = config.VirtualClusterCertificatesEntry{ - CertificateData: certificateData, - KeyData: keyData, - LastRequested: now, - ExpirationTime: now.Add(86_400 * time.Second), - } - - err = client.Save() - if err != nil { - return "", "", perrors.Wrap(err, "save config") - } - - return certificateData, keyData, nil -} - func ResolveVirtualClusterTemplate( ctx context.Context, client Client, @@ -1260,57 +1172,6 @@ func wakeupVCluster(ctx context.Context, managementClient kube.Interface, virtua return nil } -func getCertificateAndKeyDataFromKubeConfig(config string) (string, string, error) { - clientCfg, err := clientcmd.NewClientConfigFromBytes([]byte(config)) - if err != nil { - return "", "", err - } - - apiCfg, err := clientCfg.RawConfig() - if err != nil { - return "", "", err - } - - authInfo, ok := apiCfg.AuthInfos["vcluster"] - if !ok || authInfo == nil { - return "", "", errors.New("couldn't find vcluster auth infos") - } - - return string(authInfo.ClientCertificateData), string(authInfo.ClientKeyData), nil -} - -func getVirtualClusterInstanceAccessConfig(ctx context.Context, client Client, virtualClusterInstance *managementv1.VirtualClusterInstance) (clientcmdapi.Config, error) { - managementClient, err := client.Management() - if err != nil { - return clientcmdapi.Config{}, err - } - - kubeConfig, err := managementClient.Loft().ManagementV1().VirtualClusterInstances(virtualClusterInstance.Namespace).GetKubeConfig( - ctx, - virtualClusterInstance.Name, - &managementv1.VirtualClusterInstanceKubeConfig{ - Spec: managementv1.VirtualClusterInstanceKubeConfigSpec{}, - }, - metav1.CreateOptions{}, - ) - if err != nil { - return clientcmdapi.Config{}, err - } - - // parse kube config string - clientCfg, err := clientcmd.NewClientConfigFromBytes([]byte(kubeConfig.Status.KubeConfig)) - if err != nil { - return clientcmdapi.Config{}, err - } - - apiCfg, err := clientCfg.RawConfig() - if err != nil { - return clientcmdapi.Config{}, err - } - - return apiCfg, nil -} - func findProjectCluster(ctx context.Context, client Client, projectName, clusterName string) (*managementv1.Cluster, error) { managementClient, err := client.Management() if err != nil { diff --git a/pkg/platform/kubeconfig/kubeconfig.go b/pkg/platform/kubeconfig/kubeconfig.go index 5e25bb872b..c733e88418 100644 --- a/pkg/platform/kubeconfig/kubeconfig.go +++ b/pkg/platform/kubeconfig/kubeconfig.go @@ -9,23 +9,18 @@ import ( "github.com/loft-sh/vcluster/pkg/cli/config" "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1" - "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd/api" ) type ContextOptions struct { - Name string - Server string - CaData []byte - ConfigPath string - InsecureSkipTLSVerify bool - DirectClusterEndpointEnabled bool - VirtualClusterAccessPointEnabled bool - - Token string - ClientKeyData []byte - ClientCertificateData []byte + Name string + Server string + CaData []byte + ConfigPath string + InsecureSkipTLSVerify bool + + Token string CurrentNamespace string SetActive bool @@ -39,10 +34,6 @@ func VirtualClusterInstanceContextName(projectName, virtualClusterInstance strin return "vcluster-platform-vcluster_" + virtualClusterInstance + "_" + projectName } -func virtualClusterInstanceProjectAndNameFromContextName(contextName string) (string, string) { - return strings.Split(contextName, "_")[2], strings.Split(contextName, "_")[1] -} - func SpaceContextName(clusterName, namespaceName string) string { contextName := "vcluster-platform_" if namespaceName != "" { @@ -53,48 +44,10 @@ func SpaceContextName(clusterName, namespaceName string) string { return contextName } -func VirtualClusterContextName(clusterName, namespaceName, virtualClusterName string) string { - return "vcluster-platform-vcluster_" + virtualClusterName + "_" + namespaceName + "_" + clusterName -} - func ManagementContextName() string { return "vcluster-platform-management" } -func ParseContext(contextName string) (isPlatformContext bool, cluster string, namespace string, vCluster string) { - splitted := strings.Split(contextName, "_") - if len(splitted) == 0 || (splitted[0] != "vcluster-platform" && splitted[0] != "vcluster-platform-vcluster") { - return false, "", "", "" - } - - // cluster or space context - if splitted[0] == "vcluster-platform" { - if len(splitted) > 3 || len(splitted) == 1 { - return false, "", "", "" - } else if len(splitted) == 2 { - return true, splitted[1], "", "" - } - - return true, splitted[2], splitted[1], "" - } - - // vCluster context - if len(splitted) != 4 { - return false, "", "", "" - } - - return true, splitted[3], splitted[2], splitted[1] -} - -func CurrentContext() (string, error) { - config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{}).RawConfig() - if err != nil { - return "", err - } - - return config.CurrentContext, nil -} - // DeleteContext deletes the context with the given name from the kube config func DeleteContext(contextName string) error { config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{}).RawConfig() @@ -199,36 +152,6 @@ func PrintKubeConfigTo(options ContextOptions, writer io.Writer) error { return printKubeConfigTo(contextName, cluster, authInfo, options.CurrentNamespace, writer) } -// PrintTokenKubeConfig writes the kube config to the os.Stdout -func PrintTokenKubeConfig(restConfig *rest.Config, token string) error { - contextName, cluster, authInfo := createTokenContext(restConfig, token) - - return printKubeConfigTo(contextName, cluster, authInfo, "", os.Stdout) -} - -// WriteTokenKubeConfig writes the kube config to the io.Writer -func WriteTokenKubeConfig(restConfig *rest.Config, token string, w io.Writer) error { - contextName, cluster, authInfo := createTokenContext(restConfig, token) - - return printKubeConfigTo(contextName, cluster, authInfo, "", w) -} - -func createTokenContext(restConfig *rest.Config, token string) (string, *api.Cluster, *api.AuthInfo) { - contextName := "default" - - cluster := api.NewCluster() - cluster.Server = restConfig.Host - cluster.InsecureSkipTLSVerify = restConfig.Insecure - cluster.CertificateAuthority = restConfig.CAFile - cluster.CertificateAuthorityData = restConfig.CAData - cluster.TLSServerName = restConfig.ServerName - - authInfo := api.NewAuthInfo() - authInfo.Token = token - - return contextName, cluster, authInfo -} - func createContext(options ContextOptions) (string, *api.Cluster, *api.AuthInfo, error) { contextName := options.Name cluster := api.NewCluster() @@ -237,10 +160,8 @@ func createContext(options ContextOptions) (string, *api.Cluster, *api.AuthInfo, cluster.InsecureSkipTLSVerify = options.InsecureSkipTLSVerify authInfo := api.NewAuthInfo() - if options.Token != "" || options.ClientCertificateData != nil || options.ClientKeyData != nil { + if options.Token != "" { authInfo.Token = options.Token - authInfo.ClientKeyData = options.ClientKeyData - authInfo.ClientCertificateData = options.ClientCertificateData } else { command, err := os.Executable() if err != nil { @@ -252,22 +173,10 @@ func createContext(options ContextOptions) (string, *api.Cluster, *api.AuthInfo, return "", nil, nil, err } - if options.VirtualClusterAccessPointEnabled { - projectName, virtualClusterName := virtualClusterInstanceProjectAndNameFromContextName(contextName) - authInfo.Exec = &api.ExecConfig{ - APIVersion: v1beta1.SchemeGroupVersion.String(), - Command: command, - Args: []string{"platform", "token", "--silent", "--project", projectName, "--virtual-cluster", virtualClusterName}, - } - } else { - authInfo.Exec = &api.ExecConfig{ - APIVersion: v1beta1.SchemeGroupVersion.String(), - Command: command, - Args: []string{"platform", "token", "--silent", "--config", absConfigPath}, - } - if options.DirectClusterEndpointEnabled { - authInfo.Exec.Args = append(authInfo.Exec.Args, "--direct-cluster-endpoint") - } + authInfo.Exec = &api.ExecConfig{ + APIVersion: v1beta1.SchemeGroupVersion.String(), + Command: command, + Args: []string{"platform", "token", "--silent", "--config", absConfigPath}, } }