git.iko.soy/typeable/oauth2-proxy
on master help sign in
feat: add --config-test flag for validating configuration (#3338)

* feat: add --config-test flag for validating configuration without starting the proxy Signed-off-by: MayorFaj <mayorfaj@gmail.com> * doc: fix alpha config and add changelog entry for #3338 Signed-off-by: Jan Larwig <jan@larwig.com> --------- Signed-off-by: MayorFaj <mayorfaj@gmail.com> Signed-off-by: Jan Larwig <jan@larwig.com> Co-authored-by: Jan Larwig <jan@larwig.com>

commit51ecc50372c42299749dafb225dee42df3520755 authorMayowa Fajobi <127399119+MayorFaj@users.noreply.github.com> dateWed 18 Mar 2026, 15:00 (7 months ago) parentfe5c6be
7 files changed+158−4
.golangci.yml+5 CHANGELOG.md+1 docs/docs/configuration/alpha_config.md+11 docs/docs/configuration/alpha_config.md.tmpl+11 docs/docs/configuration/overview.md+41−4 main.go+14 main_test.go+75
.golangci.yml+5
@@ -53,6 +53,11 @@ linters:
5353 - revive
5454 path: util/.*\.go$
5555 text: "var-naming: avoid meaningless package names"
56+ # pkg/version conflicts with go/version (added in Go 1.22)
57+ - linters:
58+ - revive
59+ path: pkg/version/.*\.go$
60+ text: "var-naming: avoid package names that conflict with"
5661 - linters:
5762 - prealloc
5863 path: _test\.go
CHANGELOG.md+1
@@ -15,6 +15,7 @@
1515- [#2851](https://github.com/oauth2-proxy/oauth2-proxy/pull/2851) feat: add support for specifying allowed OIDC JWT signing algorithms (#2753) (@andoks / @tuunit)
1616- [#3369](https://github.com/oauth2-proxy/oauth2-proxy/pull/3369) fix: use CSRFExpire instead of Expire for CSRF cookie validation (@Br1an67)
1717- [#3365](https://github.com/oauth2-proxy/oauth2-proxy/pull/3365) fix: filter empty strings from allowed groups (@Br1an67)
18+- [#3338](https://github.com/oauth2-proxy/oauth2-proxy/pull/3338) feat: add --config-test flag for validating configuration (@MayorFaj)
1819
1920# V7.14.3
2021
docs/docs/configuration/alpha_config.md+11
@@ -106,6 +106,17 @@ the new config.
106106oauth2-proxy --alpha-config ./path/to/new/config.yaml --config ./path/to/existing/config.cfg
107107```
108108
109+### Validating Alpha Configuration
110+
111+Use `--config-test` to validate your alpha configuration without starting the proxy:
112+
113+```bash
114+oauth2-proxy --config core.cfg --alpha-config alpha.yaml --config-test
115+```
116+
117+This is useful for CI/CD pipelines to catch configuration errors before deployment.
118+See the [Configuration Validation](./overview.md#configuration-validation) section for more details.
119+
109120### How to use environment variables
110121
111122The alpha package supports the use of environment variables in place of yaml values, allowing sensitive data to be pulled from somewhere other than the yaml file.
docs/docs/configuration/alpha_config.md.tmpl+11
@@ -106,6 +106,17 @@ the new config.
106106oauth2-proxy --alpha-config ./path/to/new/config.yaml --config ./path/to/existing/config.cfg
107107```
108108
109+### Validating Alpha Configuration
110+
111+Use `--config-test` to validate your alpha configuration without starting the proxy:
112+
113+```bash
114+oauth2-proxy --config core.cfg --alpha-config alpha.yaml --config-test
115+```
116+
117+This is useful for CI/CD pipelines to catch configuration errors before deployment.
118+See the [Configuration Validation](./overview.md#configuration-validation) section for more details.
119+
109120### How to use environment variables
110121
111122The alpha package supports the use of environment variables in place of yaml values, allowing sensitive data to be pulled from somewhere other than the yaml file.
docs/docs/configuration/overview.md+41−4
@@ -66,11 +66,48 @@ An example [oauth2-proxy.cfg](https://github.com/oauth2-proxy/oauth2-proxy/blob/
6666
6767### Command Line Options
6868
69−| Flag | Description |
70−| ----------- | -------------------- |
71−| `--config` | path to config file |
72−| `--version` | print version string |
69+| Flag | Description |
70+| ---------------- | ------------------------------------------------------- |
71+| `--config` | path to config file |
72+| `--config-test` | test configuration and exit (for CI/CD validation) |
73+| `--version` | print version string |
7374
75+## Configuration Validation
76+
77+The `--config-test` flag validates your configuration file without starting the proxy server. This is useful for:
78+- **CI/CD pipelines**: Pre-deployment validation
79+- **Configuration management**: Testing before applying changes
80+- **Debugging**: Verifying syntax and required fields
81+
82+### Usage
83+
84+```bash
85+# Test legacy config
86+oauth2-proxy --config /etc/oauth2-proxy.cfg --config-test
87+
88+# Test alpha config
89+oauth2-proxy --config /etc/core.cfg --alpha-config /etc/alpha.yaml --config-test
90+
91+# CI/CD pre-deployment check
92+# Returns with exit code 1 if any validation errors occur
93+oauth2-proxy --config new-config.cfg --config-test
94+```
95+
96+### Exit Codes
97+
98+- **0**: Configuration is valid ✅
99+- **1**: Configuration is invalid (errors printed to stderr) ❌
100+
101+### Validation Coverage
102+
103+The `--config-test` flag performs the **same comprehensive validation** as normal startup, including:
104+- Required fields (client ID, client secret, cookie secret, etc.)
105+- Syntax validation (TOML/YAML parsing)
106+- Provider configuration
107+- Upstream server definitions
108+- Session store connectivity (e.g., Redis network checks if configured)
109+
110+**Note**: Cannot be combined with `--convert-config-to-alpha`.
74111
75112### General Provider Options
76113
main.go+14
@@ -26,6 +26,7 @@ func main() {
2626 alphaConfig := configFlagSet.String("alpha-config", "", "path to alpha config file (use at your own risk - the structure in this config file may change between minor releases)")
2727 convertConfig := configFlagSet.Bool("convert-config-to-alpha", false, "if true, the proxy will load configuration as normal and convert existing configuration to the alpha config structure, and print it to stdout")
2828 showVersion := configFlagSet.Bool("version", false, "print version string")
29+ configTest := configFlagSet.Bool("config-test", false, "test the configuration and exit")
2930 configFlagSet.Parse(os.Args[1:])
3031
3132 if *showVersion {
@@ -37,11 +38,24 @@ func main() {
3738 logger.Fatal("cannot use alpha-config and convert-config-to-alpha together")
3839 }
3940
41+ if *configTest && *convertConfig {
42+ logger.Fatal("cannot use config-test and convert-config-to-alpha together")
43+ }
44+
4045 opts, err := loadConfiguration(*config, *alphaConfig, configFlagSet, os.Args[1:])
4146 if err != nil {
4247 logger.Fatalf("ERROR: %v", err)
4348 }
4449
50+ if *configTest {
51+ if err = validation.Validate(opts); err != nil {
52+ logger.Errorf("%s", err)
53+ os.Exit(1)
54+ }
55+ fmt.Println("configuration is valid")
56+ return
57+ }
58+
4559 if *convertConfig {
4660 if err := printConvertedConfig(opts); err != nil {
4761 logger.Fatalf("ERROR: could not convert config: %v", err)
main_test.go+75
@@ -7,6 +7,7 @@ import (
77 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
88 . "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options/testutil"
99 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util/ptr"
10+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/validation"
1011 . "github.com/onsi/ginkgo/v2"
1112 . "github.com/onsi/gomega"
1213 "github.com/onsi/gomega/format"
@@ -292,4 +293,78 @@ redirect_url="http://localhost:4180/oauth2/callback"
292293 expectedErr: errors.New("failed to load legacy options: failed to load config: error unmarshalling config: decoding failed due to the following error(s):\n\n'' has invalid keys: unknown_field"),
293294 }),
294295 )
296+
297+ Describe("Config Test Mode", func() {
298+ const validConfig = `
299+http_address="127.0.0.1:4180"
300+upstreams="http://httpbin"
301+client_id="oauth2-proxy"
302+client_secret="b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK"
303+cookie_secret="OQINaROshtE9TcZkNAm-5Zs2Pv3xaWytBmc5W7sPX7w="
304+email_domains="example.com"
305+cookie_secure="false"
306+redirect_url="http://localhost:4180/oauth2/callback"
307+`
308+
309+ const invalidConfig = `
310+http_address="127.0.0.1:4180"
311+upstreams="http://httpbin"
312+email_domains="example.com"
313+cookie_secure="false"
314+redirect_url="http://localhost:4180/oauth2/callback"
315+`
316+
317+ writeTempConfig := func(content string) string {
318+ file, err := os.CreateTemp("", "oauth2-proxy-test-config-XXXX.cfg")
319+ Expect(err).ToNot(HaveOccurred())
320+ defer file.Close()
321+
322+ _, err = file.WriteString(content)
323+ Expect(err).ToNot(HaveOccurred())
324+ return file.Name()
325+ }
326+
327+ It("should pass validation with a valid configuration", func() {
328+ configFile := writeTempConfig(validConfig)
329+ defer os.Remove(configFile)
330+
331+ flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
332+ opts, err := loadConfiguration(configFile, "", flagSet, []string{})
333+ Expect(err).ToNot(HaveOccurred())
334+
335+ err = validation.Validate(opts)
336+ Expect(err).ToNot(HaveOccurred())
337+ })
338+
339+ It("should fail validation with an invalid configuration (missing required fields)", func() {
340+ configFile := writeTempConfig(invalidConfig)
341+ defer os.Remove(configFile)
342+
343+ flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
344+ opts, err := loadConfiguration(configFile, "", flagSet, []string{})
345+ Expect(err).ToNot(HaveOccurred())
346+
347+ err = validation.Validate(opts)
348+ Expect(err).To(HaveOccurred())
349+ Expect(err.Error()).To(ContainSubstring("invalid configuration"))
350+ })
351+
352+ It("should fail to load a configuration file with syntax errors", func() {
353+ configFile := writeTempConfig("this is not valid toml ===")
354+ defer os.Remove(configFile)
355+
356+ flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
357+ _, err := loadConfiguration(configFile, "", flagSet, []string{})
358+ Expect(err).To(HaveOccurred())
359+ })
360+
361+ It("should register the config-test flag", func() {
362+ flagSet := pflag.NewFlagSet("test", pflag.ContinueOnError)
363+ flagSet.ParseErrorsAllowlist.UnknownFlags = true
364+ configTest := flagSet.Bool("config-test", false, "test the configuration and exit")
365+ err := flagSet.Parse([]string{"--config-test"})
366+ Expect(err).ToNot(HaveOccurred())
367+ Expect(*configTest).To(BeTrue())
368+ })
369+ })
295370})