VYPR
Moderate severityNVD Advisory· Published Feb 8, 2023· Updated Mar 10, 2025

getHostByName Function Information Disclosure

CVE-2023-25165

Description

Helm is a tool that streamlines installing and managing Kubernetes applications.getHostByName is a Helm template function introduced in Helm v3. The function is able to accept a hostname and return an IP address for that hostname. To get the IP address the function performs a DNS lookup. The DNS lookup happens when used with helm install|upgrade|template or when the Helm SDK is used to render a chart. Information passed into the chart can be disclosed to the DNS servers used to lookup the IP address. For example, a malicious chart could inject getHostByName into a chart in order to disclose values to a malicious DNS server. The issue has been fixed in Helm 3.11.1. Prior to using a chart with Helm verify the getHostByName function is not being used in a template to disclose any information you do not want passed to DNS servers.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
helm.sh/helm/v3Go
>= 3.0.0, < 3.11.13.11.1

Affected products

1

Patches

2
5abcf74227bf

Merge pull request from GHSA-pwcw-6f5g-gxf8

https://github.com/helm/helmMatt FarinaFeb 8, 2023via ghsa
7 files changed · +78 10
  • cmd/helm/install.go+1 0 modified
    @@ -170,6 +170,7 @@ func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Instal
     	f.BoolVar(&client.Atomic, "atomic", false, "if set, the installation process deletes the installation on failure. The --wait flag will be set automatically if --atomic is used")
     	f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed. By default, CRDs are installed if not already present")
     	f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
    +	f.BoolVar(&client.EnableDNS, "enable-dns", false, "enable DNS lookups when rendering templates")
     	addValueOptionsFlags(f, valueOpts)
     	addChartPathOptionsFlags(f, &client.ChartPathOptions)
     
    
  • cmd/helm/upgrade.go+2 0 modified
    @@ -119,6 +119,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
     					instClient.SubNotes = client.SubNotes
     					instClient.Description = client.Description
     					instClient.DependencyUpdate = client.DependencyUpdate
    +					instClient.EnableDNS = client.EnableDNS
     
     					rel, err := runInstall(args, instClient, valueOpts, out)
     					if err != nil {
    @@ -232,6 +233,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
     	f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
     	f.StringVar(&client.Description, "description", "", "add a custom description")
     	f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart")
    +	f.BoolVar(&client.EnableDNS, "enable-dns", false, "enable DNS lookups when rendering templates")
     	addChartPathOptionsFlags(f, &client.ChartPathOptions)
     	addValueOptionsFlags(f, valueOpts)
     	bindOutputFlag(cmd, &outfmt)
    
  • pkg/action/action.go+9 4 modified
    @@ -101,8 +101,9 @@ type Configuration struct {
     //
     // TODO: This function is badly in need of a refactor.
     // TODO: As part of the refactor the duplicate code in cmd/helm/template.go should be removed
    -//       This code has to do with writing files to disk.
    -func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrender.PostRenderer, dryRun bool) ([]*release.Hook, *bytes.Buffer, string, error) {
    +//
    +//	This code has to do with writing files to disk.
    +func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrender.PostRenderer, dryRun, enableDNS bool) ([]*release.Hook, *bytes.Buffer, string, error) {
     	hs := []*release.Hook{}
     	b := bytes.NewBuffer(nil)
     
    @@ -130,9 +131,13 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Valu
     		if err != nil {
     			return hs, b, "", err
     		}
    -		files, err2 = engine.RenderWithClient(ch, values, restConfig)
    +		e := engine.New(restConfig)
    +		e.EnableDNS = enableDNS
    +		files, err2 = e.Render(ch, values)
     	} else {
    -		files, err2 = engine.Render(ch, values)
    +		var e engine.Engine
    +		e.EnableDNS = enableDNS
    +		files, err2 = e.Render(ch, values)
     	}
     
     	if err2 != nil {
    
  • pkg/action/install.go+7 5 modified
    @@ -97,6 +97,8 @@ type Install struct {
     	APIVersions chartutil.VersionSet
     	// Used by helm template to render charts with .Release.IsUpgrade. Ignored if Dry-Run is false
     	IsUpgrade bool
    +	// Enable DNS lookups when rendering templates
    +	EnableDNS bool
     	// Used by helm template to add the release as part of OutputDir path
     	// OutputDir/<ReleaseName>
     	UseReleaseName bool
    @@ -257,7 +259,7 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
     	rel := i.createRelease(chrt, vals)
     
     	var manifestDoc *bytes.Buffer
    -	rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.PostRenderer, i.DryRun)
    +	rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.PostRenderer, i.DryRun, i.EnableDNS)
     	// Even for errors, attach this if available
     	if manifestDoc != nil {
     		rel.Manifest = manifestDoc.String()
    @@ -457,10 +459,10 @@ func (i *Install) failRelease(rel *release.Release, err error) (*release.Release
     //
     // Roughly, this will return an error if name is
     //
    -//	- empty
    -//	- too long
    -//	- already in use, and not deleted
    -//	- used by a deleted release, and i.Replace is false
    +//   - empty
    +//   - too long
    +//   - already in use, and not deleted
    +//   - used by a deleted release, and i.Replace is false
     func (i *Install) availableName() error {
     	start := i.ReleaseName
     
    
  • pkg/action/upgrade.go+3 1 modified
    @@ -103,6 +103,8 @@ type Upgrade struct {
     	DependencyUpdate bool
     	// Lock to control raceconditions when the process receives a SIGTERM
     	Lock sync.Mutex
    +	// Enable DNS lookups when rendering templates
    +	EnableDNS bool
     }
     
     type resultMessage struct {
    @@ -231,7 +233,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin
     		return nil, nil, err
     	}
     
    -	hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, u.DryRun)
    +	hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, u.DryRun, u.EnableDNS)
     	if err != nil {
     		return nil, nil, err
     	}
    
  • pkg/engine/engine.go+17 0 modified
    @@ -42,6 +42,15 @@ type Engine struct {
     	LintMode bool
     	// the rest config to connect to the kubernetes api
     	config *rest.Config
    +	// EnableDNS tells the engine to allow DNS lookups when rendering templates
    +	EnableDNS bool
    +}
    +
    +// New creates a new instance of Engine using the passed in rest config.
    +func New(config *rest.Config) Engine {
    +	return Engine{
    +		config: config,
    +	}
     }
     
     // Render takes a chart, optional values, and value overrides, and attempts to render the Go templates.
    @@ -189,6 +198,14 @@ func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]render
     		funcMap["lookup"] = NewLookupFunction(e.config)
     	}
     
    +	// When DNS lookups are not enabled override the sprig function and return
    +	// an empty string.
    +	if !e.EnableDNS {
    +		funcMap["getHostByName"] = func(name string) string {
    +			return ""
    +		}
    +	}
    +
     	t.Funcs(funcMap)
     }
     
    
  • pkg/engine/engine_test.go+39 0 modified
    @@ -18,6 +18,7 @@ package engine
     
     import (
     	"fmt"
    +	"path"
     	"strings"
     	"sync"
     	"testing"
    @@ -89,6 +90,7 @@ func TestRender(t *testing.T) {
     			{Name: "templates/test2", Data: []byte("{{.Values.global.callme | lower }}")},
     			{Name: "templates/test3", Data: []byte("{{.noValue}}")},
     			{Name: "templates/test4", Data: []byte("{{toJson .Values}}")},
    +			{Name: "templates/test5", Data: []byte("{{getHostByName \"helm.sh\"}}")},
     		},
     		Values: map[string]interface{}{"outer": "DEFAULT", "inner": "DEFAULT"},
     	}
    @@ -117,6 +119,7 @@ func TestRender(t *testing.T) {
     		"moby/templates/test2": "ishmael",
     		"moby/templates/test3": "",
     		"moby/templates/test4": `{"global":{"callme":"Ishmael"},"inner":"inn","outer":"spouter"}`,
    +		"moby/templates/test5": "",
     	}
     
     	for name, data := range expect {
    @@ -200,6 +203,42 @@ func TestRenderInternals(t *testing.T) {
     	}
     }
     
    +func TestRenderWIthDNS(t *testing.T) {
    +	c := &chart.Chart{
    +		Metadata: &chart.Metadata{
    +			Name:    "moby",
    +			Version: "1.2.3",
    +		},
    +		Templates: []*chart.File{
    +			{Name: "templates/test1", Data: []byte("{{getHostByName \"helm.sh\"}}")},
    +		},
    +		Values: map[string]interface{}{},
    +	}
    +
    +	vals := map[string]interface{}{
    +		"Values": map[string]interface{}{},
    +	}
    +
    +	v, err := chartutil.CoalesceValues(c, vals)
    +	if err != nil {
    +		t.Fatalf("Failed to coalesce values: %s", err)
    +	}
    +
    +	var e Engine
    +	e.EnableDNS = true
    +	out, err := e.Render(c, v)
    +	if err != nil {
    +		t.Errorf("Failed to render templates: %s", err)
    +	}
    +
    +	for _, val := range c.Templates {
    +		fp := path.Join("moby", val.Name)
    +		if out[fp] == "" {
    +			t.Errorf("Expected IP address, got %q", out[fp])
    +		}
    +	}
    +}
    +
     func TestParallelRenderInternals(t *testing.T) {
     	// Make sure that we can use one Engine to run parallel template renders.
     	e := new(Engine)
    
293b50c65d4d

Update to func handling

https://github.com/helm/helmMatt FarinaFeb 1, 2023via ghsa
7 files changed · +78 10
  • cmd/helm/install.go+1 0 modified
    @@ -170,6 +170,7 @@ func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Instal
     	f.BoolVar(&client.Atomic, "atomic", false, "if set, the installation process deletes the installation on failure. The --wait flag will be set automatically if --atomic is used")
     	f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed. By default, CRDs are installed if not already present")
     	f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
    +	f.BoolVar(&client.EnableDNS, "enable-dns", false, "enable DNS lookups when rendering templates")
     	addValueOptionsFlags(f, valueOpts)
     	addChartPathOptionsFlags(f, &client.ChartPathOptions)
     
    
  • cmd/helm/upgrade.go+2 0 modified
    @@ -119,6 +119,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
     					instClient.SubNotes = client.SubNotes
     					instClient.Description = client.Description
     					instClient.DependencyUpdate = client.DependencyUpdate
    +					instClient.EnableDNS = client.EnableDNS
     
     					rel, err := runInstall(args, instClient, valueOpts, out)
     					if err != nil {
    @@ -232,6 +233,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
     	f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
     	f.StringVar(&client.Description, "description", "", "add a custom description")
     	f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "update dependencies if they are missing before installing the chart")
    +	f.BoolVar(&client.EnableDNS, "enable-dns", false, "enable DNS lookups when rendering templates")
     	addChartPathOptionsFlags(f, &client.ChartPathOptions)
     	addValueOptionsFlags(f, valueOpts)
     	bindOutputFlag(cmd, &outfmt)
    
  • pkg/action/action.go+9 4 modified
    @@ -101,8 +101,9 @@ type Configuration struct {
     //
     // TODO: This function is badly in need of a refactor.
     // TODO: As part of the refactor the duplicate code in cmd/helm/template.go should be removed
    -//       This code has to do with writing files to disk.
    -func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrender.PostRenderer, dryRun bool) ([]*release.Hook, *bytes.Buffer, string, error) {
    +//
    +//	This code has to do with writing files to disk.
    +func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrender.PostRenderer, dryRun, enableDNS bool) ([]*release.Hook, *bytes.Buffer, string, error) {
     	hs := []*release.Hook{}
     	b := bytes.NewBuffer(nil)
     
    @@ -130,9 +131,13 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Valu
     		if err != nil {
     			return hs, b, "", err
     		}
    -		files, err2 = engine.RenderWithClient(ch, values, restConfig)
    +		e := engine.New(restConfig)
    +		e.EnableDNS = enableDNS
    +		files, err2 = e.Render(ch, values)
     	} else {
    -		files, err2 = engine.Render(ch, values)
    +		var e engine.Engine
    +		e.EnableDNS = enableDNS
    +		files, err2 = e.Render(ch, values)
     	}
     
     	if err2 != nil {
    
  • pkg/action/install.go+7 5 modified
    @@ -97,6 +97,8 @@ type Install struct {
     	APIVersions chartutil.VersionSet
     	// Used by helm template to render charts with .Release.IsUpgrade. Ignored if Dry-Run is false
     	IsUpgrade bool
    +	// Enable DNS lookups when rendering templates
    +	EnableDNS bool
     	// Used by helm template to add the release as part of OutputDir path
     	// OutputDir/<ReleaseName>
     	UseReleaseName bool
    @@ -257,7 +259,7 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
     	rel := i.createRelease(chrt, vals)
     
     	var manifestDoc *bytes.Buffer
    -	rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.PostRenderer, i.DryRun)
    +	rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.PostRenderer, i.DryRun, i.EnableDNS)
     	// Even for errors, attach this if available
     	if manifestDoc != nil {
     		rel.Manifest = manifestDoc.String()
    @@ -457,10 +459,10 @@ func (i *Install) failRelease(rel *release.Release, err error) (*release.Release
     //
     // Roughly, this will return an error if name is
     //
    -//	- empty
    -//	- too long
    -//	- already in use, and not deleted
    -//	- used by a deleted release, and i.Replace is false
    +//   - empty
    +//   - too long
    +//   - already in use, and not deleted
    +//   - used by a deleted release, and i.Replace is false
     func (i *Install) availableName() error {
     	start := i.ReleaseName
     
    
  • pkg/action/upgrade.go+3 1 modified
    @@ -103,6 +103,8 @@ type Upgrade struct {
     	DependencyUpdate bool
     	// Lock to control raceconditions when the process receives a SIGTERM
     	Lock sync.Mutex
    +	// Enable DNS lookups when rendering templates
    +	EnableDNS bool
     }
     
     type resultMessage struct {
    @@ -231,7 +233,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin
     		return nil, nil, err
     	}
     
    -	hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, u.DryRun)
    +	hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, u.DryRun, u.EnableDNS)
     	if err != nil {
     		return nil, nil, err
     	}
    
  • pkg/engine/engine.go+17 0 modified
    @@ -42,6 +42,15 @@ type Engine struct {
     	LintMode bool
     	// the rest config to connect to the kubernetes api
     	config *rest.Config
    +	// EnableDNS tells the engine to allow DNS lookups when rendering templates
    +	EnableDNS bool
    +}
    +
    +// New creates a new instance of Engine using the passed in rest config.
    +func New(config *rest.Config) Engine {
    +	return Engine{
    +		config: config,
    +	}
     }
     
     // Render takes a chart, optional values, and value overrides, and attempts to render the Go templates.
    @@ -189,6 +198,14 @@ func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]render
     		funcMap["lookup"] = NewLookupFunction(e.config)
     	}
     
    +	// When DNS lookups are not enabled override the sprig function and return
    +	// an empty string.
    +	if !e.EnableDNS {
    +		funcMap["getHostByName"] = func(name string) string {
    +			return ""
    +		}
    +	}
    +
     	t.Funcs(funcMap)
     }
     
    
  • pkg/engine/engine_test.go+39 0 modified
    @@ -18,6 +18,7 @@ package engine
     
     import (
     	"fmt"
    +	"path"
     	"strings"
     	"sync"
     	"testing"
    @@ -89,6 +90,7 @@ func TestRender(t *testing.T) {
     			{Name: "templates/test2", Data: []byte("{{.Values.global.callme | lower }}")},
     			{Name: "templates/test3", Data: []byte("{{.noValue}}")},
     			{Name: "templates/test4", Data: []byte("{{toJson .Values}}")},
    +			{Name: "templates/test5", Data: []byte("{{getHostByName \"helm.sh\"}}")},
     		},
     		Values: map[string]interface{}{"outer": "DEFAULT", "inner": "DEFAULT"},
     	}
    @@ -117,6 +119,7 @@ func TestRender(t *testing.T) {
     		"moby/templates/test2": "ishmael",
     		"moby/templates/test3": "",
     		"moby/templates/test4": `{"global":{"callme":"Ishmael"},"inner":"inn","outer":"spouter"}`,
    +		"moby/templates/test5": "",
     	}
     
     	for name, data := range expect {
    @@ -200,6 +203,42 @@ func TestRenderInternals(t *testing.T) {
     	}
     }
     
    +func TestRenderWIthDNS(t *testing.T) {
    +	c := &chart.Chart{
    +		Metadata: &chart.Metadata{
    +			Name:    "moby",
    +			Version: "1.2.3",
    +		},
    +		Templates: []*chart.File{
    +			{Name: "templates/test1", Data: []byte("{{getHostByName \"helm.sh\"}}")},
    +		},
    +		Values: map[string]interface{}{},
    +	}
    +
    +	vals := map[string]interface{}{
    +		"Values": map[string]interface{}{},
    +	}
    +
    +	v, err := chartutil.CoalesceValues(c, vals)
    +	if err != nil {
    +		t.Fatalf("Failed to coalesce values: %s", err)
    +	}
    +
    +	var e Engine
    +	e.EnableDNS = true
    +	out, err := e.Render(c, v)
    +	if err != nil {
    +		t.Errorf("Failed to render templates: %s", err)
    +	}
    +
    +	for _, val := range c.Templates {
    +		fp := path.Join("moby", val.Name)
    +		if out[fp] == "" {
    +			t.Errorf("Expected IP address, got %q", out[fp])
    +		}
    +	}
    +}
    +
     func TestParallelRenderInternals(t *testing.T) {
     	// Make sure that we can use one Engine to run parallel template renders.
     	e := new(Engine)
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.