git.iko.soy/typeable/oauth2-proxy
on master help sign in
fix merge problems and test cases
48bd2d7Jan Larwig
main.go+1−1
@@ -70,7 +70,7 @@ func main() {
70func loadConfiguration(config, yamlConfig string, extraFlags *pflag.FlagSet, args []string) (*options.Options, error) {
70func loadConfiguration(config, yamlConfig string, extraFlags *pflag.FlagSet, args []string) (*options.Options, error) {
71 opts, err := loadLegacyOptions(config, extraFlags, args)
71 opts, err := loadLegacyOptions(config, extraFlags, args)
72 if err != nil {
72 if err != nil {
73− return nil, fmt.Errorf("couldn't load legacy options: %w", err)
73+ return nil, fmt.Errorf("failed to load legacy options: %w", err)
74 }
74 }
75
75
76 if yamlConfig != "" {
76 if yamlConfig != "" {
main_test.go+8−8
@@ -22,7 +22,7 @@ var _ = Describe("Configuration Loading Suite", func() {
22http_address="127.0.0.1:4180"
22http_address="127.0.0.1:4180"
23upstreams="http://httpbin"
23upstreams="http://httpbin"
24set_basic_auth="true"
24set_basic_auth="true"
25−basic_auth_password="super-secret-password"
25+basic_auth_password="c3VwZXItc2VjcmV0LXBhc3N3b3Jk"
26client_id="oauth2-proxy"
26client_id="oauth2-proxy"
27client_secret="b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK"
27client_secret="b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK"
28`
28`
@@ -45,7 +45,7 @@ injectRequestHeaders:
45 claim: user
45 claim: user
46 prefix: "Basic "
46 prefix: "Basic "
47 basicAuthPassword:
47 basicAuthPassword:
48− value: super-secret-password
48+ value: c3VwZXItc2VjcmV0LXBhc3N3b3Jk
49- name: X-Forwarded-Groups
49- name: X-Forwarded-Groups
50 values:
50 values:
51 - claimSource:
51 - claimSource:
@@ -69,12 +69,12 @@ injectResponseHeaders:
69 claim: user
69 claim: user
70 prefix: "Basic "
70 prefix: "Basic "
71 basicAuthPassword:
71 basicAuthPassword:
72− value: super-secret-password
72+ value: c3VwZXItc2VjcmV0LXBhc3N3b3Jk
73server:
73server:
74 bindAddress: "127.0.0.1:4180"
74 bindAddress: "127.0.0.1:4180"
75providers:
75providers:
76−- provider: google
77− ID: google=oauth2-proxy
76+- id: google=oauth2-proxy
77+ provider: google
78 clientSecret: b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK
78 clientSecret: b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK
79 clientID: oauth2-proxy
79 clientID: oauth2-proxy
80 azureConfig:
80 azureConfig:
@@ -139,7 +139,7 @@ redirect_url="http://localhost:4180/oauth2/callback"
139 Claim: "user",
139 Claim: "user",
140 Prefix: "Basic ",
140 Prefix: "Basic ",
141 BasicAuthPassword: &options.SecretSource{
141 BasicAuthPassword: &options.SecretSource{
142− Value: []byte("super-secret-password"),
142+ Value: []byte("c3VwZXItc2VjcmV0LXBhc3N3b3Jk"),
143 },
143 },
144 },
144 },
145 },
145 },
@@ -248,7 +248,7 @@ redirect_url="http://localhost:4180/oauth2/callback"
248 Entry("with bad legacy configuration", loadConfigurationTableInput{
248 Entry("with bad legacy configuration", loadConfigurationTableInput{
249 configContent: testCoreConfig + "unknown_field=\"something\"",
249 configContent: testCoreConfig + "unknown_field=\"something\"",
250 expectedOptions: func() *options.Options { return nil },
250 expectedOptions: func() *options.Options { return nil },
251− expectedErr: errors.New("failed to load config: error unmarshalling config: decoding failed due to the following error(s):\n\n'' has invalid keys: unknown_field"),
251+ 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"),
252 }),
252 }),
253 Entry("with bad alpha configuration", loadConfigurationTableInput{
253 Entry("with bad alpha configuration", loadConfigurationTableInput{
254 configContent: testCoreConfig,
254 configContent: testCoreConfig,
@@ -260,7 +260,7 @@ redirect_url="http://localhost:4180/oauth2/callback"
260 configContent: testCoreConfig + "unknown_field=\"something\"",
260 configContent: testCoreConfig + "unknown_field=\"something\"",
261 alphaConfigContent: testAlphaConfig,
261 alphaConfigContent: testAlphaConfig,
262 expectedOptions: func() *options.Options { return nil },
262 expectedOptions: func() *options.Options { return nil },
263− expectedErr: errors.New("failed to load core options: failed to load config: error unmarshalling config: decoding failed due to the following error(s):\n\n'' has invalid keys: unknown_field"),
263+ 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"),
264 }),
264 }),
265 )
265 )
266})
266})
pkg/apis/options/header.go+1−1
@@ -11,7 +11,7 @@ type Header struct {
11 // should be preserved for the request to the upstream server.
11 // should be preserved for the request to the upstream server.
12 // This option only applies to injected request headers.
12 // This option only applies to injected request headers.
13 // Defaults to false (headers that match this header will be stripped).
13 // Defaults to false (headers that match this header will be stripped).
14− PreserveRequestValue bool `yaml:"preserveRequestValue,omitempty"`
14+ PreserveRequestValue bool `yaml:"preserveRequestValue"`
15
15
16 // InsecureSkipHeaderNormalization disables normalizing the header name
16 // InsecureSkipHeaderNormalization disables normalizing the header name
17 // According to RFC 7230 Section 3.2 there aren't any rules about
17 // According to RFC 7230 Section 3.2 there aren't any rules about
pkg/apis/options/duration.go → pkg/apis/options/hooks.gorenamed+14
@@ -41,3 +41,17 @@ func toDurationHookFunc() mapstructure.DecodeHookFunc {
41 }
41 }
42 }
42 }
43}
43}
44+
45+// StringToBytesHookFunc returns a DecodeHookFunc that converts string to []byte.
46+func stringToBytesHookFunc() mapstructure.DecodeHookFunc {
47+ return func(
48+ f reflect.Type,
49+ t reflect.Type,
50+ data interface{},
51+ ) (interface{}, error) {
52+ if f.Kind() == reflect.String && t == reflect.TypeOf([]byte{}) {
53+ return []byte(data.(string)), nil
54+ }
55+ return data, nil
56+ }
57+}
pkg/apis/options/duration_test.go → pkg/apis/options/hooks_test.gorenamed+15−1
@@ -5,7 +5,7 @@ import (
5 "time"
5 "time"
6)
6)
7
7
8−func TestDecode(t *testing.T) {
8+func TestToDurationHook(t *testing.T) {
9 type result struct {
9 type result struct {
10 Duration time.Duration `yaml:"duration"`
10 Duration time.Duration `yaml:"duration"`
11 }
11 }
@@ -80,3 +80,17 @@ func TestDecode(t *testing.T) {
80 })
80 })
81 }
81 }
82}
82}
83+
84+func TestStringToBytesHook(t *testing.T) {
85+ var result struct {
86+ Value []byte `yaml:"value"`
87+ }
88+
89+ if err := Decode(map[string]interface{}{"value": "hello-world"}, &result); err != nil {
90+ t.Fatal(err)
91+ }
92+
93+ if string(result.Value) != "hello-world" {
94+ t.Errorf("expected %q, got %q", "hello-world", string(result.Value))
95+ }
96+}
pkg/apis/options/load.go+4−1
@@ -89,7 +89,10 @@ func LoadYAML(configFileName string, opts interface{}) error {
89// - An error if decoding fails or if there are unmapped keys.
89// - An error if decoding fails or if there are unmapped keys.
90func Decode(input interface{}, result interface{}) error {
90func Decode(input interface{}, result interface{}) error {
91 decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
91 decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
92− DecodeHook: mapstructure.ComposeDecodeHookFunc(toDurationHookFunc()),
92+ DecodeHook: mapstructure.ComposeDecodeHookFunc(
93+ toDurationHookFunc(),
94+ stringToBytesHookFunc(),
95+ ),
93 Metadata: nil, // Don't track any metadata
96 Metadata: nil, // Don't track any metadata
94 Result: result, // Decode the result into the prefilled options
97 Result: result, // Decode the result into the prefilled options
95 TagName: "yaml", // Parse all fields that use the json tag
98 TagName: "yaml", // Parse all fields that use the json tag
pkg/apis/options/load_test.go+2−2
@@ -155,7 +155,7 @@ var _ = Describe("Load", func() {
155 }
155 }
156 err := Load(configFileName, flagSet, input)
156 err := Load(configFileName, flagSet, input)
157 if o.expectedErr != nil {
157 if o.expectedErr != nil {
158− Expect(err).To(MatchError(o.expectedErr.Error()))
158+ Expect(err).To(MatchError(ContainSubstring(o.expectedErr.Error())))
159 } else {
159 } else {
160 Expect(err).ToNot(HaveOccurred())
160 Expect(err).ToNot(HaveOccurred())
161 }
161 }
@@ -471,7 +471,7 @@ sub:
471 configFile: []byte(`stringSliceOption: "a"`),
471 configFile: []byte(`stringSliceOption: "a"`),
472 input: &TestOptions{},
472 input: &TestOptions{},
473 expectedOutput: &TestOptions{},
473 expectedOutput: &TestOptions{},
474− expectedErr: errors.New("error unmarshalling config: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go struct field TestOptions.TestOptionSubStruct.StringSliceOption of type []string"),
474+ expectedErr: errors.New("error decoding config: decoding failed due to the following error(s):\n\n'stringSliceOption' source data must be an array or slice, got string"),
475 }),
475 }),
476 Entry("with a config file containing environment variable references", loadYAMLTableInput{
476 Entry("with a config file containing environment variable references", loadYAMLTableInput{
477 configFile: []byte("stringOption: ${TESTUSER}"),
477 configFile: []byte("stringOption: ${TESTUSER}"),
pkg/apis/options/providers.go+6−6
@@ -67,7 +67,7 @@ type Provider struct {
67 CAFiles []string `yaml:"caFiles,omitempty"`
67 CAFiles []string `yaml:"caFiles,omitempty"`
68 // UseSystemTrustStore determines if your custom CA files and the system trust store are used
68 // UseSystemTrustStore determines if your custom CA files and the system trust store are used
69 // If set to true, your custom CA files and the system trust store are used otherwise only your custom CA files.
69 // If set to true, your custom CA files and the system trust store are used otherwise only your custom CA files.
70− UseSystemTrustStore bool `yaml:"useSystemTrustStore,omitempty"`
70+ UseSystemTrustStore bool `yaml:"useSystemTrustStore"`
71 // LoginURL is the authentication endpoint
71 // LoginURL is the authentication endpoint
72 LoginURL string `yaml:"loginURL,omitempty"`
72 LoginURL string `yaml:"loginURL,omitempty"`
73 // LoginURLParameters defines the parameters that can be passed from the start URL to the IdP login URL
73 // LoginURLParameters defines the parameters that can be passed from the start URL to the IdP login URL
@@ -80,7 +80,7 @@ type Provider struct {
80 ProfileURL string `yaml:"profileURL,omitempty"`
80 ProfileURL string `yaml:"profileURL,omitempty"`
81 // SkipClaimsFromProfileURL allows to skip request to Profile URL for resolving claims not present in id_token
81 // SkipClaimsFromProfileURL allows to skip request to Profile URL for resolving claims not present in id_token
82 // default set to 'false'
82 // default set to 'false'
83− SkipClaimsFromProfileURL bool `yaml:"skipClaimsFromProfileURL,omitempty"`
83+ SkipClaimsFromProfileURL bool `yaml:"skipClaimsFromProfileURL"`
84 // ProtectedResource is the resource that is protected (Azure AD and ADFS only)
84 // ProtectedResource is the resource that is protected (Azure AD and ADFS only)
85 ProtectedResource string `yaml:"resource,omitempty"`
85 ProtectedResource string `yaml:"resource,omitempty"`
86 // ValidateURL is the access token validation endpoint
86 // ValidateURL is the access token validation endpoint
@@ -181,13 +181,13 @@ type MicrosoftEntraIDOptions struct {
181
181
182 // FederatedTokenAuth enable oAuth2 client authentication with federated token projected
182 // FederatedTokenAuth enable oAuth2 client authentication with federated token projected
183 // by Entra Workload Identity plugin, instead of client secret.
183 // by Entra Workload Identity plugin, instead of client secret.
184− FederatedTokenAuth bool `yaml:"federatedTokenAuth,omitempty"`
184+ FederatedTokenAuth bool `yaml:"federatedTokenAuth"`
185}
185}
186
186
187type ADFSOptions struct {
187type ADFSOptions struct {
188 // Skip adding the scope parameter in login request
188 // Skip adding the scope parameter in login request
189 // Default value is 'false'
189 // Default value is 'false'
190− SkipScope bool `yaml:"skipScope,omitempty"`
190+ SkipScope bool `yaml:"skipScope"`
191}
191}
192
192
193type BitbucketOptions struct {
193type BitbucketOptions struct {
@@ -227,7 +227,7 @@ type GoogleOptions struct {
227 // ServiceAccountJSON is the path to the service account json credentials
227 // ServiceAccountJSON is the path to the service account json credentials
228 ServiceAccountJSON string `yaml:"serviceAccountJson,omitempty"`
228 ServiceAccountJSON string `yaml:"serviceAccountJson,omitempty"`
229 // UseApplicationDefaultCredentials is a boolean whether to use Application Default Credentials instead of a ServiceAccountJSON
229 // UseApplicationDefaultCredentials is a boolean whether to use Application Default Credentials instead of a ServiceAccountJSON
230− UseApplicationDefaultCredentials bool `yaml:"useApplicationDefaultCredentials,omitempty"`
230+ UseApplicationDefaultCredentials bool `yaml:"useApplicationDefaultCredentials"`
231 // TargetPrincipal is the Google Service Account used for Application Default Credentials
231 // TargetPrincipal is the Google Service Account used for Application Default Credentials
232 TargetPrincipal string `yaml:"targetPrincipal,omitempty"`
232 TargetPrincipal string `yaml:"targetPrincipal,omitempty"`
233 // UseOrganizationId indicates whether to use the organization ID as the UserName claim
233 // UseOrganizationId indicates whether to use the organization ID as the UserName claim
@@ -254,7 +254,7 @@ type OIDCOptions struct {
254 InsecureSkipNonce bool `yaml:"insecureSkipNonce"`
254 InsecureSkipNonce bool `yaml:"insecureSkipNonce"`
255 // SkipDiscovery allows to skip OIDC discovery and use manually supplied Endpoints
255 // SkipDiscovery allows to skip OIDC discovery and use manually supplied Endpoints
256 // default set to 'false'
256 // default set to 'false'
257− SkipDiscovery bool `yaml:"skipDiscovery,omitempty"`
257+ SkipDiscovery bool `yaml:"skipDiscovery"`
258 // JwksURL is the OpenID Connect JWKS URL
258 // JwksURL is the OpenID Connect JWKS URL
259 // eg: https://www.googleapis.com/oauth2/v3/certs
259 // eg: https://www.googleapis.com/oauth2/v3/certs
260 JwksURL string `yaml:"jwksURL,omitempty"`
260 JwksURL string `yaml:"jwksURL,omitempty"`
pkg/apis/options/upstreams.go+5−5
@@ -14,7 +14,7 @@ const (
14type UpstreamConfig struct {
14type UpstreamConfig struct {
15 // ProxyRawPath will pass the raw url path to upstream allowing for urls
15 // ProxyRawPath will pass the raw url path to upstream allowing for urls
16 // like: "/%2F/" which would otherwise be redirected to "/"
16 // like: "/%2F/" which would otherwise be redirected to "/"
17− ProxyRawPath bool `yaml:"proxyRawPath,omitempty"`
17+ ProxyRawPath bool `yaml:"proxyRawPath"`
18
18
19 // Upstreams represents the configuration for the upstream servers.
19 // Upstreams represents the configuration for the upstream servers.
20 // Requests will be proxied to this upstream if the path matches the request path.
20 // Requests will be proxied to this upstream if the path matches the request path.
@@ -64,13 +64,13 @@ type Upstream struct {
64 // This option is insecure and will allow potential Man-In-The-Middle attacks
64 // This option is insecure and will allow potential Man-In-The-Middle attacks
65 // between OAuth2 Proxy and the upstream server.
65 // between OAuth2 Proxy and the upstream server.
66 // Defaults to false.
66 // Defaults to false.
67− InsecureSkipTLSVerify bool `yaml:"insecureSkipTLSVerify,omitempty"`
67+ InsecureSkipTLSVerify bool `yaml:"insecureSkipTLSVerify"`
68
68
69 // Static will make all requests to this upstream have a static response.
69 // Static will make all requests to this upstream have a static response.
70 // The response will have a body of "Authenticated" and a response code
70 // The response will have a body of "Authenticated" and a response code
71 // matching StaticCode.
71 // matching StaticCode.
72 // If StaticCode is not set, the response will return a 200 response.
72 // If StaticCode is not set, the response will return a 200 response.
73− Static bool `yaml:"static,omitempty"`
73+ Static bool `yaml:"static"`
74
74
75 // StaticCode determines the response code for the Static response.
75 // StaticCode determines the response code for the Static response.
76 // This option can only be used with Static enabled.
76 // This option can only be used with Static enabled.
@@ -84,11 +84,11 @@ type Upstream struct {
84 // PassHostHeader determines whether the request host header should be proxied
84 // PassHostHeader determines whether the request host header should be proxied
85 // to the upstream server.
85 // to the upstream server.
86 // Defaults to true.
86 // Defaults to true.
87− PassHostHeader *bool `yaml:"passHostHeader,omitempty"`
87+ PassHostHeader *bool `yaml:"passHostHeader"`
88
88
89 // ProxyWebSockets enables proxying of websockets to upstream servers
89 // ProxyWebSockets enables proxying of websockets to upstream servers
90 // Defaults to true.
90 // Defaults to true.
91− ProxyWebSockets *bool `yaml:"proxyWebSockets,omitempty"`
91+ ProxyWebSockets *bool `yaml:"proxyWebSockets"`
92
92
93 // Timeout is the maximum duration the server will wait for a response from the upstream server.
93 // Timeout is the maximum duration the server will wait for a response from the upstream server.
94 // Defaults to 30 seconds.
94 // Defaults to 30 seconds.