-
Notifications
You must be signed in to change notification settings - Fork 218
fall back to kubeconfig loader behavior (exec plugin capable) #986
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
28662b3
bcdca61
b7763e8
d0efa85
54d18c7
42b9046
ae1d8b4
5783312
27d0d19
9afa07c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -241,7 +241,7 @@ type ApplyHelmChartConfig struct { | |||||
| // }, | ||||||
| // OverrideValues: vals, | ||||||
| // }) | ||||||
| func (client *Client) ApplyHelmChart(cfg ApplyHelmChartConfig) error { | ||||||
| func (c *Client) ApplyHelmChart(cfg ApplyHelmChartConfig) error { | ||||||
| setupDefaults(&cfg) | ||||||
|
|
||||||
| if err := setupChartVersion(&cfg); err != nil { | ||||||
|
|
@@ -264,11 +264,10 @@ func (client *Client) ApplyHelmChart(cfg ApplyHelmChartConfig) error { | |||||
| return ErrApplyHelmChart(err) | ||||||
| } | ||||||
|
|
||||||
| actionConfig, cleanup, err := createHelmActionConfig(client, cfg) | ||||||
| actionConfig, err := c.createHelmActionConfig(cfg, c.getRESTClientGetter()) | ||||||
| if err != nil { | ||||||
| return ErrApplyHelmChart(err) | ||||||
| } | ||||||
| defer cleanup() | ||||||
|
|
||||||
| // Before installing a helm chart, check if it already exists in the cluster | ||||||
| // this is a workaround make the helm chart installation idempotent | ||||||
|
|
@@ -408,96 +407,16 @@ func checkIfInstallable(ch *chart.Chart) error { | |||||
| } | ||||||
|
|
||||||
| // createHelmActionConfig generates the actionConfig with the appropriate defaults | ||||||
| func createHelmActionConfig(c *Client, cfg ApplyHelmChartConfig) (*action.Configuration, func(), error) { | ||||||
| func (c *Client) createHelmActionConfig(cfg ApplyHelmChartConfig, restClientGetter genericclioptions.RESTClientGetter) (*action.Configuration, error) { | ||||||
| // Set the environment variable needed by the Init methods | ||||||
| _ = os.Setenv("HELM_DRIVER_SQL_CONNECTION_STRING", cfg.SQLConnectionString) | ||||||
|
|
||||||
| var tempFiles []string | ||||||
| cleanup := func() { | ||||||
| for _, f := range tempFiles { | ||||||
| _ = os.Remove(f) | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| // KubeConfig setup | ||||||
| kubeConfig := genericclioptions.NewConfigFlags(false) | ||||||
| // Set KubeConfig to DevNull to prevent read from local kubeconfig | ||||||
| // to prevent conflicts between "data" and "files" properties (CAFile, CAData and KeyFile, KeyData) | ||||||
| // ConfigFlags only allows setting CAFile, KeyFile but not CAData, KeyData. | ||||||
| // When the library reads the original kubeconfig containing cert data / key data AND we specify cert file / key file, these configurations conflict | ||||||
| devNull := os.DevNull | ||||||
| kubeConfig.KubeConfig = &devNull | ||||||
| kubeConfig.APIServer = &c.RestConfig.Host | ||||||
| kubeConfig.BearerToken = &c.RestConfig.BearerToken | ||||||
| kubeConfig.Insecure = &c.RestConfig.Insecure | ||||||
|
|
||||||
| // Set username and password for basic auth if available | ||||||
| if c.RestConfig.Username != "" { | ||||||
| kubeConfig.Username = &c.RestConfig.Username | ||||||
| } | ||||||
| if c.RestConfig.Password != "" { | ||||||
| kubeConfig.Password = &c.RestConfig.Password | ||||||
| } | ||||||
|
|
||||||
| // Only set CA file if not running in insecure mode | ||||||
| if !c.RestConfig.Insecure { | ||||||
| if len(c.RestConfig.CAData) > 0 { | ||||||
| caFileName, err := setDataAndReturnFilename(c.RestConfig.CAData) | ||||||
| if err != nil { | ||||||
| cleanup() // Clean up any files created so far | ||||||
| return nil, nil, err | ||||||
| } | ||||||
| tempFiles = append(tempFiles, caFileName) | ||||||
| kubeConfig.CAFile = &caFileName | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| // Set client certificate data if available | ||||||
| if len(c.RestConfig.CertData) > 0 { | ||||||
| certFileName, err := setDataAndReturnFilename(c.RestConfig.CertData) | ||||||
| if err != nil { | ||||||
| cleanup() | ||||||
| return nil, nil, err | ||||||
| } | ||||||
| tempFiles = append(tempFiles, certFileName) | ||||||
| kubeConfig.CertFile = &certFileName | ||||||
| } | ||||||
|
|
||||||
| // Set client key data if available | ||||||
| if len(c.RestConfig.KeyData) > 0 { | ||||||
| keyFileName, err := setDataAndReturnFilename(c.RestConfig.KeyData) | ||||||
| if err != nil { | ||||||
| cleanup() // Clean up any files created so far | ||||||
| return nil, nil, err | ||||||
| } | ||||||
| tempFiles = append(tempFiles, keyFileName) | ||||||
| kubeConfig.KeyFile = &keyFileName | ||||||
| } | ||||||
|
|
||||||
| actionConfig := new(action.Configuration) | ||||||
| if err := actionConfig.Init(kubeConfig, cfg.Namespace, string(cfg.HelmDriver), cfg.Logger); err != nil { | ||||||
| cleanup() // Clean up any files created so far | ||||||
| return nil, nil, ErrApplyHelmChart(err) | ||||||
| if err := actionConfig.Init(restClientGetter, cfg.Namespace, string(cfg.HelmDriver), cfg.Logger); err != nil { | ||||||
| return nil, ErrApplyHelmChart(err) | ||||||
| } | ||||||
|
|
||||||
| return actionConfig, cleanup, nil | ||||||
| } | ||||||
|
|
||||||
| // Populates a file in temp directory with the passed data and returns the filename | ||||||
| func setDataAndReturnFilename(data []byte) (string, error) { | ||||||
| f, err := os.CreateTemp("", "") | ||||||
| if err != nil { | ||||||
| return "", err | ||||||
| } | ||||||
| defer func() { _ = f.Close() }() // Close file immediately after writing | ||||||
|
|
||||||
| _, err = f.Write(data) | ||||||
| if err != nil { | ||||||
| _ = os.Remove(f.Name()) // Clean up on write error | ||||||
| return "", err | ||||||
| } | ||||||
|
|
||||||
| return f.Name(), nil | ||||||
| return actionConfig, nil | ||||||
| } | ||||||
|
|
||||||
| // generateAction generates an action function using action.Configuration | ||||||
|
|
@@ -554,7 +473,8 @@ func createHelmPathFromHelmChartLocation(loc HelmChartLocation) (string, error) | |||||
| getter.Provider{ | ||||||
| Schemes: []string{"http", "https"}, | ||||||
| New: getter.NewHTTPGetter, | ||||||
| }}, | ||||||
| }, | ||||||
| }, | ||||||
| ) | ||||||
| if err != nil { | ||||||
| return "", ErrApplyHelmChart(err) | ||||||
|
|
@@ -643,7 +563,7 @@ func (helmEntries HelmEntries) GetEntryWithAppVersion(entry, appVersion string) | |||||
| return HelmEntryMetadata{}, false | ||||||
| } | ||||||
|
|
||||||
| // GetEntryWithAppVersion takes in the entry name and the appversion and returns the corresponding | ||||||
| // GetEntryWithChartVersion takes in the entry name and the appversion and returns the corresponding | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win The comment still says "appversion". The function is 📝 Proposed fix-// GetEntryWithChartVersion takes in the entry name and the appversion and returns the corresponding
+// GetEntryWithChartVersion takes in the entry name and the chart version and returns the corresponding📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| // metadata for the parameters if it exists | ||||||
| func (helmEntries HelmEntries) GetEntryWithChartVersion(entry, chartVersion string) (HelmEntryMetadata, bool) { | ||||||
| hem, ok := helmEntries[entry] | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| package kubernetes | ||
|
|
||
| import ( | ||
| "k8s.io/apimachinery/pkg/api/meta" | ||
| "k8s.io/cli-runtime/pkg/genericclioptions" | ||
| "k8s.io/client-go/discovery" | ||
| "k8s.io/client-go/discovery/cached/memory" | ||
| "k8s.io/client-go/rest" | ||
| "k8s.io/client-go/restmapper" | ||
| "k8s.io/client-go/tools/clientcmd" | ||
| clientcmdapi "k8s.io/client-go/tools/clientcmd/api" | ||
| ) | ||
|
|
||
| type clientConfigRESTClientGetter struct { | ||
| clientConfig clientcmd.ClientConfig | ||
| } | ||
|
|
||
| type restConfigClientConfig struct { | ||
| restConfig *rest.Config | ||
| } | ||
|
|
||
| var _ genericclioptions.RESTClientGetter = (*clientConfigRESTClientGetter)(nil) | ||
| var _ clientcmd.ClientConfig = (*restConfigClientConfig)(nil) | ||
|
|
||
| func newClientConfigRESTClientGetter(clientConfig clientcmd.ClientConfig) genericclioptions.RESTClientGetter { | ||
| return &clientConfigRESTClientGetter{clientConfig: clientConfig} | ||
| } | ||
|
|
||
| func newRESTConfigRESTClientGetter(config *rest.Config) genericclioptions.RESTClientGetter { | ||
| return newClientConfigRESTClientGetter(&restConfigClientConfig{ | ||
| restConfig: rest.CopyConfig(config), | ||
| }) | ||
| } | ||
|
|
||
| func (g *clientConfigRESTClientGetter) ToRESTConfig() (*rest.Config, error) { | ||
| config, err := g.clientConfig.ClientConfig() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| configureRESTConfig(config) | ||
| return config, nil | ||
| } | ||
|
|
||
| func (g *clientConfigRESTClientGetter) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) { | ||
| config, err := g.ToRESTConfig() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| discoveryClient, err := discovery.NewDiscoveryClientForConfig(config) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| return memory.NewMemCacheClient(discoveryClient), nil | ||
| } | ||
|
|
||
| func (g *clientConfigRESTClientGetter) ToRESTMapper() (meta.RESTMapper, error) { | ||
| discoveryClient, err := g.ToDiscoveryClient() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| mapper := restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient) | ||
| return restmapper.NewShortcutExpander(mapper, discoveryClient, func(string) {}), nil | ||
| } | ||
|
|
||
| func (g *clientConfigRESTClientGetter) ToRawKubeConfigLoader() clientcmd.ClientConfig { | ||
| return g.clientConfig | ||
| } | ||
|
|
||
| func (c *restConfigClientConfig) RawConfig() (clientcmdapi.Config, error) { | ||
| const connectionName = "meshkit-connection" | ||
|
|
||
| config := clientcmdapi.NewConfig() | ||
| config.Clusters[connectionName] = &clientcmdapi.Cluster{ | ||
| Server: c.restConfig.Host, | ||
| TLSServerName: c.restConfig.ServerName, | ||
| InsecureSkipTLSVerify: c.restConfig.Insecure, | ||
| CertificateAuthority: c.restConfig.CAFile, | ||
| CertificateAuthorityData: c.restConfig.CAData, | ||
| DisableCompression: c.restConfig.DisableCompression, | ||
| } | ||
| config.Contexts[connectionName] = &clientcmdapi.Context{ | ||
| Cluster: connectionName, | ||
| } | ||
| config.CurrentContext = connectionName | ||
| return *config, nil | ||
| } | ||
|
Comment on lines
+72
to
+89
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
The synthetic config contains a cluster and a context, but no Add an 🔐 Proposed fix to preserve credentials in
|
||
|
|
||
| func (c *restConfigClientConfig) ClientConfig() (*rest.Config, error) { | ||
| return rest.CopyConfig(c.restConfig), nil | ||
| } | ||
|
|
||
| func (c *restConfigClientConfig) Namespace() (string, bool, error) { | ||
| return "default", false, nil | ||
| } | ||
|
|
||
| func (c *restConfigClientConfig) ConfigAccess() clientcmd.ConfigAccess { | ||
| return nil | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
os.Setenvmutates process-global state and now leaks into exec credential plugins.Three problems in this line:
ApplyHelmChartcalls with differentSQLConnectionStringvalues race on the same process environment.cfg.SQLConnectionStringis empty, the call clears any value that was set earlier in the process.aws eks get-token. Child processes inherit the process environment, so the SQL connection string, including any embedded password, is now exposed to those plugins.Set the variable only when
cfg.HelmDriveris the SQL driver andcfg.SQLConnectionStringis not empty. Also check the returned error.🔐 Proposed fix
🤖 Prompt for AI Agents