git.iko.soy/typeable/oauth2-proxy
on master help sign in
Feature - Add env variable support for alpha struct (#2375)

* added envsubstring package and added simple test cases.imple tests. * added documentation * added changelog entry * added documentation to wrong file . * changed tests to ginkgo format * update project to use better maintained library * use defer to clear test variable after tests finished * updated docs for the new package documentation and fixed bad english * refactored function to "reduce" complexity. * updated changelog for new version updated readme * minor formatting --------- Co-authored-by: Haydn Evans <h.evans@douglas.de> Co-authored-by: Joel Speed <Joel.speed@hotmail.co.uk>

commit2f3c811e6aa0620ae47063159791d06811755dd6 authorJan Larwig <jan@larwig.com> dateSat 20 Jan 2024, 20:37 (3 years ago) parentee3e9b8
7 files changed+79−13
CHANGELOG.md+1 docs/docs/configuration/alpha_config.md+14 docs/docs/configuration/alpha_config.md.tmpl+14 go.mod+1 go.sum+2 pkg/apis/options/load.go+25−12 pkg/apis/options/load_test.go+22−1
CHANGELOG.md+1
@@ -28,6 +28,7 @@
2828- [#2295](https://github.com/oauth2-proxy/oauth2-proxy/pull/2295) Change base-image to [GoogleContainerTools/distroless](https://github.com/GoogleContainerTools/distroless) (@kvanzuijlen)
2929- [#2356](https://github.com/oauth2-proxy/oauth2-proxy/pull/2356) Update go-jose dependency (@dasvh)
3030- [#2357](https://github.com/oauth2-proxy/oauth2-proxy/pull/2357) Update ojg to latest release (@bitfehler)
31+- [#1922](https://github.com/oauth2-proxy/oauth2-proxy/pull/1922) Added support for env variables in the alpha struct (@hevans-dglcom)
3132
3233# V7.5.1
3334
docs/docs/configuration/alpha_config.md+14
@@ -67,6 +67,20 @@ the new config.
6767oauth2-proxy --alpha-config ./path/to/new/config.yaml --config ./path/to/existing/config.cfg
6868```
6969
70+## Using ENV variables in the alpha configuration
71+
72+The alpha package supports the use of environment variables in place of yaml keys, allowing sensitive values to be pulled from somewhere other than the yaml file.
73+When using environment variables, your yaml will look like this:
74+
75+```yaml
76+ providers:
77+ - provider: azure
78+ clientSecret: ${CLIENT_SECRET}
79+ ...
80+```
81+Where CLIENT_SECRET is an environment variable.
82+More information and available patterns can be found [here](https://github.com/a8m/envsubst#docs)
83+
7084## Removed options
7185
7286The following flags/options and their respective environment variables are no
docs/docs/configuration/alpha_config.md.tmpl+14
@@ -67,6 +67,20 @@ the new config.
6767oauth2-proxy --alpha-config ./path/to/new/config.yaml --config ./path/to/existing/config.cfg
6868```
6969
70+## Using ENV variables in the alpha configuration
71+
72+The alpha package supports the use of environment variables in place of yaml keys, allowing sensitive values to be pulled from somewhere other than the yaml file.
73+When using environment variables, your yaml will look like this:
74+
75+```yaml
76+ providers:
77+ - provider: azure
78+ clientSecret: ${CLIENT_SECRET}
79+ ...
80+```
81+Where CLIENT_SECRET is an environment variable.
82+More information and available patterns can be found [here](https://github.com/a8m/envsubst#docs)
83+
7084## Removed options
7185
7286The following flags/options and their respective environment variables are no
go.mod+1
@@ -5,6 +5,7 @@ go 1.19
55require (
66 cloud.google.com/go/compute/metadata v0.2.3
77 github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb
8+ github.com/a8m/envsubst v1.4.2
89 github.com/alicebob/miniredis/v2 v2.23.0
910 github.com/benbjohnson/clock v1.3.0
1011 github.com/bitly/go-simplejson v0.5.1
go.sum+2
@@ -47,6 +47,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
4747github.com/FZambia/sentinel v1.0.0 h1:KJ0ryjKTZk5WMp0dXvSdNqp3lFaW1fNFuEYfrkLOYIc=
4848github.com/FZambia/sentinel v1.0.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI=
4949github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
50+github.com/a8m/envsubst v1.4.2 h1:4yWIHXOLEJHQEFd4UjrWDrYeYlV7ncFWJOCBRLOZHQg=
51+github.com/a8m/envsubst v1.4.2/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGtlsbY=
5052github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
5153github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
5254github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
pkg/apis/options/load.go+25−12
@@ -7,6 +7,7 @@ import (
77 "reflect"
88 "strings"
99
10+ "github.com/a8m/envsubst"
1011 "github.com/ghodss/yaml"
1112 "github.com/mitchellh/mapstructure"
1213 "github.com/spf13/pflag"
@@ -140,25 +141,37 @@ func isUnexported(name string) bool {
140141
141142// LoadYAML will load a YAML based configuration file into the options interface provided.
142143func LoadYAML(configFileName string, into interface{}) error {
143− v := viper.New()
144− v.SetConfigFile(configFileName)
145− v.SetConfigType("yaml")
146− v.SetTypeByDefaultValue(true)
144+ buffer, err := loadAndParseYaml(configFileName)
145+ if err != nil {
146+ return err
147+ }
148+
149+ // UnmarshalStrict will return an error if the config includes options that are
150+ // not mapped to fields of the into struct
151+ if err := yaml.UnmarshalStrict(buffer, into, yaml.DisallowUnknownFields); err != nil {
152+ return fmt.Errorf("error unmarshalling config: %w", err)
153+ }
147154
155+ return nil
156+}
157+
158+// Performs the heavy lifting of the LoadYaml function
159+func loadAndParseYaml(configFileName string) ([]byte, error) {
148160 if configFileName == "" {
149− return errors.New("no configuration file provided")
161+ return nil, errors.New("no configuration file provided")
150162 }
151163
152− data, err := os.ReadFile(configFileName)
164+ unparsedBuffer, err := os.ReadFile(configFileName)
153165 if err != nil {
154− return fmt.Errorf("unable to load config file: %w", err)
166+ return nil, fmt.Errorf("unable to load config file: %w", err)
155167 }
156168
157− // UnmarshalStrict will return an error if the config includes options that are
158− // not mapped to felds of the into struct
159− if err := yaml.UnmarshalStrict(data, into, yaml.DisallowUnknownFields); err != nil {
160− return fmt.Errorf("error unmarshalling config: %w", err)
169+ // We now parse over the yaml with env substring, and fill in the ENV's
170+ buffer, err := envsubst.Bytes(unparsedBuffer)
171+ if err != nil {
172+ return nil, fmt.Errorf("error in substituting env variables : %w", err)
161173 }
162174
163− return nil
175+ return buffer, nil
176+
164177}
pkg/apis/options/load_test.go+22−1
@@ -386,8 +386,13 @@ sub:
386386
387387 DescribeTable("LoadYAML",
388388 func(in loadYAMLTableInput) {
389− var configFileName string
389+ // Set the required environment variables before running the test
390+ os.Setenv("TESTUSER", "Alice")
391+
392+ // Unset the environment variables after running the test
393+ defer os.Unsetenv("TESTUSER")
390394
395+ var configFileName string
391396 if in.configFile != nil {
392397 By("Creating a config file")
393398 configFile, err := os.CreateTemp("", "oauth2-proxy-test-config-file")
@@ -407,12 +412,14 @@ sub:
407412 } else {
408413 input = &TestOptions{}
409414 }
415+
410416 err := LoadYAML(configFileName, input)
411417 if in.expectedErr != nil {
412418 Expect(err).To(MatchError(in.expectedErr.Error()))
413419 } else {
414420 Expect(err).ToNot(HaveOccurred())
415421 }
422+
416423 Expect(input).To(Equal(in.expectedOutput))
417424 },
418425 Entry("with a valid input", loadYAMLTableInput{
@@ -466,6 +473,20 @@ sub:
466473 expectedOutput: &TestOptions{},
467474 expectedErr: errors.New("error unmarshalling config: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go struct field TestOptions.StringSliceOption of type []string"),
468475 }),
476+ Entry("with a config file containing environment variable references", loadYAMLTableInput{
477+ configFile: []byte("stringOption: ${TESTUSER}"),
478+ input: &TestOptions{},
479+ expectedOutput: &TestOptions{
480+ StringOption: "Alice",
481+ },
482+ }),
483+ Entry("with a config file containing env variable references, with a fallback value", loadYAMLTableInput{
484+ configFile: []byte("stringOption: ${TESTUSER2=Bob}"),
485+ input: &TestOptions{},
486+ expectedOutput: &TestOptions{
487+ StringOption: "Bob",
488+ },
489+ }),
469490 )
470491 })
471492