Skip to content
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

feat(file) implement env var substitution for state files #286

Merged
merged 2 commits into from
Apr 15, 2021
Merged

Conversation

hbagdi
Copy link
Member

@hbagdi hbagdi commented Mar 7, 2021

Introducing support for envirment variable substitution in state files.
This is primarily intended for injecting sensitive data at runtime.

Fix #191

@hbagdi hbagdi requested a review from a team as a code owner March 7, 2021 19:59
@hbagdi hbagdi requested a review from cwurm March 7, 2021 19:59
Copy link
Contributor

@mflendrich mflendrich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be a security vulnerability (used to exfiltrate environment variables if the deck file is controlled by an actor interested in host's environment variables).

Let's discuss how to mitigate this risk. My first guess is that restricting available env vars e.g. to those sharing a predefined prefix (e.g. DECK_ENV_) might solve it.

@stale
Copy link

stale bot commented Mar 22, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale Will be closed unless advocated for within 7 days label Mar 22, 2021
@stale stale bot closed this Mar 29, 2021
@hbagdi hbagdi reopened this Mar 29, 2021
@stale stale bot removed the stale Will be closed unless advocated for within 7 days label Mar 29, 2021
@hbagdi
Copy link
Member Author

hbagdi commented Apr 1, 2021

There are two ways to achieve this:

  • Ask user to include DECK_ prefix to all env vars in the state file.
  • Implicitly append (and document accordingly) DECK_ prefix to any invocation, eg: ${{ env "FOO" }} will be substituted with the value of DECK_FOO.

Which one do you prefer?

@mflendrich
Copy link
Contributor

@hbagdi

Which one do you prefer?

I find the former less "magical" and less confusing. The latter approach seems acceptable too, if it gets documented well enough.

@rainest
Copy link
Contributor

rainest commented Apr 7, 2021

I'm also in favor of the first option, where users must include the DECK_ prefix on variables in the state file. Using the same variable name in both the state file and environment feels simplest, and users need to be aware of the requirement either way to set the properly-named variables in the environment.

@hbagdi
Copy link
Member Author

hbagdi commented Apr 8, 2021

@mflendrich @rainest Perfect. I get an odd sense of happiness we all agree AND use the exact same argument, however small the issue maybe.
I've updated the patch to match our discussion.
I'm not sure what is up with dependabot.

file/readfile.go Outdated
Comment on lines 147 to 153
"env": func(key string) (string, error) {
if !strings.HasPrefix(key, envVarPrefix) {
return "", fmt.Errorf("environment variables in the state file must "+
"be prefixed with 'DECK_', found: '%s'", key)
}
return os.Getenv(key), nil
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For readability and maintainability, please extract the lambda function into a named function, for example func GetPrefixedEnvVar

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

file/readfile.go Outdated
@@ -132,3 +140,26 @@ func configFilesInDir(dir string) ([]string, error) {
}
return res, nil
}

func processEnvs(content string) (string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is in fact a gotemplate rendering mapper. It happens to have env as its defined function, but may include other ones in the future.

I recommend that we redefine the responsibility of this function from "fill in env vars" to "render the deck file template" and rename it accordingly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

file/readfile.go Outdated
if err != nil {
return nil, fmt.Errorf("parsing file: %w", err)
}
bytes = []byte(processedContent)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't reuse the same []byte for input and output of template rendering. From a strongly typed perspective, these two values represent different data types (unrendered template versus rendered document)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get the point somewhat but not enough.
Why does it really matter? Or maybe when?

I do agree that this way of thinking is correct, I just don't completely grasp it so please help me improve my understanding here.

And since I agree, I have already updated the patch,

file/readfile.go Outdated
@@ -2,6 +2,9 @@ package file

import (
"bufio"
"bytes"
"fmt"
"html/template"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably meant text/template. html/template will perform additional escaping which I think is undesirable (will break valid YAML) here.

Please add a test ensuring that the template renderer doesn't escape yaml characters like & or <.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@hbagdi
Copy link
Member Author

hbagdi commented Apr 12, 2021

@mflendrich @rainest Friendly ping to review this when you get a chance.

Copy link
Contributor

@rainest rainest left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using < in tags to check that we're not garbling YAML seems a bit off since it's not actually a valid tag, but ultimately that doesn't cause issues in unit tests.

I found one UX gotcha during practical testing: unset variables in the template will (probably, depending on the field type) result in an invalid state, and the resulting error doesn't make the issue cause clear. For example, attempting to apply file/testdata/file.yaml without setting DECK_SVC2_HOST results in:

Error: reading file: validating file content: 1 errors occurred:
	services.0.host: Invalid type. Expected: string, given: null

With the attached change:

Error: reading file: parsing file: template: state:3:12: executing "state" at <env "DECK_SVC2_HOST">: error calling env: environment variable 'DECK_SVC2_HOST' present in state file but not set

While the complete error unwrap looks a bit silly, it's at least clearer than the type error that you'll hit later.

Originally attempted to use PR suggestions, and then realized that works horribly when you need to include tabs, so git apply /tmp/lookup.diff.txt: lookup.diff.txt

@hbagdi
Copy link
Member Author

hbagdi commented Apr 14, 2021

I found one UX gotcha during practical testing: unset variables in the template will (probably, depending on the field type) result in an invalid state, and the resulting error doesn't make the issue cause clear.

Fair. What if we error out early whenever we encounter an unset environment variable?

Introducing support for envirment variable substitution in state files.
This is primarily intended for injecting sensitive data at runtime.

Fix #191
@hbagdi
Copy link
Member Author

hbagdi commented Apr 14, 2021

@rainest I updated the code to include your patch and also added a test case for it. PTAL.

ping @mflendrich for another review.

file/testdata/file.yaml Outdated Show resolved Hide resolved
Co-authored-by: Michał Flendrich <michal@flendrich.pro>
@hbagdi hbagdi merged commit 6ede1a8 into main Apr 15, 2021
@hbagdi hbagdi deleted the feat/read-env branch April 15, 2021 15:15
rainest pushed a commit that referenced this pull request Apr 21, 2021
Introducing support for environment variable substitution in state files.
This is primarily intended for injecting sensitive data at runtime.

Fix #191

Co-authored-by: Michał Flendrich <michal@flendrich.pro>
AntoineJac pushed a commit that referenced this pull request Jan 23, 2024
Introducing support for environment variable substitution in state files.
This is primarily intended for injecting sensitive data at runtime.

Fix #191

Co-authored-by: Michał Flendrich <michal@flendrich.pro>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Deck support placeholder replace
3 participants