| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- package local
- import (
- "encoding/base64"
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "github.com/porter-dev/porter/internal/kubernetes"
- k8s "k8s.io/client-go/kubernetes"
- "k8s.io/client-go/tools/clientcmd"
- "k8s.io/client-go/tools/clientcmd/api"
- clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
- "k8s.io/client-go/util/homedir"
- )
- // GetKubeconfigFromHost returns the kubeconfig for a list of contexts using default
- // options set on the host, or an explicit kubeconfig path. It then strips the kubeconfig
- // of contexts not specified in the contexts array, and returns generate kubeconfig.
- func GetKubeconfigFromHost(kubeconfigPath string, contexts []string) ([]byte, error) {
- kubeconfigPath, err := ResolveKubeconfigPath(kubeconfigPath)
- if err != nil {
- return nil, err
- }
- loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
- loadingRules.ExplicitPath = kubeconfigPath
- clientConf := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
- rawConf, err := clientConf.RawConfig()
- if err != nil {
- return nil, err
- }
- if len(contexts) == 0 {
- contexts = []string{rawConf.CurrentContext}
- if contexts[0] == "" {
- return nil, fmt.Errorf("at least one context must be specified")
- }
- }
- conf, err := stripAndValidateClientContexts(&rawConf, contexts[0], contexts)
- if err != nil {
- return nil, err
- }
- strippedRawConf, err := conf.RawConfig()
- if err != nil {
- return nil, err
- }
- return clientcmd.Write(strippedRawConf)
- }
- // GetSelfAgentFromFileConfig reads a kubeconfig from a local file and generates an
- // Agent from that kubeconfig
- func GetSelfAgentFromFileConfig(kubeconfigPath string) (*kubernetes.Agent, error) {
- configBytes, err := GetKubeconfigFromHost(kubeconfigPath, []string{})
- if err != nil {
- return nil, err
- }
- cmdConf, err := clientcmd.NewClientConfigFromBytes(configBytes)
- if err != nil {
- return nil, err
- }
- restConf, err := cmdConf.ClientConfig()
- if err != nil {
- return nil, err
- }
- var namespace string
- cmdConfNamespace, _, err := cmdConf.Namespace()
- if err == nil && cmdConfNamespace != "" {
- namespace = cmdConfNamespace
- }
- restClientGetter := kubernetes.NewRESTClientGetterFromInClusterConfig(restConf, namespace)
- clientset, err := k8s.NewForConfig(restConf)
- return &kubernetes.Agent{
- RESTClientGetter: restClientGetter,
- Clientset: clientset,
- }, nil
- }
- // ResolveKubeconfigPath finds the path to a kubeconfig, first searching for the
- // passed string, then in the home directory, then as an env variable.
- func ResolveKubeconfigPath(kubeconfigPath string) (string, error) {
- envVarName := clientcmd.RecommendedConfigPathEnvVar
- if kubeconfigPath != "" {
- if _, err := os.Stat(kubeconfigPath); os.IsNotExist(err) {
- // the specified kubeconfig does not exist, throw error
- return "", fmt.Errorf("kubeconfig not found: %s does not exist", kubeconfigPath)
- }
- }
- if kubeconfigPath == "" {
- if os.Getenv(envVarName) == "" {
- if home := homedir.HomeDir(); home != "" {
- kubeconfigPath = filepath.Join(home, ".kube", "config")
- }
- } else {
- kubeconfigPath = os.Getenv(envVarName)
- }
- }
- return kubeconfigPath, nil
- }
- // GetConfigFromHostWithCertData gets the kubeconfig using default options set on the host:
- // the kubeconfig can either be retrieved from a specified path or an environment variable.
- // This function only outputs a clientcmd that uses the allowedContexts.
- //
- // This function also populates all of the certificate data that's specified as a filepath.
- func GetConfigFromHostWithCertData(kubeconfigPath string, allowedContexts []string) (clientcmd.ClientConfig, error) {
- envVarName := clientcmd.RecommendedConfigPathEnvVar
- if kubeconfigPath != "" {
- if _, err := os.Stat(kubeconfigPath); os.IsNotExist(err) {
- // the specified kubeconfig does not exist so fallback to other options
- kubeconfigPath = ""
- }
- }
- if kubeconfigPath == "" && os.Getenv(envVarName) == "" {
- if home := homedir.HomeDir(); home != "" {
- kubeconfigPath = filepath.Join(home, ".kube", "config")
- }
- }
- loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
- loadingRules.ExplicitPath = kubeconfigPath
- clientConf := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
- rawConf, err := clientConf.RawConfig()
- if err != nil {
- return nil, err
- }
- populateCertificateRefs(&rawConf)
- populateOIDCPluginCerts(&rawConf)
- if len(allowedContexts) == 0 {
- allowedContexts = []string{rawConf.CurrentContext}
- if allowedContexts[0] == "" {
- return nil, fmt.Errorf("at least one context must be specified")
- }
- }
- res, err := stripAndValidateClientContexts(&rawConf, allowedContexts[0], allowedContexts)
- if err != nil {
- return nil, err
- }
- return res, nil
- }
- func stripAndValidateClientContexts(
- rawConf *clientcmdapi.Config,
- currentContext string,
- allowedContexts []string,
- ) (clientcmd.ClientConfig, error) {
- // grab a copy to get the pointer and set clusters, authinfos, and contexts to empty
- copyConf := rawConf.DeepCopy()
- copyConf.Clusters = make(map[string]*api.Cluster)
- copyConf.AuthInfos = make(map[string]*api.AuthInfo)
- copyConf.Contexts = make(map[string]*api.Context)
- copyConf.CurrentContext = currentContext
- // put allowed clusters in a map
- aContextMap := kubernetes.CreateAllowedContextMap(allowedContexts)
- for contextName, context := range rawConf.Contexts {
- userName := context.AuthInfo
- clusterName := context.Cluster
- authInfo, userFound := rawConf.AuthInfos[userName]
- cluster, clusterFound := rawConf.Clusters[clusterName]
- // make sure the cluster is "allowed"
- _, isAllowed := aContextMap[contextName]
- if userFound && clusterFound && isAllowed {
- copyConf.Clusters[clusterName] = cluster
- copyConf.AuthInfos[userName] = authInfo
- copyConf.Contexts[contextName] = context
- }
- }
- // validate the copyConf and create a ClientConfig
- err := clientcmd.Validate(*copyConf)
- if err != nil {
- return nil, err
- }
- clientConf := clientcmd.NewDefaultClientConfig(*copyConf, &clientcmd.ConfigOverrides{})
- return clientConf, nil
- }
- func populateCertificateRefs(config *clientcmdapi.Config) {
- for _, cluster := range config.Clusters {
- refs := clientcmd.GetClusterFileReferences(cluster)
- for _, str := range refs {
- // only write certificate if the file reference is CA
- if *str != cluster.CertificateAuthority {
- break
- }
- fileBytes, err := ioutil.ReadFile(*str)
- if err != nil {
- continue
- }
- cluster.CertificateAuthorityData = fileBytes
- cluster.CertificateAuthority = ""
- }
- }
- for _, authInfo := range config.AuthInfos {
- refs := clientcmd.GetAuthInfoFileReferences(authInfo)
- for _, str := range refs {
- if *str == "" {
- continue
- }
- var refType int
- if authInfo.ClientCertificate == *str {
- refType = 0
- } else if authInfo.ClientKey == *str {
- refType = 1
- } else if authInfo.TokenFile == *str {
- refType = 2
- }
- fileBytes, err := ioutil.ReadFile(*str)
- if err != nil {
- continue
- }
- if refType == 0 {
- authInfo.ClientCertificateData = fileBytes
- authInfo.ClientCertificate = ""
- } else if refType == 1 {
- authInfo.ClientKeyData = fileBytes
- authInfo.ClientKey = ""
- } else if refType == 2 {
- authInfo.Token = base64.StdEncoding.EncodeToString(fileBytes)
- authInfo.TokenFile = ""
- }
- }
- }
- }
- func populateOIDCPluginCerts(config *clientcmdapi.Config) {
- for _, authInfo := range config.AuthInfos {
- if authInfo.AuthProvider != nil && authInfo.AuthProvider.Name == "oidc" {
- if ca, ok := authInfo.AuthProvider.Config["idp-certificate-authority"]; ok && ca != "" {
- fileBytes, err := ioutil.ReadFile(ca)
- if err != nil {
- continue
- }
- authInfo.AuthProvider.Config["idp-certificate-authority-data"] = base64.StdEncoding.EncodeToString(fileBytes)
- delete(authInfo.AuthProvider.Config, "idp-certificate-authority")
- }
- }
- }
- }
|