-
Notifications
You must be signed in to change notification settings - Fork 208
Add validation to helm values file path to prevent potential dirtrav vulnerability #3726
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
Changes from all commits
6bf370b
a6d49a3
56cec2e
8d53e6e
41fcec9
955829b
447572e
9a013fa
4b9cf96
fa9737f
f4ed5f0
e296c2b
a771d3e
bdc1a25
f1f978d
b17e2c0
4b2f23a
492019e
d7d47dd
582f83e
3ac6d69
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 |
|---|---|---|
|
|
@@ -18,6 +18,7 @@ import ( | |
| "bytes" | ||
| "context" | ||
| "fmt" | ||
| "net/url" | ||
| "os" | ||
| "os/exec" | ||
| "path/filepath" | ||
|
|
@@ -30,6 +31,10 @@ import ( | |
| "github.com/pipe-cd/pipecd/pkg/config" | ||
| ) | ||
|
|
||
| var ( | ||
| allowedURLSchemes = []string{"http", "https"} | ||
| ) | ||
|
|
||
| type Helm struct { | ||
| version string | ||
| execPath string | ||
|
|
@@ -63,6 +68,10 @@ func (c *Helm) TemplateLocalChart(ctx context.Context, appName, appDir, namespac | |
|
|
||
| if opts != nil { | ||
| for _, v := range opts.ValueFiles { | ||
| if err := verifyHelmValueFilePath(appDir, v); err != nil { | ||
| c.logger.Error("failed to verify values file path", zap.Error(err)) | ||
| return "", err | ||
| } | ||
| args = append(args, "-f", v) | ||
| } | ||
| for k, v := range opts.SetFiles { | ||
|
|
@@ -99,7 +108,7 @@ type helmRemoteGitChart struct { | |
| } | ||
|
|
||
| func (c *Helm) TemplateRemoteGitChart(ctx context.Context, appName, appDir, namespace string, chart helmRemoteGitChart, gitClient gitClient, opts *config.InputHelmOptions) (string, error) { | ||
| // Firstly, we need to download the remote repositoy. | ||
| // Firstly, we need to download the remote repository. | ||
| repoDir, err := os.MkdirTemp("", "helm-remote-chart") | ||
| if err != nil { | ||
| return "", fmt.Errorf("unable to created temporary directory for storing remote helm chart: %w", err) | ||
|
|
@@ -153,6 +162,10 @@ func (c *Helm) TemplateRemoteChart(ctx context.Context, appName, appDir, namespa | |
|
|
||
| if opts != nil { | ||
| for _, v := range opts.ValueFiles { | ||
| if err := verifyHelmValueFilePath(appDir, v); err != nil { | ||
| c.logger.Error("failed to verify values file path", zap.Error(err)) | ||
| return "", err | ||
| } | ||
| args = append(args, "-f", v) | ||
| } | ||
| for k, v := range opts.SetFiles { | ||
|
|
@@ -199,3 +212,62 @@ func (c *Helm) TemplateRemoteChart(ctx context.Context, appName, appDir, namespa | |
| } | ||
| return executor() | ||
| } | ||
|
|
||
| // verifyHelmValueFilePath verifies if the path of the values file references | ||
| // a remote URL or inside the path where the application configuration file (i.e. *.pipecd.yaml) is located. | ||
| func verifyHelmValueFilePath(appDir, valueFilePath string) error { | ||
| url, err := url.Parse(valueFilePath) | ||
| if err == nil && url.Scheme != "" { | ||
| for _, s := range allowedURLSchemes { | ||
|
Member
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. was it a miss or did we really think about allowing the remote valueFiles? I feel a bit unsafe to allow remote file value, wdyt?
Contributor
Author
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. Since our concern here is the unauthorized rendering of unrelated sensitive information on the host on which Piped is running, remote files are outside the scope of this PR's concern, I think. However, there may be such a case: SSRF, i.e., when a server accessible only from a host running Piped holds the confidential values file and attackers tries to render it (e.g.
Contributor
Author
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. To prevent this, it may be necessary to specify an allowlist of URLs from which the values file can be read.
Contributor
Author
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. This concept follows the advisory of GHSA-63qx-x74g-jcr7.
Member
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. I understand that this is out of context on this PR 👍 My concern is currently, do we allow the remote valueFiles that way? and if it's yes, then the docs we updated may need to be updated as well.
Member
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. okay, lets go with that option 👍
Contributor
Author
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. updated it 👍
Member
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. @Szarny
Contributor
Author
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. @nghialv Yes, please see this pr: helm/helm#2769
Member
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. Thanks for teaching me. Didn't know about that before. 👍 |
||
| if strings.EqualFold(url.Scheme, s) { | ||
| return nil | ||
| } | ||
| } | ||
|
|
||
| return fmt.Errorf("scheme %s is not allowed to load values file", url.Scheme) | ||
| } | ||
|
|
||
| // valueFilePath is a path where non-default Helm values file is located. | ||
| if !filepath.IsAbs(valueFilePath) { | ||
| valueFilePath = filepath.Join(appDir, valueFilePath) | ||
| } | ||
|
|
||
| if isSymlink(valueFilePath) { | ||
| if valueFilePath, err = resolveSymlinkToAbsPath(valueFilePath, appDir); err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| // If a path outside of appDir is specified as the path for the values file, | ||
| // it may indicate that someone trying to illegally read a file as values file that | ||
| // exists in the environment where Piped is running. | ||
| if !strings.HasPrefix(valueFilePath, appDir) { | ||
| return fmt.Errorf("values file %s references outside the application configuration directory", valueFilePath) | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // isSymlink returns the path is whether symbolic link or not. | ||
| func isSymlink(path string) bool { | ||
| lstat, err := os.Lstat(path) | ||
| if err != nil { | ||
| return false | ||
| } | ||
|
|
||
| return lstat.Mode()&os.ModeSymlink == os.ModeSymlink | ||
| } | ||
|
|
||
| // resolveSymlinkToAbsPath resolves symbolic link to an absolute path. | ||
| func resolveSymlinkToAbsPath(path, absParentDir string) (string, error) { | ||
| resolved, err := os.Readlink(path) | ||
| if err != nil { | ||
| return "", err | ||
| } | ||
|
|
||
| if !filepath.IsAbs(resolved) { | ||
| resolved = filepath.Join(absParentDir, resolved) | ||
| } | ||
|
|
||
| return resolved, nil | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| /etc/hosts |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| dir/values.yaml |
Uh oh!
There was an error while loading. Please reload this page.