Skip to content

Commit

Permalink
Merge pull request #2185 from FabianKramm/refactor-ingress-auth
Browse files Browse the repository at this point in the history
Use regular access key for all vCluster types
  • Loading branch information
FabianKramm authored Sep 27, 2024
2 parents dcfb601 + 38d5819 commit 98d59fe
Show file tree
Hide file tree
Showing 6 changed files with 26 additions and 294 deletions.
47 changes: 6 additions & 41 deletions cmd/vclusterctl/cmd/platform/access_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
1 change: 0 additions & 1 deletion pkg/cli/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ func New() *CLI {
Kind: "Config",
APIVersion: "storage.loft.sh/v1",
},
VirtualClusterAccessPointCertificates: make(map[string]VirtualClusterCertificatesEntry),
},
}
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/cli/connect_platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
4 changes: 1 addition & 3 deletions pkg/platform/clihelper/clihelper.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
151 changes: 6 additions & 145 deletions pkg/platform/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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
}

Expand All @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down
Loading

0 comments on commit 98d59fe

Please sign in to comment.