git.iko.soy/typeable/oauth2-proxy
on master help sign in
Merge pull request #489 from oauth2-proxy/move-options

Move Options and Validation to packages

commit236c7fa60ef6c1adaf74e163f129eb337c369bc1 authorJoel Speed <Joel.speed@hotmail.co.uk> dateSat 23 May 2020, 15:59 (6 years ago) parentde0c92acce2c68
14 files changed+583−519
http.go+3−2 http_test.go+5−4 main.go+4−122 oauthproxy.go+20−17 oauthproxy_test.go+55−53 pkg/apis/ip/interfaces.go+11 pkg/apis/options/cookie.go+9−9 pkg/apis/options/load_test.go+5 pkg/apis/options/options.go+340 pkg/apis/options/sessions.go+9−9 realclientip.go → pkg/ip/realclientip.go+6−15 realclientip_test.go → pkg/ip/realclientip_test.go+6−5 options.go → pkg/validation/options.go+47−217 options_test.go → pkg/validation/options_test.go+63−66
http.go+3−2
@@ -9,13 +9,14 @@ import (
99 "strings"
1010 "time"
1111
12+ "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
1213 "github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
1314)
1415
1516// Server represents an HTTP server
1617type Server struct {
1718 Handler http.Handler
18− Opts *Options
19+ Opts *options.Options
1920 stop chan struct{} // channel for waiting shutdown
2021}
2122
@@ -167,7 +168,7 @@ func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
167168 return tc, nil
168169}
169170
170−func redirectToHTTPS(opts *Options, h http.Handler) http.Handler {
171+func redirectToHTTPS(opts *options.Options, h http.Handler) http.Handler {
171172 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
172173 proto := r.Header.Get("X-Forwarded-Proto")
173174 if opts.ForceHTTPS && (r.TLS == nil || (proto != "" && strings.ToLower(proto) != "https")) {
http_test.go+5−4
@@ -7,6 +7,7 @@ import (
77 "testing"
88 "time"
99
10+ "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
1011 "github.com/stretchr/testify/assert"
1112)
1213
@@ -110,7 +111,7 @@ func TestGCPHealthcheckNotIngressPut(t *testing.T) {
110111}
111112
112113func TestRedirectToHTTPSTrue(t *testing.T) {
113− opts := NewOptions()
114+ opts := options.NewOptions()
114115 opts.ForceHTTPS = true
115116 handler := func(w http.ResponseWriter, req *http.Request) {
116117 w.Write([]byte("test"))
@@ -125,7 +126,7 @@ func TestRedirectToHTTPSTrue(t *testing.T) {
125126}
126127
127128func TestRedirectToHTTPSFalse(t *testing.T) {
128− opts := NewOptions()
129+ opts := options.NewOptions()
129130 handler := func(w http.ResponseWriter, req *http.Request) {
130131 w.Write([]byte("test"))
131132 }
@@ -139,7 +140,7 @@ func TestRedirectToHTTPSFalse(t *testing.T) {
139140}
140141
141142func TestRedirectNotWhenHTTPS(t *testing.T) {
142− opts := NewOptions()
143+ opts := options.NewOptions()
143144 opts.ForceHTTPS = true
144145 handler := func(w http.ResponseWriter, req *http.Request) {
145146 w.Write([]byte("test"))
@@ -160,7 +161,7 @@ func TestRedirectNotWhenHTTPS(t *testing.T) {
160161}
161162
162163func TestGracefulShutdown(t *testing.T) {
163− opts := NewOptions()
164+ opts := options.NewOptions()
164165 stop := make(chan struct{}, 1)
165166 srv := Server{Handler: http.DefaultServeMux, Opts: opts, stop: stop}
166167 var wg sync.WaitGroup
main.go+4−122
@@ -13,134 +13,16 @@ import (
1313
1414 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
1515 "github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
16− "github.com/spf13/pflag"
16+ "github.com/oauth2-proxy/oauth2-proxy/pkg/validation"
1717)
1818
1919func main() {
2020 logger.SetFlags(logger.Lshortfile)
21− flagSet := pflag.NewFlagSet("oauth2-proxy", pflag.ExitOnError)
21+ flagSet := options.NewFlagSet()
2222
2323 config := flagSet.String("config", "", "path to config file")
2424 showVersion := flagSet.Bool("version", false, "print version string")
2525
26− flagSet.String("http-address", "127.0.0.1:4180", "[http://]<addr>:<port> or unix://<path> to listen on for HTTP clients")
27− flagSet.String("https-address", ":443", "<addr>:<port> to listen on for HTTPS clients")
28− flagSet.Bool("reverse-proxy", false, "are we running behind a reverse proxy, controls whether headers like X-Real-Ip are accepted")
29− flagSet.String("real-client-ip-header", "X-Real-IP", "Header used to determine the real IP of the client (one of: X-Forwarded-For, X-Real-IP, or X-ProxyUser-IP)")
30− flagSet.Bool("force-https", false, "force HTTPS redirect for HTTP requests")
31− flagSet.String("tls-cert-file", "", "path to certificate file")
32− flagSet.String("tls-key-file", "", "path to private key file")
33− flagSet.String("redirect-url", "", "the OAuth Redirect URL. ie: \"https://internalapp.yourcompany.com/oauth2/callback\"")
34− flagSet.Bool("set-xauthrequest", false, "set X-Auth-Request-User and X-Auth-Request-Email response headers (useful in Nginx auth_request mode)")
35− flagSet.StringSlice("upstream", []string{}, "the http url(s) of the upstream endpoint, file:// paths for static files or static://<status_code> for static response. Routing is based on the path")
36− flagSet.Bool("pass-basic-auth", true, "pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream")
37− flagSet.Bool("set-basic-auth", false, "set HTTP Basic Auth information in response (useful in Nginx auth_request mode)")
38− flagSet.Bool("prefer-email-to-user", false, "Prefer to use the Email address as the Username when passing information to upstream. Will only use Username if Email is unavailable, eg. htaccess authentication. Used in conjunction with -pass-basic-auth and -pass-user-headers")
39− flagSet.Bool("pass-user-headers", true, "pass X-Forwarded-User and X-Forwarded-Email information to upstream")
40− flagSet.String("basic-auth-password", "", "the password to set when passing the HTTP Basic Auth header")
41− flagSet.Bool("pass-access-token", false, "pass OAuth access_token to upstream via X-Forwarded-Access-Token header")
42− flagSet.Bool("pass-host-header", true, "pass the request Host Header to upstream")
43− flagSet.Bool("pass-authorization-header", false, "pass the Authorization Header to upstream")
44− flagSet.Bool("set-authorization-header", false, "set Authorization response headers (useful in Nginx auth_request mode)")
45− flagSet.StringSlice("skip-auth-regex", []string{}, "bypass authentication for requests path's that match (may be given multiple times)")
46− flagSet.Bool("skip-provider-button", false, "will skip sign-in-page to directly reach the next step: oauth/start")
47− flagSet.Bool("skip-auth-preflight", false, "will skip authentication for OPTIONS requests")
48− flagSet.Bool("ssl-insecure-skip-verify", false, "skip validation of certificates presented when using HTTPS providers")
49− flagSet.Bool("ssl-upstream-insecure-skip-verify", false, "skip validation of certificates presented when using HTTPS upstreams")
50− flagSet.Duration("flush-interval", time.Duration(1)*time.Second, "period between response flushing when streaming responses")
51− flagSet.Bool("skip-jwt-bearer-tokens", false, "will skip requests that have verified JWT bearer tokens (default false)")
52− flagSet.StringSlice("extra-jwt-issuers", []string{}, "if skip-jwt-bearer-tokens is set, a list of extra JWT issuer=audience pairs (where the issuer URL has a .well-known/openid-configuration or a .well-known/jwks.json)")
53−
54− flagSet.StringSlice("email-domain", []string{}, "authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email")
55− flagSet.StringSlice("whitelist-domain", []string{}, "allowed domains for redirection after authentication. Prefix domain with a . to allow subdomains (eg .example.com)")
56− flagSet.String("keycloak-group", "", "restrict login to members of this group.")
57− flagSet.String("azure-tenant", "common", "go to a tenant-specific or common (tenant-independent) endpoint.")
58− flagSet.String("bitbucket-team", "", "restrict logins to members of this team")
59− flagSet.String("bitbucket-repository", "", "restrict logins to user with access to this repository")
60− flagSet.String("github-org", "", "restrict logins to members of this organisation")
61− flagSet.String("github-team", "", "restrict logins to members of this team")
62− flagSet.String("github-repo", "", "restrict logins to collaborators of this repository")
63− flagSet.String("github-token", "", "the token to use when verifying repository collaborators (must have push access to the repository)")
64− flagSet.String("gitlab-group", "", "restrict logins to members of this group")
65− flagSet.StringSlice("google-group", []string{}, "restrict logins to members of this google group (may be given multiple times).")
66− flagSet.String("google-admin-email", "", "the google admin to impersonate for api calls")
67− flagSet.String("google-service-account-json", "", "the path to the service account json credentials")
68− flagSet.String("client-id", "", "the OAuth Client ID: ie: \"123456.apps.googleusercontent.com\"")
69− flagSet.String("client-secret", "", "the OAuth Client Secret")
70− flagSet.String("client-secret-file", "", "the file with OAuth Client Secret")
71− flagSet.String("authenticated-emails-file", "", "authenticate against emails via file (one per line)")
72− flagSet.String("htpasswd-file", "", "additionally authenticate against a htpasswd file. Entries must be created with \"htpasswd -s\" for SHA encryption or \"htpasswd -B\" for bcrypt encryption")
73− flagSet.Bool("display-htpasswd-form", true, "display username / password login form if an htpasswd file is provided")
74− flagSet.String("custom-templates-dir", "", "path to custom html templates")
75− flagSet.String("banner", "", "custom banner string. Use \"-\" to disable default banner.")
76− flagSet.String("footer", "", "custom footer string. Use \"-\" to disable default footer.")
77− flagSet.String("proxy-prefix", "/oauth2", "the url root path that this proxy should be nested under (e.g. /<oauth2>/sign_in)")
78− flagSet.String("ping-path", "/ping", "the ping endpoint that can be used for basic health checks")
79− flagSet.Bool("proxy-websockets", true, "enables WebSocket proxying")
80−
81− flagSet.String("cookie-name", "_oauth2_proxy", "the name of the cookie that the oauth_proxy creates")
82− flagSet.String("cookie-secret", "", "the seed string for secure cookies (optionally base64 encoded)")
83− flagSet.StringSlice("cookie-domain", []string{}, "Optional cookie domains to force cookies to (ie: `.yourcompany.com`). The longest domain matching the request's host will be used (or the shortest cookie domain if there is no match).")
84− flagSet.String("cookie-path", "/", "an optional cookie path to force cookies to (ie: /poc/)*")
85− flagSet.Duration("cookie-expire", time.Duration(168)*time.Hour, "expire timeframe for cookie")
86− flagSet.Duration("cookie-refresh", time.Duration(0), "refresh the cookie after this duration; 0 to disable")
87− flagSet.Bool("cookie-secure", true, "set secure (HTTPS) cookie flag")
88− flagSet.Bool("cookie-httponly", true, "set HttpOnly cookie flag")
89− flagSet.String("cookie-samesite", "", "set SameSite cookie attribute (ie: \"lax\", \"strict\", \"none\", or \"\"). ")
90−
91− flagSet.String("session-store-type", "cookie", "the session storage provider to use")
92− flagSet.String("redis-connection-url", "", "URL of redis server for redis session storage (eg: redis://HOST[:PORT])")
93− flagSet.Bool("redis-use-sentinel", false, "Connect to redis via sentinels. Must set --redis-sentinel-master-name and --redis-sentinel-connection-urls to use this feature")
94− flagSet.String("redis-sentinel-master-name", "", "Redis sentinel master name. Used in conjunction with --redis-use-sentinel")
95− flagSet.String("redis-ca-path", "", "Redis custom CA path")
96− flagSet.Bool("redis-insecure-skip-tls-verify", false, "Use insecure TLS connection to redis")
97− flagSet.StringSlice("redis-sentinel-connection-urls", []string{}, "List of Redis sentinel connection URLs (eg redis://HOST[:PORT]). Used in conjunction with --redis-use-sentinel")
98− flagSet.Bool("redis-use-cluster", false, "Connect to redis cluster. Must set --redis-cluster-connection-urls to use this feature")
99− flagSet.StringSlice("redis-cluster-connection-urls", []string{}, "List of Redis cluster connection URLs (eg redis://HOST[:PORT]). Used in conjunction with --redis-use-cluster")
100−
101− flagSet.String("logging-filename", "", "File to log requests to, empty for stdout")
102− flagSet.Int("logging-max-size", 100, "Maximum size in megabytes of the log file before rotation")
103− flagSet.Int("logging-max-age", 7, "Maximum number of days to retain old log files")
104− flagSet.Int("logging-max-backups", 0, "Maximum number of old log files to retain; 0 to disable")
105− flagSet.Bool("logging-local-time", true, "If the time in log files and backup filenames are local or UTC time")
106− flagSet.Bool("logging-compress", false, "Should rotated log files be compressed using gzip")
107−
108− flagSet.Bool("standard-logging", true, "Log standard runtime information")
109− flagSet.String("standard-logging-format", logger.DefaultStandardLoggingFormat, "Template for standard log lines")
110−
111− flagSet.Bool("request-logging", true, "Log HTTP requests")
112− flagSet.String("request-logging-format", logger.DefaultRequestLoggingFormat, "Template for HTTP request log lines")
113− flagSet.String("exclude-logging-paths", "", "Exclude logging requests to paths (eg: '/path1,/path2,/path3')")
114− flagSet.Bool("silence-ping-logging", false, "Disable logging of requests to ping endpoint")
115−
116− flagSet.Bool("auth-logging", true, "Log authentication attempts")
117− flagSet.String("auth-logging-format", logger.DefaultAuthLoggingFormat, "Template for authentication log lines")
118−
119− flagSet.String("provider", "google", "OAuth provider")
120− flagSet.String("provider-display-name", "", "Provider display name")
121− flagSet.String("oidc-issuer-url", "", "OpenID Connect issuer URL (ie: https://accounts.google.com)")
122− flagSet.Bool("insecure-oidc-allow-unverified-email", false, "Don't fail if an email address in an id_token is not verified")
123− flagSet.Bool("insecure-oidc-skip-issuer-verification", false, "Do not verify if issuer matches OIDC discovery URL")
124− flagSet.Bool("skip-oidc-discovery", false, "Skip OIDC discovery and use manually supplied Endpoints")
125− flagSet.String("oidc-jwks-url", "", "OpenID Connect JWKS URL (ie: https://www.googleapis.com/oauth2/v3/certs)")
126− flagSet.String("login-url", "", "Authentication endpoint")
127− flagSet.String("redeem-url", "", "Token redemption endpoint")
128− flagSet.String("profile-url", "", "Profile access endpoint")
129− flagSet.String("resource", "", "The resource that is protected (Azure AD only)")
130− flagSet.String("validate-url", "", "Access token validation endpoint")
131− flagSet.String("scope", "", "OAuth scope specification")
132− flagSet.String("prompt", "", "OIDC prompt")
133− flagSet.String("approval-prompt", "force", "OAuth approval_prompt")
134−
135− flagSet.String("signature-key", "", "GAP-Signature request signature key (algorithm:secretkey)")
136− flagSet.String("acr-values", "", "acr values string: optional")
137− flagSet.String("jwt-key", "", "private key in PEM format used to sign JWT, so that you can say something like -jwt-key=\"${OAUTH2_PROXY_JWT_KEY}\": required by login.gov")
138− flagSet.String("jwt-key-file", "", "path to the private key file in PEM format used to sign the JWT so that you can say something like -jwt-key-file=/etc/ssl/private/jwt_signing_key.pem: required by login.gov")
139− flagSet.String("pubjwk-url", "", "JWK pubkey access endpoint: required by login.gov")
140− flagSet.Bool("gcp-healthchecks", false, "Enable GCP/GKE healthcheck endpoints")
141−
142− flagSet.String("user-id-claim", "email", "which claim contains the user ID")
143−
14426 flagSet.Parse(os.Args[1:])
14527
14628 if *showVersion {
@@ -148,14 +30,14 @@ func main() {
14830 return
14931 }
15032
151− opts := NewOptions()
33+ opts := options.NewOptions()
15234 err := options.Load(*config, flagSet, opts)
15335 if err != nil {
15436 logger.Printf("ERROR: Failed to load config: %v", err)
15537 os.Exit(1)
15638 }
15739
158− err = opts.Validate()
40+ err = validation.Validate(opts)
15941 if err != nil {
16042 logger.Printf("%s", err)
16143 os.Exit(1)
oauthproxy.go+20−17
@@ -19,9 +19,12 @@ import (
1919
2020 "github.com/coreos/go-oidc"
2121 "github.com/mbland/hmacauth"
22+ ipapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/ip"
23+ "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
2224 sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
2325 "github.com/oauth2-proxy/oauth2-proxy/pkg/cookies"
2426 "github.com/oauth2-proxy/oauth2-proxy/pkg/encryption"
27+ "github.com/oauth2-proxy/oauth2-proxy/pkg/ip"
2528 "github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
2629 "github.com/oauth2-proxy/oauth2-proxy/providers"
2730 "github.com/yhat/wsutil"
@@ -112,7 +115,7 @@ type OAuthProxy struct {
112115 jwtBearerVerifiers []*oidc.IDTokenVerifier
113116 compiledRegex []*regexp.Regexp
114117 templates *template.Template
115− realClientIPParser realClientIPParser
118+ realClientIPParser ipapi.RealClientIPParser
116119 Banner string
117120 Footer string
118121}
@@ -143,7 +146,7 @@ func (u *UpstreamProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
143146
144147// NewReverseProxy creates a new reverse proxy for proxying requests to upstream
145148// servers
146−func NewReverseProxy(target *url.URL, opts *Options) (proxy *httputil.ReverseProxy) {
149+func NewReverseProxy(target *url.URL, opts *options.Options) (proxy *httputil.ReverseProxy) {
147150 proxy = httputil.NewSingleHostReverseProxy(target)
148151 proxy.FlushInterval = opts.FlushInterval
149152 if opts.SSLUpstreamInsecureSkipVerify {
@@ -181,7 +184,7 @@ func NewFileServer(path string, filesystemPath string) (proxy http.Handler) {
181184}
182185
183186// NewWebSocketOrRestReverseProxy creates a reverse proxy for REST or websocket based on url
184−func NewWebSocketOrRestReverseProxy(u *url.URL, opts *Options, auth hmacauth.HmacAuth) http.Handler {
187+func NewWebSocketOrRestReverseProxy(u *url.URL, opts *options.Options, auth hmacauth.HmacAuth) http.Handler {
185188 u.Path = ""
186189 proxy := NewReverseProxy(u, opts)
187190 if !opts.PassHostHeader {
@@ -209,14 +212,14 @@ func NewWebSocketOrRestReverseProxy(u *url.URL, opts *Options, auth hmacauth.Hma
209212}
210213
211214// NewOAuthProxy creates a new instance of OAuthProxy from the options provided
212−func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy {
215+func NewOAuthProxy(opts *options.Options, validator func(string) bool) *OAuthProxy {
213216 serveMux := http.NewServeMux()
214217 var auth hmacauth.HmacAuth
215− if sigData := opts.signatureData; sigData != nil {
216− auth = hmacauth.NewHmacAuth(sigData.hash, []byte(sigData.key),
218+ if sigData := opts.GetSignatureData(); sigData != nil {
219+ auth = hmacauth.NewHmacAuth(sigData.Hash, []byte(sigData.Key),
217220 SignatureHeader, SignatureHeaders)
218221 }
219− for _, u := range opts.proxyURLs {
222+ for _, u := range opts.GetProxyURLs() {
220223 path := u.Path
221224 host := u.Host
222225 switch u.Scheme {
@@ -252,7 +255,7 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy {
252255 panic(fmt.Sprintf("unknown upstream protocol %s", u.Scheme))
253256 }
254257 }
255− for _, u := range opts.compiledRegex {
258+ for _, u := range opts.GetCompiledRegex() {
256259 logger.Printf("compiled skip-auth-regex => %q", u)
257260 }
258261
@@ -262,12 +265,12 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy {
262265 logger.Printf("Skipping JWT tokens from extra JWT issuer: %q", issuer)
263266 }
264267 }
265− redirectURL := opts.redirectURL
268+ redirectURL := opts.GetRedirectURL()
266269 if redirectURL.Path == "" {
267270 redirectURL.Path = fmt.Sprintf("%s/callback", opts.ProxyPrefix)
268271 }
269272
270− logger.Printf("OAuthProxy configured for %s Client ID: %s", opts.provider.Data().ProviderName, opts.ClientID)
273+ logger.Printf("OAuthProxy configured for %s Client ID: %s", opts.GetProvider().Data().ProviderName, opts.ClientID)
271274 refresh := "disabled"
272275 if opts.Cookie.Refresh != time.Duration(0) {
273276 refresh = fmt.Sprintf("after %s", opts.Cookie.Refresh)
@@ -298,18 +301,18 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy {
298301 UserInfoPath: fmt.Sprintf("%s/userinfo", opts.ProxyPrefix),
299302
300303 ProxyPrefix: opts.ProxyPrefix,
301− provider: opts.provider,
304+ provider: opts.GetProvider(),
302305 providerNameOverride: opts.ProviderName,
303− sessionStore: opts.sessionStore,
306+ sessionStore: opts.GetSessionStore(),
304307 serveMux: serveMux,
305308 redirectURL: redirectURL,
306309 whitelistDomains: opts.WhitelistDomains,
307310 skipAuthRegex: opts.SkipAuthRegex,
308311 skipAuthPreflight: opts.SkipAuthPreflight,
309312 skipJwtBearerTokens: opts.SkipJwtBearerTokens,
310− jwtBearerVerifiers: opts.jwtBearerVerifiers,
311− compiledRegex: opts.compiledRegex,
312− realClientIPParser: opts.realClientIPParser,
313+ jwtBearerVerifiers: opts.GetJWTBearerVerifiers(),
314+ compiledRegex: opts.GetCompiledRegex(),
315+ realClientIPParser: opts.GetRealClientIPParser(),
313316 SetXAuthRequest: opts.SetXAuthRequest,
314317 PassBasicAuth: opts.PassBasicAuth,
315318 SetBasicAuth: opts.SetBasicAuth,
@@ -760,7 +763,7 @@ func (p *OAuthProxy) OAuthStart(rw http.ResponseWriter, req *http.Request) {
760763// OAuthCallback is the OAuth2 authentication flow callback that finishes the
761764// OAuth2 authentication flow
762765func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
763− remoteAddr := getClientString(p.realClientIPParser, req, true)
766+ remoteAddr := ip.GetClientString(p.realClientIPParser, req, true)
764767
765768 // finish the oauth cycle
766769 err := req.ParseForm()
@@ -888,7 +891,7 @@ func (p *OAuthProxy) getAuthenticatedSession(rw http.ResponseWriter, req *http.R
888891 }
889892 }
890893
891− remoteAddr := getClientString(p.realClientIPParser, req, true)
894+ remoteAddr := ip.GetClientString(p.realClientIPParser, req, true)
892895 if session == nil {
893896 session, err = p.LoadCookiedSession(req)
894897 if err != nil {
oauthproxy_test.go+55−53
@@ -18,9 +18,11 @@ import (
1818
1919 "github.com/coreos/go-oidc"
2020 "github.com/mbland/hmacauth"
21+ "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
2122 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
2223 "github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
2324 "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions/cookie"
25+ "github.com/oauth2-proxy/oauth2-proxy/pkg/validation"
2426 "github.com/oauth2-proxy/oauth2-proxy/providers"
2527 "github.com/stretchr/testify/assert"
2628 "github.com/stretchr/testify/require"
@@ -71,7 +73,7 @@ func TestWebSocketProxy(t *testing.T) {
7173
7274 backendURL, _ := url.Parse(backend.URL)
7375
74− options := NewOptions()
76+ options := options.NewOptions()
7577 var auth hmacauth.HmacAuth
7678 options.PassHostHeader = true
7779 proxyHandler := NewWebSocketOrRestReverseProxy(backendURL, options, auth)
@@ -121,7 +123,7 @@ func TestNewReverseProxy(t *testing.T) {
121123 backendHost := net.JoinHostPort(backendHostname, backendPort)
122124 proxyURL, _ := url.Parse(backendURL.Scheme + "://" + backendHost + "/")
123125
124− proxyHandler := NewReverseProxy(proxyURL, &Options{FlushInterval: time.Second})
126+ proxyHandler := NewReverseProxy(proxyURL, &options.Options{FlushInterval: time.Second})
125127 setProxyUpstreamHostHeader(proxyHandler, proxyURL)
126128 frontend := httptest.NewServer(proxyHandler)
127129 defer frontend.Close()
@@ -143,7 +145,7 @@ func TestEncodedSlashes(t *testing.T) {
143145 defer backend.Close()
144146
145147 b, _ := url.Parse(backend.URL)
146− proxyHandler := NewReverseProxy(b, &Options{FlushInterval: time.Second})
148+ proxyHandler := NewReverseProxy(b, &options.Options{FlushInterval: time.Second})
147149 setProxyDirector(proxyHandler)
148150 frontend := httptest.NewServer(proxyHandler)
149151 defer frontend.Close()
@@ -161,11 +163,11 @@ func TestEncodedSlashes(t *testing.T) {
161163}
162164
163165func TestRobotsTxt(t *testing.T) {
164− opts := NewOptions()
166+ opts := options.NewOptions()
165167 opts.ClientID = "asdlkjx"
166168 opts.ClientSecret = "alkgks"
167169 opts.Cookie.Secret = "asdkugkj"
168− opts.Validate()
170+ validation.Validate(opts)
169171
170172 proxy := NewOAuthProxy(opts, func(string) bool { return true })
171173 rw := httptest.NewRecorder()
@@ -176,7 +178,7 @@ func TestRobotsTxt(t *testing.T) {
176178}
177179
178180func TestIsValidRedirect(t *testing.T) {
179− opts := NewOptions()
181+ opts := options.NewOptions()
180182 opts.ClientID = "skdlfj"
181183 opts.ClientSecret = "fgkdsgj"
182184 opts.Cookie.Secret = "ljgiogbj"
@@ -189,7 +191,7 @@ func TestIsValidRedirect(t *testing.T) {
189191 "anyport.bar:*",
190192 ".sub.anyport.bar:*",
191193 }
192− opts.Validate()
194+ validation.Validate(opts)
193195
194196 proxy := NewOAuthProxy(opts, func(string) bool { return true })
195197
@@ -451,7 +453,7 @@ func TestBasicAuthPassword(t *testing.T) {
451453 w.WriteHeader(200)
452454 w.Write([]byte(payload))
453455 }))
454− opts := NewOptions()
456+ opts := options.NewOptions()
455457 opts.Upstreams = append(opts.Upstreams, providerServer.URL)
456458 // The CookieSecret must be 32 bytes in order to create the AES
457459 // cipher.
@@ -464,12 +466,12 @@ func TestBasicAuthPassword(t *testing.T) {
464466 opts.PassUserHeaders = true
465467 opts.PreferEmailToUser = true
466468 opts.BasicAuthPassword = "This is a secure password"
467− opts.Validate()
469+ validation.Validate(opts)
468470
469471 providerURL, _ := url.Parse(providerServer.URL)
470472 const emailAddress = "john.doe@example.com"
471473
472− opts.provider = NewTestProvider(providerURL, emailAddress)
474+ opts.SetProvider(NewTestProvider(providerURL, emailAddress))
473475 proxy := NewOAuthProxy(opts, func(email string) bool {
474476 return email == emailAddress
475477 })
@@ -518,12 +520,12 @@ func TestBasicAuthPassword(t *testing.T) {
518520}
519521
520522func TestBasicAuthWithEmail(t *testing.T) {
521− opts := NewOptions()
523+ opts := options.NewOptions()
522524 opts.PassBasicAuth = true
523525 opts.PassUserHeaders = false
524526 opts.PreferEmailToUser = false
525527 opts.BasicAuthPassword = "This is a secure password"
526− opts.Validate()
528+ validation.Validate(opts)
527529
528530 const emailAddress = "john.doe@example.com"
529531 const userName = "9fcab5c9b889a557"
@@ -564,11 +566,11 @@ func TestBasicAuthWithEmail(t *testing.T) {
564566}
565567
566568func TestPassUserHeadersWithEmail(t *testing.T) {
567− opts := NewOptions()
569+ opts := options.NewOptions()
568570 opts.PassBasicAuth = false
569571 opts.PassUserHeaders = true
570572 opts.PreferEmailToUser = false
571− opts.Validate()
573+ validation.Validate(opts)
572574
573575 const emailAddress = "john.doe@example.com"
574576 const userName = "9fcab5c9b889a557"
@@ -605,7 +607,7 @@ func TestPassUserHeadersWithEmail(t *testing.T) {
605607type PassAccessTokenTest struct {
606608 providerServer *httptest.Server
607609 proxy *OAuthProxy
608− opts *Options
610+ opts *options.Options
609611}
610612
611613type PassAccessTokenTestOptions struct {
@@ -632,7 +634,7 @@ func NewPassAccessTokenTest(opts PassAccessTokenTestOptions) *PassAccessTokenTes
632634 w.Write([]byte(payload))
633635 }))
634636
635− t.opts = NewOptions()
637+ t.opts = options.NewOptions()
636638 t.opts.Upstreams = append(t.opts.Upstreams, t.providerServer.URL)
637639 if opts.ProxyUpstream != "" {
638640 t.opts.Upstreams = append(t.opts.Upstreams, opts.ProxyUpstream)
@@ -644,12 +646,12 @@ func NewPassAccessTokenTest(opts PassAccessTokenTestOptions) *PassAccessTokenTes
644646 t.opts.ClientSecret = "gfjgojl"
645647 t.opts.Cookie.Secure = false
646648 t.opts.PassAccessToken = opts.PassAccessToken
647− t.opts.Validate()
649+ validation.Validate(t.opts)
648650
649651 providerURL, _ := url.Parse(t.providerServer.URL)
650652 const emailAddress = "michael.bland@gsa.gov"
651653
652− t.opts.provider = NewTestProvider(providerURL, emailAddress)
654+ t.opts.SetProvider(NewTestProvider(providerURL, emailAddress))
653655 t.proxy = NewOAuthProxy(t.opts, func(email string) bool {
654656 return email == emailAddress
655657 })
@@ -779,7 +781,7 @@ func TestDoNotForwardAccessTokenUpstream(t *testing.T) {
779781}
780782
781783type SignInPageTest struct {
782− opts *Options
784+ opts *options.Options
783785 proxy *OAuthProxy
784786 signInRegexp *regexp.Regexp
785787 signInProviderRegexp *regexp.Regexp
@@ -791,12 +793,12 @@ const signInSkipProvider = `>Found<`
791793func NewSignInPageTest(skipProvider bool) *SignInPageTest {
792794 var sipTest SignInPageTest
793795
794− sipTest.opts = NewOptions()
796+ sipTest.opts = options.NewOptions()
795797 sipTest.opts.Cookie.Secret = "adklsj2"
796798 sipTest.opts.ClientID = "lkdgj"
797799 sipTest.opts.ClientSecret = "sgiufgoi"
798800 sipTest.opts.SkipProviderButton = skipProvider
799− sipTest.opts.Validate()
801+ validation.Validate(sipTest.opts)
800802
801803 sipTest.proxy = NewOAuthProxy(sipTest.opts, func(email string) bool {
802804 return true
@@ -876,7 +878,7 @@ func TestSignInPageSkipProviderDirect(t *testing.T) {
876878}
877879
878880type ProcessCookieTest struct {
879− opts *Options
881+ opts *options.Options
880882 proxy *OAuthProxy
881883 rw *httptest.ResponseRecorder
882884 req *http.Request
@@ -887,12 +889,12 @@ type ProcessCookieTestOpts struct {
887889 providerValidateCookieResponse bool
888890}
889891
890−type OptionsModifier func(*Options)
892+type OptionsModifier func(*options.Options)
891893
892894func NewProcessCookieTest(opts ProcessCookieTestOpts, modifiers ...OptionsModifier) *ProcessCookieTest {
893895 var pcTest ProcessCookieTest
894896
895− pcTest.opts = NewOptions()
897+ pcTest.opts = options.NewOptions()
896898 for _, modifier := range modifiers {
897899 modifier(pcTest.opts)
898900 }
@@ -902,7 +904,7 @@ func NewProcessCookieTest(opts ProcessCookieTestOpts, modifiers ...OptionsModifi
902904 // First, set the CookieRefresh option so proxy.AesCipher is created,
903905 // needed to encrypt the access_token.
904906 pcTest.opts.Cookie.Refresh = time.Hour
905− pcTest.opts.Validate()
907+ validation.Validate(pcTest.opts)
906908
907909 pcTest.proxy = NewOAuthProxy(pcTest.opts, func(email string) bool {
908910 return pcTest.validateUser
@@ -971,7 +973,7 @@ func TestProcessCookieNoCookieError(t *testing.T) {
971973}
972974
973975func TestProcessCookieRefreshNotSet(t *testing.T) {
974− pcTest := NewProcessCookieTestWithOptionsModifiers(func(opts *Options) {
976+ pcTest := NewProcessCookieTestWithOptionsModifiers(func(opts *options.Options) {
975977 opts.Cookie.Expire = time.Duration(23) * time.Hour
976978 })
977979 reference := time.Now().Add(time.Duration(-2) * time.Hour)
@@ -988,7 +990,7 @@ func TestProcessCookieRefreshNotSet(t *testing.T) {
988990}
989991
990992func TestProcessCookieFailIfCookieExpired(t *testing.T) {
991− pcTest := NewProcessCookieTestWithOptionsModifiers(func(opts *Options) {
993+ pcTest := NewProcessCookieTestWithOptionsModifiers(func(opts *options.Options) {
992994 opts.Cookie.Expire = time.Duration(24) * time.Hour
993995 })
994996 reference := time.Now().Add(time.Duration(25) * time.Hour * -1)
@@ -1003,7 +1005,7 @@ func TestProcessCookieFailIfCookieExpired(t *testing.T) {
10031005}
10041006
10051007func TestProcessCookieFailIfRefreshSetAndCookieExpired(t *testing.T) {
1006− pcTest := NewProcessCookieTestWithOptionsModifiers(func(opts *Options) {
1008+ pcTest := NewProcessCookieTestWithOptionsModifiers(func(opts *options.Options) {
10071009 opts.Cookie.Expire = time.Duration(24) * time.Hour
10081010 })
10091011 reference := time.Now().Add(time.Duration(25) * time.Hour * -1)
@@ -1073,7 +1075,7 @@ func TestAuthOnlyEndpointUnauthorizedOnNoCookieSetError(t *testing.T) {
10731075}
10741076
10751077func TestAuthOnlyEndpointUnauthorizedOnExpiration(t *testing.T) {
1076− test := NewAuthOnlyEndpointTest(func(opts *Options) {
1078+ test := NewAuthOnlyEndpointTest(func(opts *options.Options) {
10771079 opts.Cookie.Expire = time.Duration(24) * time.Hour
10781080 })
10791081 reference := time.Now().Add(time.Duration(25) * time.Hour * -1)
@@ -1103,9 +1105,9 @@ func TestAuthOnlyEndpointUnauthorizedOnEmailValidationFailure(t *testing.T) {
11031105func TestAuthOnlyEndpointSetXAuthRequestHeaders(t *testing.T) {
11041106 var pcTest ProcessCookieTest
11051107
1106− pcTest.opts = NewOptions()
1108+ pcTest.opts = options.NewOptions()
11071109 pcTest.opts.SetXAuthRequest = true
1108− pcTest.opts.Validate()
1110+ validation.Validate(pcTest.opts)
11091111
11101112 pcTest.proxy = NewOAuthProxy(pcTest.opts, func(email string) bool {
11111113 return pcTest.validateUser
@@ -1133,10 +1135,10 @@ func TestAuthOnlyEndpointSetXAuthRequestHeaders(t *testing.T) {
11331135func TestAuthOnlyEndpointSetBasicAuthTrueRequestHeaders(t *testing.T) {
11341136 var pcTest ProcessCookieTest
11351137
1136− pcTest.opts = NewOptions()
1138+ pcTest.opts = options.NewOptions()
11371139 pcTest.opts.SetXAuthRequest = true
11381140 pcTest.opts.SetBasicAuth = true
1139− pcTest.opts.Validate()
1141+ validation.Validate(pcTest.opts)
11401142
11411143 pcTest.proxy = NewOAuthProxy(pcTest.opts, func(email string) bool {
11421144 return pcTest.validateUser
@@ -1166,10 +1168,10 @@ func TestAuthOnlyEndpointSetBasicAuthTrueRequestHeaders(t *testing.T) {
11661168func TestAuthOnlyEndpointSetBasicAuthFalseRequestHeaders(t *testing.T) {
11671169 var pcTest ProcessCookieTest
11681170
1169− pcTest.opts = NewOptions()
1171+ pcTest.opts = options.NewOptions()
11701172 pcTest.opts.SetXAuthRequest = true
11711173 pcTest.opts.SetBasicAuth = false
1172− pcTest.opts.Validate()
1174+ validation.Validate(pcTest.opts)
11731175
11741176 pcTest.proxy = NewOAuthProxy(pcTest.opts, func(email string) bool {
11751177 return pcTest.validateUser
@@ -1202,16 +1204,16 @@ func TestAuthSkippedForPreflightRequests(t *testing.T) {
12021204 }))
12031205 defer upstream.Close()
12041206
1205− opts := NewOptions()
1207+ opts := options.NewOptions()
12061208 opts.Upstreams = append(opts.Upstreams, upstream.URL)
12071209 opts.ClientID = "aljsal"
12081210 opts.ClientSecret = "jglkfsdgj"
12091211 opts.Cookie.Secret = "dkfjgdls"
12101212 opts.SkipAuthPreflight = true
1211− opts.Validate()
1213+ validation.Validate(opts)
12121214
12131215 upstreamURL, _ := url.Parse(upstream.URL)
1214− opts.provider = NewTestProvider(upstreamURL, "")
1216+ opts.SetProvider(NewTestProvider(upstreamURL, ""))
12151217
12161218 proxy := NewOAuthProxy(opts, func(string) bool { return false })
12171219 rw := httptest.NewRecorder()
@@ -1242,7 +1244,7 @@ func (v *SignatureAuthenticator) Authenticate(w http.ResponseWriter, r *http.Req
12421244}
12431245
12441246type SignatureTest struct {
1245− opts *Options
1247+ opts *options.Options
12461248 upstream *httptest.Server
12471249 upstreamHost string
12481250 provider *httptest.Server
@@ -1252,7 +1254,7 @@ type SignatureTest struct {
12521254}
12531255
12541256func NewSignatureTest() *SignatureTest {
1255− opts := NewOptions()
1257+ opts := options.NewOptions()
12561258 opts.Cookie.Secret = "cookie secret"
12571259 opts.ClientID = "client ID"
12581260 opts.ClientSecret = "client secret"
@@ -1269,7 +1271,7 @@ func NewSignatureTest() *SignatureTest {
12691271 }
12701272 provider := httptest.NewServer(http.HandlerFunc(providerHandler))
12711273 providerURL, _ := url.Parse(provider.URL)
1272− opts.provider = NewTestProvider(providerURL, "mbland@acm.org")
1274+ opts.SetProvider(NewTestProvider(providerURL, "mbland@acm.org"))
12731275
12741276 return &SignatureTest{
12751277 opts,
@@ -1304,7 +1306,7 @@ func (fnc *fakeNetConn) Read(p []byte) (n int, err error) {
13041306}
13051307
13061308func (st *SignatureTest) MakeRequestWithExpectedKey(method, body, key string) {
1307− err := st.opts.Validate()
1309+ err := validation.Validate(st.opts)
13081310 if err != nil {
13091311 panic(err)
13101312 }
@@ -1360,8 +1362,8 @@ func TestRequestSignaturePostRequest(t *testing.T) {
13601362}
13611363
13621364func TestGetRedirect(t *testing.T) {
1363− options := NewOptions()
1364− _ = options.Validate()
1365+ options := options.NewOptions()
1366+ _ = validation.Validate(options)
13651367 require.NotEmpty(t, options.ProxyPrefix)
13661368 proxy := NewOAuthProxy(options, func(s string) bool { return false })
13671369
@@ -1393,17 +1395,17 @@ func TestGetRedirect(t *testing.T) {
13931395}
13941396
13951397type ajaxRequestTest struct {
1396− opts *Options
1398+ opts *options.Options
13971399 proxy *OAuthProxy
13981400}
13991401
14001402func newAjaxRequestTest() *ajaxRequestTest {
14011403 test := &ajaxRequestTest{}
1402− test.opts = NewOptions()
1404+ test.opts = options.NewOptions()
14031405 test.opts.Cookie.Secret = "sdflsw"
14041406 test.opts.ClientID = "gkljfdl"
14051407 test.opts.ClientSecret = "sdflkjs"
1406− test.opts.Validate()
1408+ validation.Validate(test.opts)
14071409 test.proxy = NewOAuthProxy(test.opts, func(email string) bool {
14081410 return true
14091411 })
@@ -1457,7 +1459,7 @@ func TestAjaxForbiddendRequest(t *testing.T) {
14571459}
14581460
14591461func TestClearSplitCookie(t *testing.T) {
1460− opts := NewOptions()
1462+ opts := options.NewOptions()
14611463 opts.Cookie.Name = "oauth2"
14621464 opts.Cookie.Domains = []string{"abc"}
14631465 store, err := cookie.NewCookieSessionStore(&opts.Session, &opts.Cookie)
@@ -1486,7 +1488,7 @@ func TestClearSplitCookie(t *testing.T) {
14861488}
14871489
14881490func TestClearSingleCookie(t *testing.T) {
1489− opts := NewOptions()
1491+ opts := options.NewOptions()
14901492 opts.Cookie.Name = "oauth2"
14911493 opts.Cookie.Domains = []string{"abc"}
14921494 store, err := cookie.NewCookieSessionStore(&opts.Session, &opts.Cookie)
@@ -1542,12 +1544,12 @@ func TestGetJwtSession(t *testing.T) {
15421544 verifier := oidc.NewVerifier("https://issuer.example.com", keyset,
15431545 &oidc.Config{ClientID: "https://test.myapp.com", SkipExpiryCheck: true})
15441546
1545− test := NewAuthOnlyEndpointTest(func(opts *Options) {
1547+ test := NewAuthOnlyEndpointTest(func(opts *options.Options) {
15461548 opts.PassAuthorization = true
15471549 opts.SetAuthorization = true
15481550 opts.SetXAuthRequest = true
15491551 opts.SkipJwtBearerTokens = true
1550− opts.jwtBearerVerifiers = append(opts.jwtBearerVerifiers, verifier)
1552+ opts.SetJWTBearerVerifiers(append(opts.GetJWTBearerVerifiers(), verifier))
15511553 })
15521554 tp, _ := test.proxy.provider.(*TestProvider)
15531555 tp.GroupValidator = func(s string) bool {
@@ -1666,10 +1668,10 @@ func Test_noCacheHeadersDoesNotExistsInResponseHeadersFromUpstream(t *testing.T)
16661668 }))
16671669 t.Cleanup(upstream.Close)
16681670
1669− opts := NewOptions()
1671+ opts := options.NewOptions()
16701672 opts.Upstreams = []string{upstream.URL}
16711673 opts.SkipAuthRegex = []string{".*"}
1672− _ = opts.Validate()
1674+ _ = validation.Validate(opts)
16731675 proxy := NewOAuthProxy(opts, func(email string) bool {
16741676 return true
16751677 })
pkg/apis/ip/interfaces.gonew file+11
@@ -0,0 +1,11 @@
1+package ip
2+
3+import (
4+ "net"
5+ "net/http"
6+)
7+
8+// RealClientIPParser is an interface for a getting the client's real IP to be used for logging.
9+type RealClientIPParser interface {
10+ GetRealClientIP(http.Header) (net.IP, error)
11+}
pkg/apis/options/cookie.go+9−9
@@ -4,13 +4,13 @@ import "time"
44
55// CookieOptions contains configuration options relating to Cookie configuration
66type CookieOptions struct {
7− Name string `flag:"cookie-name" cfg:"cookie_name" env:"OAUTH2_PROXY_COOKIE_NAME"`
8− Secret string `flag:"cookie-secret" cfg:"cookie_secret" env:"OAUTH2_PROXY_COOKIE_SECRET"`
9− Domains []string `flag:"cookie-domain" cfg:"cookie_domain" env:"OAUTH2_PROXY_COOKIE_DOMAIN"`
10− Path string `flag:"cookie-path" cfg:"cookie_path" env:"OAUTH2_PROXY_COOKIE_PATH"`
11− Expire time.Duration `flag:"cookie-expire" cfg:"cookie_expire" env:"OAUTH2_PROXY_COOKIE_EXPIRE"`
12− Refresh time.Duration `flag:"cookie-refresh" cfg:"cookie_refresh" env:"OAUTH2_PROXY_COOKIE_REFRESH"`
13− Secure bool `flag:"cookie-secure" cfg:"cookie_secure" env:"OAUTH2_PROXY_COOKIE_SECURE"`
14− HTTPOnly bool `flag:"cookie-httponly" cfg:"cookie_httponly" env:"OAUTH2_PROXY_COOKIE_HTTPONLY"`
15− SameSite string `flag:"cookie-samesite" cfg:"cookie_samesite" env:"OAUTH2_PROXY_COOKIE_SAMESITE"`
7+ Name string `flag:"cookie-name" cfg:"cookie_name"`
8+ Secret string `flag:"cookie-secret" cfg:"cookie_secret"`
9+ Domains []string `flag:"cookie-domain" cfg:"cookie_domain"`
10+ Path string `flag:"cookie-path" cfg:"cookie_path"`
11+ Expire time.Duration `flag:"cookie-expire" cfg:"cookie_expire"`
12+ Refresh time.Duration `flag:"cookie-refresh" cfg:"cookie_refresh"`
13+ Secure bool `flag:"cookie-secure" cfg:"cookie_secure"`
14+ HTTPOnly bool `flag:"cookie-httponly" cfg:"cookie_httponly"`
15+ SameSite string `flag:"cookie-samesite" cfg:"cookie_samesite"`
1616}
pkg/apis/options/load_test.go+5
@@ -295,6 +295,11 @@ var _ = Describe("Load", func() {
295295 },
296296 },
297297 }),
298+ Entry("with an empty Options struct, should return default values", &testOptionsTableInput{
299+ flagSet: NewFlagSet,
300+ input: &Options{},
301+ expectedOutput: NewOptions(),
302+ }),
298303 )
299304 })
300305})
pkg/apis/options/options.gonew file+340
@@ -0,0 +1,340 @@
1+package options
2+
3+import (
4+ "crypto"
5+ "net/url"
6+ "regexp"
7+ "time"
8+
9+ oidc "github.com/coreos/go-oidc"
10+ ipapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/ip"
11+ sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
12+ "github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
13+ "github.com/oauth2-proxy/oauth2-proxy/providers"
14+ "github.com/spf13/pflag"
15+)
16+
17+// SignatureData holds hmacauth signature hash and key
18+type SignatureData struct {
19+ Hash crypto.Hash
20+ Key string
21+}
22+
23+// Options holds Configuration Options that can be set by Command Line Flag,
24+// or Config File
25+type Options struct {
26+ ProxyPrefix string `flag:"proxy-prefix" cfg:"proxy_prefix"`
27+ PingPath string `flag:"ping-path" cfg:"ping_path"`
28+ ProxyWebSockets bool `flag:"proxy-websockets" cfg:"proxy_websockets"`
29+ HTTPAddress string `flag:"http-address" cfg:"http_address"`
30+ HTTPSAddress string `flag:"https-address" cfg:"https_address"`
31+ ReverseProxy bool `flag:"reverse-proxy" cfg:"reverse_proxy"`
32+ RealClientIPHeader string `flag:"real-client-ip-header" cfg:"real_client_ip_header"`
33+ ForceHTTPS bool `flag:"force-https" cfg:"force_https"`
34+ RawRedirectURL string `flag:"redirect-url" cfg:"redirect_url"`
35+ ClientID string `flag:"client-id" cfg:"client_id"`
36+ ClientSecret string `flag:"client-secret" cfg:"client_secret"`
37+ ClientSecretFile string `flag:"client-secret-file" cfg:"client_secret_file"`
38+ TLSCertFile string `flag:"tls-cert-file" cfg:"tls_cert_file"`
39+ TLSKeyFile string `flag:"tls-key-file" cfg:"tls_key_file"`
40+
41+ AuthenticatedEmailsFile string `flag:"authenticated-emails-file" cfg:"authenticated_emails_file"`
42+ KeycloakGroup string `flag:"keycloak-group" cfg:"keycloak_group"`
43+ AzureTenant string `flag:"azure-tenant" cfg:"azure_tenant"`
44+ BitbucketTeam string `flag:"bitbucket-team" cfg:"bitbucket_team"`
45+ BitbucketRepository string `flag:"bitbucket-repository" cfg:"bitbucket_repository"`
46+ EmailDomains []string `flag:"email-domain" cfg:"email_domains"`
47+ WhitelistDomains []string `flag:"whitelist-domain" cfg:"whitelist_domains"`
48+ GitHubOrg string `flag:"github-org" cfg:"github_org"`
49+ GitHubTeam string `flag:"github-team" cfg:"github_team"`
50+ GitHubRepo string `flag:"github-repo" cfg:"github_repo"`
51+ GitHubToken string `flag:"github-token" cfg:"github_token"`
52+ GitLabGroup string `flag:"gitlab-group" cfg:"gitlab_group"`
53+ GoogleGroups []string `flag:"google-group" cfg:"google_group"`
54+ GoogleAdminEmail string `flag:"google-admin-email" cfg:"google_admin_email"`
55+ GoogleServiceAccountJSON string `flag:"google-service-account-json" cfg:"google_service_account_json"`
56+ HtpasswdFile string `flag:"htpasswd-file" cfg:"htpasswd_file"`
57+ DisplayHtpasswdForm bool `flag:"display-htpasswd-form" cfg:"display_htpasswd_form"`
58+ CustomTemplatesDir string `flag:"custom-templates-dir" cfg:"custom_templates_dir"`
59+ Banner string `flag:"banner" cfg:"banner"`
60+ Footer string `flag:"footer" cfg:"footer"`
61+
62+ Cookie CookieOptions `cfg:",squash"`
63+ Session SessionOptions `cfg:",squash"`
64+
65+ Upstreams []string `flag:"upstream" cfg:"upstreams"`
66+ SkipAuthRegex []string `flag:"skip-auth-regex" cfg:"skip_auth_regex"`
67+ SkipJwtBearerTokens bool `flag:"skip-jwt-bearer-tokens" cfg:"skip_jwt_bearer_tokens"`
68+ ExtraJwtIssuers []string `flag:"extra-jwt-issuers" cfg:"extra_jwt_issuers"`
69+ PassBasicAuth bool `flag:"pass-basic-auth" cfg:"pass_basic_auth"`
70+ SetBasicAuth bool `flag:"set-basic-auth" cfg:"set_basic_auth"`
71+ PreferEmailToUser bool `flag:"prefer-email-to-user" cfg:"prefer_email_to_user"`
72+ BasicAuthPassword string `flag:"basic-auth-password" cfg:"basic_auth_password"`
73+ PassAccessToken bool `flag:"pass-access-token" cfg:"pass_access_token"`
74+ PassHostHeader bool `flag:"pass-host-header" cfg:"pass_host_header"`
75+ SkipProviderButton bool `flag:"skip-provider-button" cfg:"skip_provider_button"`
76+ PassUserHeaders bool `flag:"pass-user-headers" cfg:"pass_user_headers"`
77+ SSLInsecureSkipVerify bool `flag:"ssl-insecure-skip-verify" cfg:"ssl_insecure_skip_verify"`
78+ SSLUpstreamInsecureSkipVerify bool `flag:"ssl-upstream-insecure-skip-verify" cfg:"ssl_upstream_insecure_skip_verify"`
79+ SetXAuthRequest bool `flag:"set-xauthrequest" cfg:"set_xauthrequest"`
80+ SetAuthorization bool `flag:"set-authorization-header" cfg:"set_authorization_header"`
81+ PassAuthorization bool `flag:"pass-authorization-header" cfg:"pass_authorization_header"`
82+ SkipAuthPreflight bool `flag:"skip-auth-preflight" cfg:"skip_auth_preflight"`
83+ FlushInterval time.Duration `flag:"flush-interval" cfg:"flush_interval"`
84+
85+ // These options allow for other providers besides Google, with
86+ // potential overrides.
87+ ProviderType string `flag:"provider" cfg:"provider"`
88+ ProviderName string `flag:"provider-display-name" cfg:"provider_display_name"`
89+ OIDCIssuerURL string `flag:"oidc-issuer-url" cfg:"oidc_issuer_url"`
90+ InsecureOIDCAllowUnverifiedEmail bool `flag:"insecure-oidc-allow-unverified-email" cfg:"insecure_oidc_allow_unverified_email"`
91+ InsecureOIDCSkipIssuerVerification bool `flag:"insecure-oidc-skip-issuer-verification" cfg:"insecure_oidc_skip_issuer_verification"`
92+ SkipOIDCDiscovery bool `flag:"skip-oidc-discovery" cfg:"skip_oidc_discovery"`
93+ OIDCJwksURL string `flag:"oidc-jwks-url" cfg:"oidc_jwks_url"`
94+ LoginURL string `flag:"login-url" cfg:"login_url"`
95+ RedeemURL string `flag:"redeem-url" cfg:"redeem_url"`
96+ ProfileURL string `flag:"profile-url" cfg:"profile_url"`
97+ ProtectedResource string `flag:"resource" cfg:"resource"`
98+ ValidateURL string `flag:"validate-url" cfg:"validate_url"`
99+ Scope string `flag:"scope" cfg:"scope"`
100+ Prompt string `flag:"prompt" cfg:"prompt"`
101+ ApprovalPrompt string `flag:"approval-prompt" cfg:"approval_prompt"` // Deprecated by OIDC 1.0
102+ UserIDClaim string `flag:"user-id-claim" cfg:"user_id_claim"`
103+
104+ // Configuration values for logging
105+ LoggingFilename string `flag:"logging-filename" cfg:"logging_filename"`
106+ LoggingMaxSize int `flag:"logging-max-size" cfg:"logging_max_size"`
107+ LoggingMaxAge int `flag:"logging-max-age" cfg:"logging_max_age"`
108+ LoggingMaxBackups int `flag:"logging-max-backups" cfg:"logging_max_backups"`
109+ LoggingLocalTime bool `flag:"logging-local-time" cfg:"logging_local_time"`
110+ LoggingCompress bool `flag:"logging-compress" cfg:"logging_compress"`
111+ StandardLogging bool `flag:"standard-logging" cfg:"standard_logging"`
112+ StandardLoggingFormat string `flag:"standard-logging-format" cfg:"standard_logging_format"`
113+ RequestLogging bool `flag:"request-logging" cfg:"request_logging"`
114+ RequestLoggingFormat string `flag:"request-logging-format" cfg:"request_logging_format"`
115+ ExcludeLoggingPaths string `flag:"exclude-logging-paths" cfg:"exclude_logging_paths"`
116+ SilencePingLogging bool `flag:"silence-ping-logging" cfg:"silence_ping_logging"`
117+ AuthLogging bool `flag:"auth-logging" cfg:"auth_logging"`
118+ AuthLoggingFormat string `flag:"auth-logging-format" cfg:"auth_logging_format"`
119+ SignatureKey string `flag:"signature-key" cfg:"signature_key"`
120+ AcrValues string `flag:"acr-values" cfg:"acr_values"`
121+ JWTKey string `flag:"jwt-key" cfg:"jwt_key"`
122+ JWTKeyFile string `flag:"jwt-key-file" cfg:"jwt_key_file"`
123+ PubJWKURL string `flag:"pubjwk-url" cfg:"pubjwk_url"`
124+ GCPHealthChecks bool `flag:"gcp-healthchecks" cfg:"gcp_healthchecks"`
125+
126+ // internal values that are set after config validation
127+ redirectURL *url.URL
128+ proxyURLs []*url.URL
129+ compiledRegex []*regexp.Regexp
130+ provider providers.Provider
131+ sessionStore sessionsapi.SessionStore
132+ signatureData *SignatureData
133+ oidcVerifier *oidc.IDTokenVerifier
134+ jwtBearerVerifiers []*oidc.IDTokenVerifier
135+ realClientIPParser ipapi.RealClientIPParser
136+}
137+
138+// Options for Getting internal values
139+func (o *Options) GetRedirectURL() *url.URL { return o.redirectURL }
140+func (o *Options) GetProxyURLs() []*url.URL { return o.proxyURLs }
141+func (o *Options) GetCompiledRegex() []*regexp.Regexp { return o.compiledRegex }
142+func (o *Options) GetProvider() providers.Provider { return o.provider }
143+func (o *Options) GetSessionStore() sessionsapi.SessionStore { return o.sessionStore }
144+func (o *Options) GetSignatureData() *SignatureData { return o.signatureData }
145+func (o *Options) GetOIDCVerifier() *oidc.IDTokenVerifier { return o.oidcVerifier }
146+func (o *Options) GetJWTBearerVerifiers() []*oidc.IDTokenVerifier { return o.jwtBearerVerifiers }
147+func (o *Options) GetRealClientIPParser() ipapi.RealClientIPParser { return o.realClientIPParser }
148+
149+// Options for Setting internal values
150+func (o *Options) SetRedirectURL(s *url.URL) { o.redirectURL = s }
151+func (o *Options) SetProxyURLs(s []*url.URL) { o.proxyURLs = s }
152+func (o *Options) SetCompiledRegex(s []*regexp.Regexp) { o.compiledRegex = s }
153+func (o *Options) SetProvider(s providers.Provider) { o.provider = s }
154+func (o *Options) SetSessionStore(s sessionsapi.SessionStore) { o.sessionStore = s }
155+func (o *Options) SetSignatureData(s *SignatureData) { o.signatureData = s }
156+func (o *Options) SetOIDCVerifier(s *oidc.IDTokenVerifier) { o.oidcVerifier = s }
157+func (o *Options) SetJWTBearerVerifiers(s []*oidc.IDTokenVerifier) { o.jwtBearerVerifiers = s }
158+func (o *Options) SetRealClientIPParser(s ipapi.RealClientIPParser) { o.realClientIPParser = s }
159+
160+// NewOptions constructs a new Options with defaulted values
161+func NewOptions() *Options {
162+ return &Options{
163+ ProxyPrefix: "/oauth2",
164+ ProviderType: "google",
165+ PingPath: "/ping",
166+ ProxyWebSockets: true,
167+ HTTPAddress: "127.0.0.1:4180",
168+ HTTPSAddress: ":443",
169+ RealClientIPHeader: "X-Real-IP",
170+ ForceHTTPS: false,
171+ DisplayHtpasswdForm: true,
172+ Cookie: CookieOptions{
173+ Name: "_oauth2_proxy",
174+ Secure: true,
175+ HTTPOnly: true,
176+ Expire: time.Duration(168) * time.Hour,
177+ Refresh: time.Duration(0),
178+ Path: "/",
179+ },
180+ Session: SessionOptions{
181+ Type: "cookie",
182+ },
183+ AzureTenant: "common",
184+ SetXAuthRequest: false,
185+ SkipAuthPreflight: false,
186+ FlushInterval: time.Duration(1) * time.Second,
187+ PassBasicAuth: true,
188+ SetBasicAuth: false,
189+ PassUserHeaders: true,
190+ PassAccessToken: false,
191+ PassHostHeader: true,
192+ SetAuthorization: false,
193+ PassAuthorization: false,
194+ PreferEmailToUser: false,
195+ Prompt: "", // Change to "login" when ApprovalPrompt officially deprecated
196+ ApprovalPrompt: "force",
197+ UserIDClaim: "email",
198+ InsecureOIDCAllowUnverifiedEmail: false,
199+ SkipOIDCDiscovery: false,
200+ LoggingFilename: "",
201+ LoggingMaxSize: 100,
202+ LoggingMaxAge: 7,
203+ LoggingMaxBackups: 0,
204+ LoggingLocalTime: true,
205+ LoggingCompress: false,
206+ ExcludeLoggingPaths: "",
207+ SilencePingLogging: false,
208+ StandardLogging: true,
209+ StandardLoggingFormat: logger.DefaultStandardLoggingFormat,
210+ RequestLogging: true,
211+ RequestLoggingFormat: logger.DefaultRequestLoggingFormat,
212+ AuthLogging: true,
213+ AuthLoggingFormat: logger.DefaultAuthLoggingFormat,
214+ }
215+}
216+
217+// NewFlagSet creates a new FlagSet with all of the flags required by Options
218+func NewFlagSet() *pflag.FlagSet {
219+ flagSet := pflag.NewFlagSet("oauth2-proxy", pflag.ExitOnError)
220+
221+ flagSet.String("http-address", "127.0.0.1:4180", "[http://]<addr>:<port> or unix://<path> to listen on for HTTP clients")
222+ flagSet.String("https-address", ":443", "<addr>:<port> to listen on for HTTPS clients")
223+ flagSet.Bool("reverse-proxy", false, "are we running behind a reverse proxy, controls whether headers like X-Real-Ip are accepted")
224+ flagSet.String("real-client-ip-header", "X-Real-IP", "Header used to determine the real IP of the client (one of: X-Forwarded-For, X-Real-IP, or X-ProxyUser-IP)")
225+ flagSet.Bool("force-https", false, "force HTTPS redirect for HTTP requests")
226+ flagSet.String("tls-cert-file", "", "path to certificate file")
227+ flagSet.String("tls-key-file", "", "path to private key file")
228+ flagSet.String("redirect-url", "", "the OAuth Redirect URL. ie: \"https://internalapp.yourcompany.com/oauth2/callback\"")
229+ flagSet.Bool("set-xauthrequest", false, "set X-Auth-Request-User and X-Auth-Request-Email response headers (useful in Nginx auth_request mode)")
230+ flagSet.StringSlice("upstream", []string{}, "the http url(s) of the upstream endpoint, file:// paths for static files or static://<status_code> for static response. Routing is based on the path")
231+ flagSet.Bool("pass-basic-auth", true, "pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream")
232+ flagSet.Bool("set-basic-auth", false, "set HTTP Basic Auth information in response (useful in Nginx auth_request mode)")
233+ flagSet.Bool("prefer-email-to-user", false, "Prefer to use the Email address as the Username when passing information to upstream. Will only use Username if Email is unavailable, eg. htaccess authentication. Used in conjunction with -pass-basic-auth and -pass-user-headers")
234+ flagSet.Bool("pass-user-headers", true, "pass X-Forwarded-User and X-Forwarded-Email information to upstream")
235+ flagSet.String("basic-auth-password", "", "the password to set when passing the HTTP Basic Auth header")
236+ flagSet.Bool("pass-access-token", false, "pass OAuth access_token to upstream via X-Forwarded-Access-Token header")
237+ flagSet.Bool("pass-host-header", true, "pass the request Host Header to upstream")
238+ flagSet.Bool("pass-authorization-header", false, "pass the Authorization Header to upstream")
239+ flagSet.Bool("set-authorization-header", false, "set Authorization response headers (useful in Nginx auth_request mode)")
240+ flagSet.StringSlice("skip-auth-regex", []string{}, "bypass authentication for requests path's that match (may be given multiple times)")
241+ flagSet.Bool("skip-provider-button", false, "will skip sign-in-page to directly reach the next step: oauth/start")
242+ flagSet.Bool("skip-auth-preflight", false, "will skip authentication for OPTIONS requests")
243+ flagSet.Bool("ssl-insecure-skip-verify", false, "skip validation of certificates presented when using HTTPS providers")
244+ flagSet.Bool("ssl-upstream-insecure-skip-verify", false, "skip validation of certificates presented when using HTTPS upstreams")
245+ flagSet.Duration("flush-interval", time.Duration(1)*time.Second, "period between response flushing when streaming responses")
246+ flagSet.Bool("skip-jwt-bearer-tokens", false, "will skip requests that have verified JWT bearer tokens (default false)")
247+ flagSet.StringSlice("extra-jwt-issuers", []string{}, "if skip-jwt-bearer-tokens is set, a list of extra JWT issuer=audience pairs (where the issuer URL has a .well-known/openid-configuration or a .well-known/jwks.json)")
248+
249+ flagSet.StringSlice("email-domain", []string{}, "authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email")
250+ flagSet.StringSlice("whitelist-domain", []string{}, "allowed domains for redirection after authentication. Prefix domain with a . to allow subdomains (eg .example.com)")
251+ flagSet.String("keycloak-group", "", "restrict login to members of this group.")
252+ flagSet.String("azure-tenant", "common", "go to a tenant-specific or common (tenant-independent) endpoint.")
253+ flagSet.String("bitbucket-team", "", "restrict logins to members of this team")
254+ flagSet.String("bitbucket-repository", "", "restrict logins to user with access to this repository")
255+ flagSet.String("github-org", "", "restrict logins to members of this organisation")
256+ flagSet.String("github-team", "", "restrict logins to members of this team")
257+ flagSet.String("github-repo", "", "restrict logins to collaborators of this repository")
258+ flagSet.String("github-token", "", "the token to use when verifying repository collaborators (must have push access to the repository)")
259+ flagSet.String("gitlab-group", "", "restrict logins to members of this group")
260+ flagSet.StringSlice("google-group", []string{}, "restrict logins to members of this google group (may be given multiple times).")
261+ flagSet.String("google-admin-email", "", "the google admin to impersonate for api calls")
262+ flagSet.String("google-service-account-json", "", "the path to the service account json credentials")
263+ flagSet.String("client-id", "", "the OAuth Client ID: ie: \"123456.apps.googleusercontent.com\"")
264+ flagSet.String("client-secret", "", "the OAuth Client Secret")
265+ flagSet.String("client-secret-file", "", "the file with OAuth Client Secret")
266+ flagSet.String("authenticated-emails-file", "", "authenticate against emails via file (one per line)")
267+ flagSet.String("htpasswd-file", "", "additionally authenticate against a htpasswd file. Entries must be created with \"htpasswd -s\" for SHA encryption or \"htpasswd -B\" for bcrypt encryption")
268+ flagSet.Bool("display-htpasswd-form", true, "display username / password login form if an htpasswd file is provided")
269+ flagSet.String("custom-templates-dir", "", "path to custom html templates")
270+ flagSet.String("banner", "", "custom banner string. Use \"-\" to disable default banner.")
271+ flagSet.String("footer", "", "custom footer string. Use \"-\" to disable default footer.")
272+ flagSet.String("proxy-prefix", "/oauth2", "the url root path that this proxy should be nested under (e.g. /<oauth2>/sign_in)")
273+ flagSet.String("ping-path", "/ping", "the ping endpoint that can be used for basic health checks")
274+ flagSet.Bool("proxy-websockets", true, "enables WebSocket proxying")
275+
276+ flagSet.String("cookie-name", "_oauth2_proxy", "the name of the cookie that the oauth_proxy creates")
277+ flagSet.String("cookie-secret", "", "the seed string for secure cookies (optionally base64 encoded)")
278+ flagSet.StringSlice("cookie-domain", []string{}, "Optional cookie domains to force cookies to (ie: `.yourcompany.com`). The longest domain matching the request's host will be used (or the shortest cookie domain if there is no match).")
279+ flagSet.String("cookie-path", "/", "an optional cookie path to force cookies to (ie: /poc/)*")
280+ flagSet.Duration("cookie-expire", time.Duration(168)*time.Hour, "expire timeframe for cookie")
281+ flagSet.Duration("cookie-refresh", time.Duration(0), "refresh the cookie after this duration; 0 to disable")
282+ flagSet.Bool("cookie-secure", true, "set secure (HTTPS) cookie flag")
283+ flagSet.Bool("cookie-httponly", true, "set HttpOnly cookie flag")
284+ flagSet.String("cookie-samesite", "", "set SameSite cookie attribute (ie: \"lax\", \"strict\", \"none\", or \"\"). ")
285+
286+ flagSet.String("session-store-type", "cookie", "the session storage provider to use")
287+ flagSet.String("redis-connection-url", "", "URL of redis server for redis session storage (eg: redis://HOST[:PORT])")
288+ flagSet.Bool("redis-use-sentinel", false, "Connect to redis via sentinels. Must set --redis-sentinel-master-name and --redis-sentinel-connection-urls to use this feature")
289+ flagSet.String("redis-sentinel-master-name", "", "Redis sentinel master name. Used in conjunction with --redis-use-sentinel")
290+ flagSet.String("redis-ca-path", "", "Redis custom CA path")
291+ flagSet.Bool("redis-insecure-skip-tls-verify", false, "Use insecure TLS connection to redis")
292+ flagSet.StringSlice("redis-sentinel-connection-urls", []string{}, "List of Redis sentinel connection URLs (eg redis://HOST[:PORT]). Used in conjunction with --redis-use-sentinel")
293+ flagSet.Bool("redis-use-cluster", false, "Connect to redis cluster. Must set --redis-cluster-connection-urls to use this feature")
294+ flagSet.StringSlice("redis-cluster-connection-urls", []string{}, "List of Redis cluster connection URLs (eg redis://HOST[:PORT]). Used in conjunction with --redis-use-cluster")
295+
296+ flagSet.String("logging-filename", "", "File to log requests to, empty for stdout")
297+ flagSet.Int("logging-max-size", 100, "Maximum size in megabytes of the log file before rotation")
298+ flagSet.Int("logging-max-age", 7, "Maximum number of days to retain old log files")
299+ flagSet.Int("logging-max-backups", 0, "Maximum number of old log files to retain; 0 to disable")
300+ flagSet.Bool("logging-local-time", true, "If the time in log files and backup filenames are local or UTC time")
301+ flagSet.Bool("logging-compress", false, "Should rotated log files be compressed using gzip")
302+
303+ flagSet.Bool("standard-logging", true, "Log standard runtime information")
304+ flagSet.String("standard-logging-format", logger.DefaultStandardLoggingFormat, "Template for standard log lines")
305+
306+ flagSet.Bool("request-logging", true, "Log HTTP requests")
307+ flagSet.String("request-logging-format", logger.DefaultRequestLoggingFormat, "Template for HTTP request log lines")
308+ flagSet.String("exclude-logging-paths", "", "Exclude logging requests to paths (eg: '/path1,/path2,/path3')")
309+ flagSet.Bool("silence-ping-logging", false, "Disable logging of requests to ping endpoint")
310+
311+ flagSet.Bool("auth-logging", true, "Log authentication attempts")
312+ flagSet.String("auth-logging-format", logger.DefaultAuthLoggingFormat, "Template for authentication log lines")
313+
314+ flagSet.String("provider", "google", "OAuth provider")
315+ flagSet.String("provider-display-name", "", "Provider display name")
316+ flagSet.String("oidc-issuer-url", "", "OpenID Connect issuer URL (ie: https://accounts.google.com)")
317+ flagSet.Bool("insecure-oidc-allow-unverified-email", false, "Don't fail if an email address in an id_token is not verified")
318+ flagSet.Bool("insecure-oidc-skip-issuer-verification", false, "Do not verify if issuer matches OIDC discovery URL")
319+ flagSet.Bool("skip-oidc-discovery", false, "Skip OIDC discovery and use manually supplied Endpoints")
320+ flagSet.String("oidc-jwks-url", "", "OpenID Connect JWKS URL (ie: https://www.googleapis.com/oauth2/v3/certs)")
321+ flagSet.String("login-url", "", "Authentication endpoint")
322+ flagSet.String("redeem-url", "", "Token redemption endpoint")
323+ flagSet.String("profile-url", "", "Profile access endpoint")
324+ flagSet.String("resource", "", "The resource that is protected (Azure AD only)")
325+ flagSet.String("validate-url", "", "Access token validation endpoint")
326+ flagSet.String("scope", "", "OAuth scope specification")
327+ flagSet.String("prompt", "", "OIDC prompt")
328+ flagSet.String("approval-prompt", "force", "OAuth approval_prompt")
329+
330+ flagSet.String("signature-key", "", "GAP-Signature request signature key (algorithm:secretkey)")
331+ flagSet.String("acr-values", "", "acr values string: optional")
332+ flagSet.String("jwt-key", "", "private key in PEM format used to sign JWT, so that you can say something like -jwt-key=\"${OAUTH2_PROXY_JWT_KEY}\": required by login.gov")
333+ flagSet.String("jwt-key-file", "", "path to the private key file in PEM format used to sign the JWT so that you can say something like -jwt-key-file=/etc/ssl/private/jwt_signing_key.pem: required by login.gov")
334+ flagSet.String("pubjwk-url", "", "JWK pubkey access endpoint: required by login.gov")
335+ flagSet.Bool("gcp-healthchecks", false, "Enable GCP/GKE healthcheck endpoints")
336+
337+ flagSet.String("user-id-claim", "email", "which claim contains the user ID")
338+
339+ return flagSet
340+}
pkg/apis/options/sessions.go+9−9
@@ -4,7 +4,7 @@ import "github.com/oauth2-proxy/oauth2-proxy/pkg/encryption"
44
55// SessionOptions contains configuration options for the SessionStore providers.
66type SessionOptions struct {
7− Type string `flag:"session-store-type" cfg:"session_store_type" env:"OAUTH2_PROXY_SESSION_STORE_TYPE"`
7+ Type string `flag:"session-store-type" cfg:"session_store_type"`
88 Cipher *encryption.Cipher `cfg:",internal"`
99 Redis RedisStoreOptions `cfg:",squash"`
1010}
@@ -19,12 +19,12 @@ var RedisSessionStoreType = "redis"
1919
2020// RedisStoreOptions contains configuration options for the RedisSessionStore.
2121type RedisStoreOptions struct {
22− ConnectionURL string `flag:"redis-connection-url" cfg:"redis_connection_url" env:"OAUTH2_PROXY_REDIS_CONNECTION_URL"`
23− UseSentinel bool `flag:"redis-use-sentinel" cfg:"redis_use_sentinel" env:"OAUTH2_PROXY_REDIS_USE_SENTINEL"`
24− SentinelMasterName string `flag:"redis-sentinel-master-name" cfg:"redis_sentinel_master_name" env:"OAUTH2_PROXY_REDIS_SENTINEL_MASTER_NAME"`
25− SentinelConnectionURLs []string `flag:"redis-sentinel-connection-urls" cfg:"redis_sentinel_connection_urls" env:"OAUTH2_PROXY_REDIS_SENTINEL_CONNECTION_URLS"`
26− UseCluster bool `flag:"redis-use-cluster" cfg:"redis_use_cluster" env:"OAUTH2_PROXY_REDIS_USE_CLUSTER"`
27− ClusterConnectionURLs []string `flag:"redis-cluster-connection-urls" cfg:"redis_cluster_connection_urls" env:"OAUTH2_PROXY_REDIS_CLUSTER_CONNECTION_URLS"`
28− CAPath string `flag:"redis-ca-path" cfg:"redis_ca_path" env:"OAUTH2_PROXY_REDIS_CA_PATH"`
29− InsecureSkipTLSVerify bool `flag:"redis-insecure-skip-tls-verify" cfg:"redis_insecure_skip_tls_verify" env:"OAUTH2_PROXY_REDIS_INSECURE_SKIP_TLS_VERIFY"`
22+ ConnectionURL string `flag:"redis-connection-url" cfg:"redis_connection_url"`
23+ UseSentinel bool `flag:"redis-use-sentinel" cfg:"redis_use_sentinel"`
24+ SentinelMasterName string `flag:"redis-sentinel-master-name" cfg:"redis_sentinel_master_name"`
25+ SentinelConnectionURLs []string `flag:"redis-sentinel-connection-urls" cfg:"redis_sentinel_connection_urls"`
26+ UseCluster bool `flag:"redis-use-cluster" cfg:"redis_use_cluster"`
27+ ClusterConnectionURLs []string `flag:"redis-cluster-connection-urls" cfg:"redis_cluster_connection_urls"`
28+ CAPath string `flag:"redis-ca-path" cfg:"redis_ca_path"`
29+ InsecureSkipTLSVerify bool `flag:"redis-insecure-skip-tls-verify" cfg:"redis_insecure_skip_tls_verify"`
3030}
realclientip.go → pkg/ip/realclientip.gorenamed+6−15
@@ -1,4 +1,4 @@
1−package main
1+package ip
22
33import (
44 "fmt"
@@ -6,14 +6,10 @@ import (
66 "net/http"
77 "strings"
88
9− "github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
9+ ipapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/ip"
1010)
1111
12−type realClientIPParser interface {
13− GetRealClientIP(http.Header) (net.IP, error)
14−}
15−
16−func getRealClientIPParser(headerKey string) (realClientIPParser, error) {
12+func GetRealClientIPParser(headerKey string) (ipapi.RealClientIPParser, error) {
1713 headerKey = http.CanonicalHeaderKey(headerKey)
1814
1915 switch headerKey {
@@ -73,13 +69,11 @@ func getRemoteIP(req *http.Request) (net.IP, error) {
7369 }
7470}
7571
76−// getClientString obtains the human readable string of the remote IP and optionally the real client IP if available
77−func getClientString(p realClientIPParser, req *http.Request, full bool) (s string) {
72+// GetClientString obtains the human readable string of the remote IP and optionally the real client IP if available
73+func GetClientString(p ipapi.RealClientIPParser, req *http.Request, full bool) (s string) {
7874 var realClientIPStr string
7975 if p != nil {
80− if realClientIP, err := p.GetRealClientIP(req.Header); err != nil {
81− logger.Printf("Unable to get real client IP: %v", err)
82− } else if realClientIP != nil {
76+ if realClientIP, err := p.GetRealClientIP(req.Header); err == nil && realClientIP != nil {
8377 realClientIPStr = realClientIP.String()
8478 }
8579 }
@@ -87,9 +81,6 @@ func getClientString(p realClientIPParser, req *http.Request, full bool) (s stri
8781 var remoteIPStr string
8882 if remoteIP, err := getRemoteIP(req); err == nil {
8983 remoteIPStr = remoteIP.String()
90− } else {
91− // Should not happen, if it does, likely a bug.
92− logger.Printf("Unable to get remote IP(?!?!): %v", err)
9384 }
9485
9586 if !full && realClientIPStr != "" {
realclientip_test.go → pkg/ip/realclientip_test.gorenamed+6−5
@@ -1,4 +1,4 @@
1−package main
1+package ip
22
33import (
44 "net"
@@ -6,6 +6,7 @@ import (
66 "reflect"
77 "testing"
88
9+ ipapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/ip"
910 "github.com/stretchr/testify/assert"
1011)
1112
@@ -26,7 +27,7 @@ func TestGetRealClientIPParser(t *testing.T) {
2627 }
2728
2829 for _, test := range tests {
29− p, err := getRealClientIPParser(test.header)
30+ p, err := GetRealClientIPParser(test.header)
3031
3132 if test.errString == "" {
3233 assert.Nil(t, err)
@@ -144,7 +145,7 @@ func TestGetClientString(t *testing.T) {
144145 p := &xForwardedForClientIPParser{header: http.CanonicalHeaderKey("X-Forwarded-For")}
145146
146147 tests := []struct {
147− parser realClientIPParser
148+ parser ipapi.RealClientIPParser
148149 remoteAddr string
149150 headerValue string
150151 expectedClient string
@@ -167,10 +168,10 @@ func TestGetClientString(t *testing.T) {
167168 RemoteAddr: test.remoteAddr,
168169 }
169170
170− client := getClientString(test.parser, req, false)
171+ client := GetClientString(test.parser, req, false)
171172 assert.Equal(t, test.expectedClient, client)
172173
173− clientFull := getClientString(test.parser, req, true)
174+ clientFull := GetClientString(test.parser, req, true)
174175 assert.Equal(t, test.expectedClientFull, clientFull)
175176 }
176177}
options.go → pkg/validation/options.gorenamed+47−217
@@ -1,4 +1,4 @@
1−package main
1+package validation
22
33import (
44 "context"
@@ -14,12 +14,12 @@ import (
1414 "strings"
1515 "time"
1616
17− oidc "github.com/coreos/go-oidc"
17+ "github.com/coreos/go-oidc"
1818 "github.com/dgrijalva/jwt-go"
1919 "github.com/mbland/hmacauth"
2020 "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
21− sessionsapi "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions"
2221 "github.com/oauth2-proxy/oauth2-proxy/pkg/encryption"
22+ "github.com/oauth2-proxy/oauth2-proxy/pkg/ip"
2323 "github.com/oauth2-proxy/oauth2-proxy/pkg/logger"
2424 "github.com/oauth2-proxy/oauth2-proxy/pkg/requests"
2525 "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions"
@@ -27,197 +27,9 @@ import (
2727 "gopkg.in/natefinch/lumberjack.v2"
2828)
2929
30−// Options holds Configuration Options that can be set by Command Line Flag,
31−// or Config File
32−type Options struct {
33− ProxyPrefix string `flag:"proxy-prefix" cfg:"proxy_prefix" env:"OAUTH2_PROXY_PROXY_PREFIX"`
34− PingPath string `flag:"ping-path" cfg:"ping_path" env:"OAUTH2_PROXY_PING_PATH"`
35− ProxyWebSockets bool `flag:"proxy-websockets" cfg:"proxy_websockets" env:"OAUTH2_PROXY_PROXY_WEBSOCKETS"`
36− HTTPAddress string `flag:"http-address" cfg:"http_address" env:"OAUTH2_PROXY_HTTP_ADDRESS"`
37− HTTPSAddress string `flag:"https-address" cfg:"https_address" env:"OAUTH2_PROXY_HTTPS_ADDRESS"`
38− ReverseProxy bool `flag:"reverse-proxy" cfg:"reverse_proxy" env:"OAUTH2_PROXY_REVERSE_PROXY"`
39− RealClientIPHeader string `flag:"real-client-ip-header" cfg:"real_client_ip_header" env:"OAUTH2_PROXY_REAL_CLIENT_IP_HEADER"`
40− ForceHTTPS bool `flag:"force-https" cfg:"force_https" env:"OAUTH2_PROXY_FORCE_HTTPS"`
41− RedirectURL string `flag:"redirect-url" cfg:"redirect_url" env:"OAUTH2_PROXY_REDIRECT_URL"`
42− ClientID string `flag:"client-id" cfg:"client_id" env:"OAUTH2_PROXY_CLIENT_ID"`
43− ClientSecret string `flag:"client-secret" cfg:"client_secret" env:"OAUTH2_PROXY_CLIENT_SECRET"`
44− ClientSecretFile string `flag:"client-secret-file" cfg:"client_secret_file" env:"OAUTH2_PROXY_CLIENT_SECRET_FILE"`
45− TLSCertFile string `flag:"tls-cert-file" cfg:"tls_cert_file" env:"OAUTH2_PROXY_TLS_CERT_FILE"`
46− TLSKeyFile string `flag:"tls-key-file" cfg:"tls_key_file" env:"OAUTH2_PROXY_TLS_KEY_FILE"`
47−
48− AuthenticatedEmailsFile string `flag:"authenticated-emails-file" cfg:"authenticated_emails_file" env:"OAUTH2_PROXY_AUTHENTICATED_EMAILS_FILE"`
49− KeycloakGroup string `flag:"keycloak-group" cfg:"keycloak_group" env:"OAUTH2_PROXY_KEYCLOAK_GROUP"`
50− AzureTenant string `flag:"azure-tenant" cfg:"azure_tenant" env:"OAUTH2_PROXY_AZURE_TENANT"`
51− BitbucketTeam string `flag:"bitbucket-team" cfg:"bitbucket_team" env:"OAUTH2_PROXY_BITBUCKET_TEAM"`
52− BitbucketRepository string `flag:"bitbucket-repository" cfg:"bitbucket_repository" env:"OAUTH2_PROXY_BITBUCKET_REPOSITORY"`
53− EmailDomains []string `flag:"email-domain" cfg:"email_domains" env:"OAUTH2_PROXY_EMAIL_DOMAINS"`
54− WhitelistDomains []string `flag:"whitelist-domain" cfg:"whitelist_domains" env:"OAUTH2_PROXY_WHITELIST_DOMAINS"`
55− GitHubOrg string `flag:"github-org" cfg:"github_org" env:"OAUTH2_PROXY_GITHUB_ORG"`
56− GitHubTeam string `flag:"github-team" cfg:"github_team" env:"OAUTH2_PROXY_GITHUB_TEAM"`
57− GitHubRepo string `flag:"github-repo" cfg:"github_repo" env:"OAUTH2_PROXY_GITHUB_REPO"`
58− GitHubToken string `flag:"github-token" cfg:"github_token" env:"OAUTH2_PROXY_GITHUB_TOKEN"`
59− GitLabGroup string `flag:"gitlab-group" cfg:"gitlab_group" env:"OAUTH2_PROXY_GITLAB_GROUP"`
60− GoogleGroups []string `flag:"google-group" cfg:"google_group" env:"OAUTH2_PROXY_GOOGLE_GROUPS"`
61− GoogleAdminEmail string `flag:"google-admin-email" cfg:"google_admin_email" env:"OAUTH2_PROXY_GOOGLE_ADMIN_EMAIL"`
62− GoogleServiceAccountJSON string `flag:"google-service-account-json" cfg:"google_service_account_json" env:"OAUTH2_PROXY_GOOGLE_SERVICE_ACCOUNT_JSON"`
63− HtpasswdFile string `flag:"htpasswd-file" cfg:"htpasswd_file" env:"OAUTH2_PROXY_HTPASSWD_FILE"`
64− DisplayHtpasswdForm bool `flag:"display-htpasswd-form" cfg:"display_htpasswd_form" env:"OAUTH2_PROXY_DISPLAY_HTPASSWD_FORM"`
65− CustomTemplatesDir string `flag:"custom-templates-dir" cfg:"custom_templates_dir" env:"OAUTH2_PROXY_CUSTOM_TEMPLATES_DIR"`
66− Banner string `flag:"banner" cfg:"banner" env:"OAUTH2_PROXY_BANNER"`
67− Footer string `flag:"footer" cfg:"footer" env:"OAUTH2_PROXY_FOOTER"`
68−
69− Cookie options.CookieOptions `cfg:",squash"`
70− Session options.SessionOptions `cfg:",squash"`
71−
72− Upstreams []string `flag:"upstream" cfg:"upstreams" env:"OAUTH2_PROXY_UPSTREAMS"`
73− SkipAuthRegex []string `flag:"skip-auth-regex" cfg:"skip_auth_regex" env:"OAUTH2_PROXY_SKIP_AUTH_REGEX"`
74− SkipJwtBearerTokens bool `flag:"skip-jwt-bearer-tokens" cfg:"skip_jwt_bearer_tokens" env:"OAUTH2_PROXY_SKIP_JWT_BEARER_TOKENS"`
75− ExtraJwtIssuers []string `flag:"extra-jwt-issuers" cfg:"extra_jwt_issuers" env:"OAUTH2_PROXY_EXTRA_JWT_ISSUERS"`
76− PassBasicAuth bool `flag:"pass-basic-auth" cfg:"pass_basic_auth" env:"OAUTH2_PROXY_PASS_BASIC_AUTH"`
77− SetBasicAuth bool `flag:"set-basic-auth" cfg:"set_basic_auth" env:"OAUTH2_PROXY_SET_BASIC_AUTH"`
78− PreferEmailToUser bool `flag:"prefer-email-to-user" cfg:"prefer_email_to_user" env:"OAUTH2_PROXY_PREFER_EMAIL_TO_USER"`
79− BasicAuthPassword string `flag:"basic-auth-password" cfg:"basic_auth_password" env:"OAUTH2_PROXY_BASIC_AUTH_PASSWORD"`
80− PassAccessToken bool `flag:"pass-access-token" cfg:"pass_access_token" env:"OAUTH2_PROXY_PASS_ACCESS_TOKEN"`
81− PassHostHeader bool `flag:"pass-host-header" cfg:"pass_host_header" env:"OAUTH2_PROXY_PASS_HOST_HEADER"`
82− SkipProviderButton bool `flag:"skip-provider-button" cfg:"skip_provider_button" env:"OAUTH2_PROXY_SKIP_PROVIDER_BUTTON"`
83− PassUserHeaders bool `flag:"pass-user-headers" cfg:"pass_user_headers" env:"OAUTH2_PROXY_PASS_USER_HEADERS"`
84− SSLInsecureSkipVerify bool `flag:"ssl-insecure-skip-verify" cfg:"ssl_insecure_skip_verify" env:"OAUTH2_PROXY_SSL_INSECURE_SKIP_VERIFY"`
85− SSLUpstreamInsecureSkipVerify bool `flag:"ssl-upstream-insecure-skip-verify" cfg:"ssl_upstream_insecure_skip_verify" env:"OAUTH2_PROXY_SSL_UPSTREAM_INSECURE_SKIP_VERIFY"`
86− SetXAuthRequest bool `flag:"set-xauthrequest" cfg:"set_xauthrequest" env:"OAUTH2_PROXY_SET_XAUTHREQUEST"`
87− SetAuthorization bool `flag:"set-authorization-header" cfg:"set_authorization_header" env:"OAUTH2_PROXY_SET_AUTHORIZATION_HEADER"`
88− PassAuthorization bool `flag:"pass-authorization-header" cfg:"pass_authorization_header" env:"OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER"`
89− SkipAuthPreflight bool `flag:"skip-auth-preflight" cfg:"skip_auth_preflight" env:"OAUTH2_PROXY_SKIP_AUTH_PREFLIGHT"`
90− FlushInterval time.Duration `flag:"flush-interval" cfg:"flush_interval" env:"OAUTH2_PROXY_FLUSH_INTERVAL"`
91−
92− // These options allow for other providers besides Google, with
93− // potential overrides.
94− Provider string `flag:"provider" cfg:"provider" env:"OAUTH2_PROXY_PROVIDER"`
95− ProviderName string `flag:"provider-display-name" cfg:"provider_display_name" env:"OAUTH2_PROXY_PROVIDER_DISPLAY_NAME"`
96− OIDCIssuerURL string `flag:"oidc-issuer-url" cfg:"oidc_issuer_url" env:"OAUTH2_PROXY_OIDC_ISSUER_URL"`
97− InsecureOIDCAllowUnverifiedEmail bool `flag:"insecure-oidc-allow-unverified-email" cfg:"insecure_oidc_allow_unverified_email" env:"OAUTH2_PROXY_INSECURE_OIDC_ALLOW_UNVERIFIED_EMAIL"`
98− InsecureOIDCSkipIssuerVerification bool `flag:"insecure-oidc-skip-issuer-verification" cfg:"insecure_oidc_skip_issuer_verification" env:"OAUTH2_PROXY_INSECURE_OIDC_SKIP_ISSUER_VERIFICATION"`
99− SkipOIDCDiscovery bool `flag:"skip-oidc-discovery" cfg:"skip_oidc_discovery" env:"OAUTH2_PROXY_SKIP_OIDC_DISCOVERY"`
100− OIDCJwksURL string `flag:"oidc-jwks-url" cfg:"oidc_jwks_url" env:"OAUTH2_PROXY_OIDC_JWKS_URL"`
101− LoginURL string `flag:"login-url" cfg:"login_url" env:"OAUTH2_PROXY_LOGIN_URL"`
102− RedeemURL string `flag:"redeem-url" cfg:"redeem_url" env:"OAUTH2_PROXY_REDEEM_URL"`
103− ProfileURL string `flag:"profile-url" cfg:"profile_url" env:"OAUTH2_PROXY_PROFILE_URL"`
104− ProtectedResource string `flag:"resource" cfg:"resource" env:"OAUTH2_PROXY_RESOURCE"`
105− ValidateURL string `flag:"validate-url" cfg:"validate_url" env:"OAUTH2_PROXY_VALIDATE_URL"`
106− Scope string `flag:"scope" cfg:"scope" env:"OAUTH2_PROXY_SCOPE"`
107− Prompt string `flag:"prompt" cfg:"prompt" env:"OAUTH2_PROXY_PROMPT"`
108− ApprovalPrompt string `flag:"approval-prompt" cfg:"approval_prompt" env:"OAUTH2_PROXY_APPROVAL_PROMPT"` // Deprecated by OIDC 1.0
109− UserIDClaim string `flag:"user-id-claim" cfg:"user_id_claim" env:"OAUTH2_PROXY_USER_ID_CLAIM"`
110−
111− // Configuration values for logging
112− LoggingFilename string `flag:"logging-filename" cfg:"logging_filename" env:"OAUTH2_PROXY_LOGGING_FILENAME"`
113− LoggingMaxSize int `flag:"logging-max-size" cfg:"logging_max_size" env:"OAUTH2_PROXY_LOGGING_MAX_SIZE"`
114− LoggingMaxAge int `flag:"logging-max-age" cfg:"logging_max_age" env:"OAUTH2_PROXY_LOGGING_MAX_AGE"`
115− LoggingMaxBackups int `flag:"logging-max-backups" cfg:"logging_max_backups" env:"OAUTH2_PROXY_LOGGING_MAX_BACKUPS"`
116− LoggingLocalTime bool `flag:"logging-local-time" cfg:"logging_local_time" env:"OAUTH2_PROXY_LOGGING_LOCAL_TIME"`
117− LoggingCompress bool `flag:"logging-compress" cfg:"logging_compress" env:"OAUTH2_PROXY_LOGGING_COMPRESS"`
118− StandardLogging bool `flag:"standard-logging" cfg:"standard_logging" env:"OAUTH2_PROXY_STANDARD_LOGGING"`
119− StandardLoggingFormat string `flag:"standard-logging-format" cfg:"standard_logging_format" env:"OAUTH2_PROXY_STANDARD_LOGGING_FORMAT"`
120− RequestLogging bool `flag:"request-logging" cfg:"request_logging" env:"OAUTH2_PROXY_REQUEST_LOGGING"`
121− RequestLoggingFormat string `flag:"request-logging-format" cfg:"request_logging_format" env:"OAUTH2_PROXY_REQUEST_LOGGING_FORMAT"`
122− ExcludeLoggingPaths string `flag:"exclude-logging-paths" cfg:"exclude_logging_paths" env:"OAUTH2_PROXY_EXCLUDE_LOGGING_PATHS"`
123− SilencePingLogging bool `flag:"silence-ping-logging" cfg:"silence_ping_logging" env:"OAUTH2_PROXY_SILENCE_PING_LOGGING"`
124− AuthLogging bool `flag:"auth-logging" cfg:"auth_logging" env:"OAUTH2_PROXY_LOGGING_AUTH_LOGGING"`
125− AuthLoggingFormat string `flag:"auth-logging-format" cfg:"auth_logging_format" env:"OAUTH2_PROXY_AUTH_LOGGING_FORMAT"`
126− SignatureKey string `flag:"signature-key" cfg:"signature_key" env:"OAUTH2_PROXY_SIGNATURE_KEY"`
127− AcrValues string `flag:"acr-values" cfg:"acr_values" env:"OAUTH2_PROXY_ACR_VALUES"`
128− JWTKey string `flag:"jwt-key" cfg:"jwt_key" env:"OAUTH2_PROXY_JWT_KEY"`
129− JWTKeyFile string `flag:"jwt-key-file" cfg:"jwt_key_file" env:"OAUTH2_PROXY_JWT_KEY_FILE"`
130− PubJWKURL string `flag:"pubjwk-url" cfg:"pubjwk_url" env:"OAUTH2_PROXY_PUBJWK_URL"`
131− GCPHealthChecks bool `flag:"gcp-healthchecks" cfg:"gcp_healthchecks" env:"OAUTH2_PROXY_GCP_HEALTHCHECKS"`
132−
133− // internal values that are set after config validation
134− redirectURL *url.URL
135− proxyURLs []*url.URL
136− compiledRegex []*regexp.Regexp
137− provider providers.Provider
138− sessionStore sessionsapi.SessionStore
139− signatureData *SignatureData
140− oidcVerifier *oidc.IDTokenVerifier
141− jwtBearerVerifiers []*oidc.IDTokenVerifier
142− realClientIPParser realClientIPParser
143−}
144−
145−// SignatureData holds hmacauth signature hash and key
146−type SignatureData struct {
147− hash crypto.Hash
148− key string
149−}
150−
151−// NewOptions constructs a new Options with defaulted values
152−func NewOptions() *Options {
153− return &Options{
154− ProxyPrefix: "/oauth2",
155− PingPath: "/ping",
156− ProxyWebSockets: true,
157− HTTPAddress: "127.0.0.1:4180",
158− HTTPSAddress: ":443",
159− ForceHTTPS: false,
160− DisplayHtpasswdForm: true,
161− Cookie: options.CookieOptions{
162− Name: "_oauth2_proxy",
163− Secure: true,
164− HTTPOnly: true,
165− Expire: time.Duration(168) * time.Hour,
166− Refresh: time.Duration(0),
167− },
168− Session: options.SessionOptions{
169− Type: "cookie",
170− },
171− SetXAuthRequest: false,
172− SkipAuthPreflight: false,
173− PassBasicAuth: true,
174− SetBasicAuth: false,
175− PassUserHeaders: true,
176− PassAccessToken: false,
177− PassHostHeader: true,
178− SetAuthorization: false,
179− PassAuthorization: false,
180− PreferEmailToUser: false,
181− Prompt: "", // Change to "login" when ApprovalPrompt officially deprecated
182− ApprovalPrompt: "force",
183− UserIDClaim: "email",
184− InsecureOIDCAllowUnverifiedEmail: false,
185− SkipOIDCDiscovery: false,
186− LoggingFilename: "",
187− LoggingMaxSize: 100,
188− LoggingMaxAge: 7,
189− LoggingMaxBackups: 0,
190− LoggingLocalTime: true,
191− LoggingCompress: false,
192− ExcludeLoggingPaths: "",
193− SilencePingLogging: false,
194− StandardLogging: true,
195− StandardLoggingFormat: logger.DefaultStandardLoggingFormat,
196− RequestLogging: true,
197− RequestLoggingFormat: logger.DefaultRequestLoggingFormat,
198− AuthLogging: true,
199− AuthLoggingFormat: logger.DefaultAuthLoggingFormat,
200− }
201−}
202−
203−// jwtIssuer hold parsed JWT issuer info that's used to construct a verifier.
204−type jwtIssuer struct {
205− issuerURI string
206− audience string
207−}
208−
209−func parseURL(toParse string, urltype string, msgs []string) (*url.URL, []string) {
210− parsed, err := url.Parse(toParse)
211− if err != nil {
212− return nil, append(msgs, fmt.Sprintf(
213− "error parsing %s-url=%q %s", urltype, toParse, err))
214− }
215− return parsed, msgs
216−}
217−
21830// Validate checks that required options are set and validates those that they
21931// are of the correct format
220−func (o *Options) Validate() error {
32+func Validate(o *options.Options) error {
22133 if o.SSLInsecureSkipVerify {
22234 // TODO: Accept a certificate bundle.
22335 insecureTransport := &http.Transport{
@@ -234,7 +46,7 @@ func (o *Options) Validate() error {
23446 msgs = append(msgs, "missing setting: client-id")
23547 }
23648 // login.gov uses a signed JWT to authenticate, not a client-secret
237− if o.Provider != "login.gov" {
49+ if o.ProviderType != "login.gov" {
23850 if o.ClientSecret == "" && o.ClientSecretFile == "" {
23951 msgs = append(msgs, "missing setting: client-secret or client-secret-file")
24052 }
@@ -311,20 +123,20 @@ func (o *Options) Validate() error {
311123 msgs = append(msgs, "missing setting: oidc-jwks-url")
312124 }
313125 keySet := oidc.NewRemoteKeySet(ctx, o.OIDCJwksURL)
314− o.oidcVerifier = oidc.NewVerifier(o.OIDCIssuerURL, keySet, &oidc.Config{
126+ o.SetOIDCVerifier(oidc.NewVerifier(o.OIDCIssuerURL, keySet, &oidc.Config{
315127 ClientID: o.ClientID,
316128 SkipIssuerCheck: o.InsecureOIDCSkipIssuerVerification,
317− })
129+ }))
318130 } else {
319131 // Configure discoverable provider data.
320132 provider, err := oidc.NewProvider(ctx, o.OIDCIssuerURL)
321133 if err != nil {
322134 return err
323135 }
324− o.oidcVerifier = provider.Verifier(&oidc.Config{
136+ o.SetOIDCVerifier(provider.Verifier(&oidc.Config{
325137 ClientID: o.ClientID,
326138 SkipIssuerCheck: o.InsecureOIDCSkipIssuerVerification,
327− })
139+ }))
328140
329141 o.LoginURL = provider.Endpoint().AuthURL
330142 o.RedeemURL = provider.Endpoint().TokenURL
@@ -340,8 +152,8 @@ func (o *Options) Validate() error {
340152
341153 if o.SkipJwtBearerTokens {
342154 // If we are using an oidc provider, go ahead and add that provider to the list
343− if o.oidcVerifier != nil {
344− o.jwtBearerVerifiers = append(o.jwtBearerVerifiers, o.oidcVerifier)
155+ if o.GetOIDCVerifier() != nil {
156+ o.SetJWTBearerVerifiers(append(o.GetJWTBearerVerifiers(), o.GetOIDCVerifier()))
345157 }
346158 // Configure extra issuers
347159 if len(o.ExtraJwtIssuers) > 0 {
@@ -352,12 +164,14 @@ func (o *Options) Validate() error {
352164 if err != nil {
353165 msgs = append(msgs, fmt.Sprintf("error building verifiers: %s", err))
354166 }
355− o.jwtBearerVerifiers = append(o.jwtBearerVerifiers, verifier)
167+ o.SetJWTBearerVerifiers(append(o.GetJWTBearerVerifiers(), verifier))
356168 }
357169 }
358170 }
359171
360− o.redirectURL, msgs = parseURL(o.RedirectURL, "redirect", msgs)
172+ var redirectURL *url.URL
173+ redirectURL, msgs = parseURL(o.RawRedirectURL, "redirect", msgs)
174+ o.SetRedirectURL(redirectURL)
361175
362176 for _, u := range o.Upstreams {
363177 upstreamURL, err := url.Parse(u)
@@ -367,7 +181,7 @@ func (o *Options) Validate() error {
367181 if upstreamURL.Path == "" {
368182 upstreamURL.Path = "/"
369183 }
370− o.proxyURLs = append(o.proxyURLs, upstreamURL)
184+ o.SetProxyURLs(append(o.GetProxyURLs(), upstreamURL))
371185 }
372186 }
373187
@@ -377,7 +191,7 @@ func (o *Options) Validate() error {
377191 msgs = append(msgs, fmt.Sprintf("error compiling regex=%q %s", u, err))
378192 continue
379193 }
380− o.compiledRegex = append(o.compiledRegex, compiledRegex)
194+ o.SetCompiledRegex(append(o.GetCompiledRegex(), compiledRegex))
381195 }
382196 msgs = parseProviderInfo(o, msgs)
383197
@@ -418,7 +232,7 @@ func (o *Options) Validate() error {
418232 if err != nil {
419233 msgs = append(msgs, fmt.Sprintf("error initialising session storage: %v", err))
420234 } else {
421− o.sessionStore = sessionStore
235+ o.SetSessionStore(sessionStore)
422236 }
423237
424238 if o.Cookie.Refresh >= o.Cookie.Expire {
@@ -458,10 +272,11 @@ func (o *Options) Validate() error {
458272 msgs = setupLogger(o, msgs)
459273
460274 if o.ReverseProxy {
461− o.realClientIPParser, err = getRealClientIPParser(o.RealClientIPHeader)
275+ parser, err := ip.GetRealClientIPParser(o.RealClientIPHeader)
462276 if err != nil {
463277 msgs = append(msgs, fmt.Sprintf("real_client_ip_header (%s) not accepted parameter value: %v", o.RealClientIPHeader, err))
464278 }
279+ o.SetRealClientIPParser(parser)
465280 }
466281
467282 if len(msgs) != 0 {
@@ -471,7 +286,7 @@ func (o *Options) Validate() error {
471286 return nil
472287}
473288
474−func parseProviderInfo(o *Options, msgs []string) []string {
289+func parseProviderInfo(o *options.Options, msgs []string) []string {
475290 p := &providers.ProviderData{
476291 Scope: o.Scope,
477292 ClientID: o.ClientID,
@@ -487,8 +302,8 @@ func parseProviderInfo(o *Options, msgs []string) []string {
487302 p.ValidateURL, msgs = parseURL(o.ValidateURL, "validate", msgs)
488303 p.ProtectedResource, msgs = parseURL(o.ProtectedResource, "resource", msgs)
489304
490− o.provider = providers.New(o.Provider, p)
491− switch p := o.provider.(type) {
305+ o.SetProvider(providers.New(o.ProviderType, p))
306+ switch p := o.GetProvider().(type) {
492307 case *providers.AzureProvider:
493308 p.Configure(o.AzureTenant)
494309 case *providers.GitHubProvider:
@@ -511,18 +326,18 @@ func parseProviderInfo(o *Options, msgs []string) []string {
511326 case *providers.OIDCProvider:
512327 p.AllowUnverifiedEmail = o.InsecureOIDCAllowUnverifiedEmail
513328 p.UserIDClaim = o.UserIDClaim
514− if o.oidcVerifier == nil {
329+ if o.GetOIDCVerifier() == nil {
515330 msgs = append(msgs, "oidc provider requires an oidc issuer URL")
516331 } else {
517− p.Verifier = o.oidcVerifier
332+ p.Verifier = o.GetOIDCVerifier()
518333 }
519334 case *providers.GitLabProvider:
520335 p.AllowUnverifiedEmail = o.InsecureOIDCAllowUnverifiedEmail
521336 p.Group = o.GitLabGroup
522337 p.EmailDomains = o.EmailDomains
523338
524− if o.oidcVerifier != nil {
525− p.Verifier = o.oidcVerifier
339+ if o.GetOIDCVerifier() != nil {
340+ p.Verifier = o.GetOIDCVerifier()
526341 } else {
527342 // Initialize with default verifier for gitlab.com
528343 ctx := context.Background()
@@ -573,7 +388,7 @@ func parseProviderInfo(o *Options, msgs []string) []string {
573388 return msgs
574389}
575390
576−func parseSignatureKey(o *Options, msgs []string) []string {
391+func parseSignatureKey(o *options.Options, msgs []string) []string {
577392 if o.SignatureKey == "" {
578393 return msgs
579394 }
@@ -591,7 +406,7 @@ func parseSignatureKey(o *Options, msgs []string) []string {
591406 return append(msgs, "unsupported signature hash algorithm: "+
592407 o.SignatureKey)
593408 }
594− o.signatureData = &SignatureData{hash: hash, key: secretKey}
409+ o.SetSignatureData(&options.SignatureData{Hash: hash, Key: secretKey})
595410 return msgs
596411}
597412
@@ -634,7 +449,7 @@ func newVerifierFromJwtIssuer(jwtIssuer jwtIssuer) (*oidc.IDTokenVerifier, error
634449 return verifier, nil
635450}
636451
637−func validateCookieName(o *Options, msgs []string) []string {
452+func validateCookieName(o *options.Options, msgs []string) []string {
638453 cookie := &http.Cookie{Name: o.Cookie.Name}
639454 if cookie.String() == "" {
640455 return append(msgs, fmt.Sprintf("invalid cookie name: %q", o.Cookie.Name))
@@ -642,7 +457,7 @@ func validateCookieName(o *Options, msgs []string) []string {
642457 return msgs
643458}
644459
645−func setupLogger(o *Options, msgs []string) []string {
460+func setupLogger(o *options.Options, msgs []string) []string {
646461 // Setup the log file
647462 if len(o.LoggingFilename) > 0 {
648463 // Validate that the file/dir can be written
@@ -681,7 +496,7 @@ func setupLogger(o *Options, msgs []string) []string {
681496 logger.SetAuthTemplate(o.AuthLoggingFormat)
682497 logger.SetReqTemplate(o.RequestLoggingFormat)
683498 logger.SetGetClientFunc(func(r *http.Request) string {
684− return getClientString(o.realClientIPParser, r, false)
499+ return ip.GetClientString(o.GetRealClientIPParser(), r, false)
685500 })
686501
687502 excludePaths := make([]string, 0)
@@ -698,3 +513,18 @@ func setupLogger(o *Options, msgs []string) []string {
698513
699514 return msgs
700515}
516+
517+// jwtIssuer hold parsed JWT issuer info that's used to construct a verifier.
518+type jwtIssuer struct {
519+ issuerURI string
520+ audience string
521+}
522+
523+func parseURL(toParse string, urltype string, msgs []string) (*url.URL, []string) {
524+ parsed, err := url.Parse(toParse)
525+ if err != nil {
526+ return nil, append(msgs, fmt.Sprintf(
527+ "error parsing %s-url=%q %s", urltype, toParse, err))
528+ }
529+ return parsed, msgs
530+}
options_test.go → pkg/validation/options_test.gorenamed+63−66
@@ -1,4 +1,4 @@
1−package main
1+package validation
22
33import (
44 "crypto"
@@ -10,6 +10,7 @@ import (
1010 "testing"
1111 "time"
1212
13+ "github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
1314 "github.com/stretchr/testify/assert"
1415)
1516
@@ -19,8 +20,8 @@ const (
1920 clientSecret = "xyzzyplugh"
2021)
2122
22−func testOptions() *Options {
23− o := NewOptions()
23+func testOptions() *options.Options {
24+ o := options.NewOptions()
2425 o.Upstreams = append(o.Upstreams, "http://127.0.0.1:8080/")
2526 o.Cookie.Secret = cookieSecret
2627 o.ClientID = clientID
@@ -37,9 +38,9 @@ func errorMsg(msgs []string) string {
3738}
3839
3940func TestNewOptions(t *testing.T) {
40− o := NewOptions()
41+ o := options.NewOptions()
4142 o.EmailDomains = []string{"*"}
42− err := o.Validate()
43+ err := Validate(o)
4344 assert.NotEqual(t, nil, err)
4445
4546 expected := errorMsg([]string{
@@ -50,15 +51,15 @@ func TestNewOptions(t *testing.T) {
5051}
5152
5253func TestClientSecretFileOptionFails(t *testing.T) {
53− o := NewOptions()
54+ o := options.NewOptions()
5455 o.Cookie.Secret = cookieSecret
5556 o.ClientID = clientID
5657 o.ClientSecretFile = clientSecret
5758 o.EmailDomains = []string{"*"}
58− err := o.Validate()
59+ err := Validate(o)
5960 assert.NotEqual(t, nil, err)
6061
61− p := o.provider.Data()
62+ p := o.GetProvider().Data()
6263 assert.Equal(t, clientSecret, p.ClientSecretFile)
6364 assert.Equal(t, "", p.ClientSecret)
6465
@@ -80,15 +81,15 @@ func TestClientSecretFileOption(t *testing.T) {
8081 clientSecretFileName := f.Name()
8182 defer os.Remove(clientSecretFileName)
8283
83− o := NewOptions()
84+ o := options.NewOptions()
8485 o.Cookie.Secret = cookieSecret
8586 o.ClientID = clientID
8687 o.ClientSecretFile = clientSecretFileName
8788 o.EmailDomains = []string{"*"}
88− err = o.Validate()
89+ err = Validate(o)
8990 assert.Equal(t, nil, err)
9091
91− p := o.provider.Data()
92+ p := o.GetProvider().Data()
9293 assert.Equal(t, clientSecretFileName, p.ClientSecretFile)
9394 assert.Equal(t, "", p.ClientSecret)
9495
@@ -100,7 +101,7 @@ func TestClientSecretFileOption(t *testing.T) {
100101func TestGoogleGroupOptions(t *testing.T) {
101102 o := testOptions()
102103 o.GoogleGroups = []string{"googlegroup"}
103− err := o.Validate()
104+ err := Validate(o)
104105 assert.NotEqual(t, nil, err)
105106
106107 expected := errorMsg([]string{
@@ -114,7 +115,7 @@ func TestGoogleGroupInvalidFile(t *testing.T) {
114115 o.GoogleGroups = []string{"test_group"}
115116 o.GoogleAdminEmail = "admin@example.com"
116117 o.GoogleServiceAccountJSON = "file_doesnt_exist.json"
117− err := o.Validate()
118+ err := Validate(o)
118119 assert.NotEqual(t, nil, err)
119120
120121 expected := errorMsg([]string{
@@ -125,36 +126,36 @@ func TestGoogleGroupInvalidFile(t *testing.T) {
125126
126127func TestInitializedOptions(t *testing.T) {
127128 o := testOptions()
128− assert.Equal(t, nil, o.Validate())
129+ assert.Equal(t, nil, Validate(o))
129130}
130131
131132// Note that it's not worth testing nonparseable URLs, since url.Parse()
132133// seems to parse damn near anything.
133134func TestRedirectURL(t *testing.T) {
134135 o := testOptions()
135− o.RedirectURL = "https://myhost.com/oauth2/callback"
136− assert.Equal(t, nil, o.Validate())
136+ o.RawRedirectURL = "https://myhost.com/oauth2/callback"
137+ assert.Equal(t, nil, Validate(o))
137138 expected := &url.URL{
138139 Scheme: "https", Host: "myhost.com", Path: "/oauth2/callback"}
139− assert.Equal(t, expected, o.redirectURL)
140+ assert.Equal(t, expected, o.GetRedirectURL())
140141}
141142
142143func TestProxyURLs(t *testing.T) {
143144 o := testOptions()
144145 o.Upstreams = append(o.Upstreams, "http://127.0.0.1:8081")
145− assert.Equal(t, nil, o.Validate())
146+ assert.Equal(t, nil, Validate(o))
146147 expected := []*url.URL{
147148 {Scheme: "http", Host: "127.0.0.1:8080", Path: "/"},
148149 // note the '/' was added
149150 {Scheme: "http", Host: "127.0.0.1:8081", Path: "/"},
150151 }
151− assert.Equal(t, expected, o.proxyURLs)
152+ assert.Equal(t, expected, o.GetProxyURLs())
152153}
153154
154155func TestProxyURLsError(t *testing.T) {
155156 o := testOptions()
156157 o.Upstreams = append(o.Upstreams, "127.0.0.1:8081")
157− err := o.Validate()
158+ err := Validate(o)
158159 assert.NotEqual(t, nil, err)
159160 assert.Contains(t, err.Error(), "error parsing upstream")
160161}
@@ -163,9 +164,9 @@ func TestCompiledRegex(t *testing.T) {
163164 o := testOptions()
164165 regexps := []string{"/foo/.*", "/ba[rz]/quux"}
165166 o.SkipAuthRegex = regexps
166− assert.Equal(t, nil, o.Validate())
167+ assert.Equal(t, nil, Validate(o))
167168 actual := make([]string, 0)
168− for _, regex := range o.compiledRegex {
169+ for _, regex := range o.GetCompiledRegex() {
169170 actual = append(actual, regex.String())
170171 }
171172 assert.Equal(t, regexps, actual)
@@ -174,7 +175,7 @@ func TestCompiledRegex(t *testing.T) {
174175func TestCompiledRegexError(t *testing.T) {
175176 o := testOptions()
176177 o.SkipAuthRegex = []string{"(foobaz", "barquux)"}
177− err := o.Validate()
178+ err := Validate(o)
178179 assert.NotEqual(t, nil, err)
179180
180181 expected := errorMsg([]string{
@@ -185,7 +186,7 @@ func TestCompiledRegexError(t *testing.T) {
185186 assert.Equal(t, expected, err.Error())
186187
187188 o.SkipAuthRegex = []string{"foobaz", "barquux)"}
188− err = o.Validate()
189+ err = Validate(o)
189190 assert.NotEqual(t, nil, err)
190191
191192 expected = errorMsg([]string{
@@ -196,8 +197,8 @@ func TestCompiledRegexError(t *testing.T) {
196197
197198func TestDefaultProviderApiSettings(t *testing.T) {
198199 o := testOptions()
199− assert.Equal(t, nil, o.Validate())
200− p := o.provider.Data()
200+ assert.Equal(t, nil, Validate(o))
201+ p := o.GetProvider().Data()
201202 assert.Equal(t, "https://accounts.google.com/o/oauth2/auth?access_type=offline",
202203 p.LoginURL.String())
203204 assert.Equal(t, "https://www.googleapis.com/oauth2/v3/token",
@@ -208,76 +209,76 @@ func TestDefaultProviderApiSettings(t *testing.T) {
208209
209210func TestPassAccessTokenRequiresSpecificCookieSecretLengths(t *testing.T) {
210211 o := testOptions()
211− assert.Equal(t, nil, o.Validate())
212+ assert.Equal(t, nil, Validate(o))
212213
213214 assert.Equal(t, false, o.PassAccessToken)
214215 o.PassAccessToken = true
215216 o.Cookie.Secret = "cookie of invalid length-"
216− assert.NotEqual(t, nil, o.Validate())
217+ assert.NotEqual(t, nil, Validate(o))
217218
218219 o.PassAccessToken = false
219220 o.Cookie.Refresh = time.Duration(24) * time.Hour
220− assert.NotEqual(t, nil, o.Validate())
221+ assert.NotEqual(t, nil, Validate(o))
221222
222223 o.Cookie.Secret = "16 bytes AES-128"
223− assert.Equal(t, nil, o.Validate())
224+ assert.Equal(t, nil, Validate(o))
224225
225226 o.Cookie.Secret = "24 byte secret AES-192--"
226− assert.Equal(t, nil, o.Validate())
227+ assert.Equal(t, nil, Validate(o))
227228
228229 o.Cookie.Secret = "32 byte secret for AES-256------"
229− assert.Equal(t, nil, o.Validate())
230+ assert.Equal(t, nil, Validate(o))
230231}
231232
232233func TestCookieRefreshMustBeLessThanCookieExpire(t *testing.T) {
233234 o := testOptions()
234− assert.Equal(t, nil, o.Validate())
235+ assert.Equal(t, nil, Validate(o))
235236
236237 o.Cookie.Secret = "0123456789abcdef"
237238 o.Cookie.Refresh = o.Cookie.Expire
238− assert.NotEqual(t, nil, o.Validate())
239+ assert.NotEqual(t, nil, Validate(o))
239240
240241 o.Cookie.Refresh -= time.Duration(1)
241− assert.Equal(t, nil, o.Validate())
242+ assert.Equal(t, nil, Validate(o))
242243}
243244
244245func TestBase64CookieSecret(t *testing.T) {
245246 o := testOptions()
246− assert.Equal(t, nil, o.Validate())
247+ assert.Equal(t, nil, Validate(o))
247248
248249 // 32 byte, base64 (urlsafe) encoded key
249250 o.Cookie.Secret = "yHBw2lh2Cvo6aI_jn_qMTr-pRAjtq0nzVgDJNb36jgQ="
250− assert.Equal(t, nil, o.Validate())
251+ assert.Equal(t, nil, Validate(o))
251252
252253 // 32 byte, base64 (urlsafe) encoded key, w/o padding
253254 o.Cookie.Secret = "yHBw2lh2Cvo6aI_jn_qMTr-pRAjtq0nzVgDJNb36jgQ"
254− assert.Equal(t, nil, o.Validate())
255+ assert.Equal(t, nil, Validate(o))
255256
256257 // 24 byte, base64 (urlsafe) encoded key
257258 o.Cookie.Secret = "Kp33Gj-GQmYtz4zZUyUDdqQKx5_Hgkv3"
258− assert.Equal(t, nil, o.Validate())
259+ assert.Equal(t, nil, Validate(o))
259260
260261 // 16 byte, base64 (urlsafe) encoded key
261262 o.Cookie.Secret = "LFEqZYvYUwKwzn0tEuTpLA=="
262− assert.Equal(t, nil, o.Validate())
263+ assert.Equal(t, nil, Validate(o))
263264
264265 // 16 byte, base64 (urlsafe) encoded key, w/o padding
265266 o.Cookie.Secret = "LFEqZYvYUwKwzn0tEuTpLA"
266− assert.Equal(t, nil, o.Validate())
267+ assert.Equal(t, nil, Validate(o))
267268}
268269
269270func TestValidateSignatureKey(t *testing.T) {
270271 o := testOptions()
271272 o.SignatureKey = "sha1:secret"
272− assert.Equal(t, nil, o.Validate())
273− assert.Equal(t, o.signatureData.hash, crypto.SHA1)
274− assert.Equal(t, o.signatureData.key, "secret")
273+ assert.Equal(t, nil, Validate(o))
274+ assert.Equal(t, o.GetSignatureData().Hash, crypto.SHA1)
275+ assert.Equal(t, o.GetSignatureData().Key, "secret")
275276}
276277
277278func TestValidateSignatureKeyInvalidSpec(t *testing.T) {
278279 o := testOptions()
279280 o.SignatureKey = "invalid spec"
280− err := o.Validate()
281+ err := Validate(o)
281282 assert.Equal(t, err.Error(), "invalid configuration:\n"+
282283 " invalid signature hash:key spec: "+o.SignatureKey)
283284}
@@ -285,7 +286,7 @@ func TestValidateSignatureKeyInvalidSpec(t *testing.T) {
285286func TestValidateSignatureKeyUnsupportedAlgorithm(t *testing.T) {
286287 o := testOptions()
287288 o.SignatureKey = "unsupported:default secret"
288− err := o.Validate()
289+ err := Validate(o)
289290 assert.Equal(t, err.Error(), "invalid configuration:\n"+
290291 " unsupported signature hash algorithm: "+o.SignatureKey)
291292}
@@ -293,24 +294,24 @@ func TestValidateSignatureKeyUnsupportedAlgorithm(t *testing.T) {
293294func TestValidateCookie(t *testing.T) {
294295 o := testOptions()
295296 o.Cookie.Name = "_valid_cookie_name"
296− assert.Equal(t, nil, o.Validate())
297+ assert.Equal(t, nil, Validate(o))
297298}
298299
299300func TestValidateCookieBadName(t *testing.T) {
300301 o := testOptions()
301302 o.Cookie.Name = "_bad_cookie_name{}"
302− err := o.Validate()
303+ err := Validate(o)
303304 assert.Equal(t, err.Error(), "invalid configuration:\n"+
304305 fmt.Sprintf(" invalid cookie name: %q", o.Cookie.Name))
305306}
306307
307308func TestSkipOIDCDiscovery(t *testing.T) {
308309 o := testOptions()
309− o.Provider = "oidc"
310+ o.ProviderType = "oidc"
310311 o.OIDCIssuerURL = "https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/v2.0/"
311312 o.SkipOIDCDiscovery = true
312313
313− err := o.Validate()
314+ err := Validate(o)
314315 assert.Equal(t, "invalid configuration:\n"+
315316 " missing setting: login-url\n missing setting: redeem-url\n missing setting: oidc-jwks-url", err.Error())
316317
@@ -318,54 +319,50 @@ func TestSkipOIDCDiscovery(t *testing.T) {
318319 o.RedeemURL = "https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/oauth2/v2.0/token?p=b2c_1_sign_in"
319320 o.OIDCJwksURL = "https://login.microsoftonline.com/fabrikamb2c.onmicrosoft.com/discovery/v2.0/keys"
320321
321− assert.Equal(t, nil, o.Validate())
322+ assert.Equal(t, nil, Validate(o))
322323}
323324
324325func TestGCPHealthcheck(t *testing.T) {
325326 o := testOptions()
326327 o.GCPHealthChecks = true
327− assert.Equal(t, nil, o.Validate())
328+ assert.Equal(t, nil, Validate(o))
328329}
329330
330331func TestRealClientIPHeader(t *testing.T) {
331− var o *Options
332− var err error
333− var expected string
334−
335332 // Ensure nil if ReverseProxy not set.
336− o = testOptions()
333+ o := testOptions()
337334 o.RealClientIPHeader = "X-Real-IP"
338− assert.Equal(t, nil, o.Validate())
339− assert.Nil(t, o.realClientIPParser)
335+ assert.Equal(t, nil, Validate(o))
336+ assert.Nil(t, o.GetRealClientIPParser())
340337
341338 // Ensure simple use case works.
342339 o = testOptions()
343340 o.ReverseProxy = true
344341 o.RealClientIPHeader = "X-Forwarded-For"
345− assert.Equal(t, nil, o.Validate())
346− assert.NotNil(t, o.realClientIPParser)
342+ assert.Equal(t, nil, Validate(o))
343+ assert.NotNil(t, o.GetRealClientIPParser())
347344
348345 // Ensure unknown header format process an error.
349346 o = testOptions()
350347 o.ReverseProxy = true
351348 o.RealClientIPHeader = "Forwarded"
352− err = o.Validate()
349+ err := Validate(o)
353350 assert.NotEqual(t, nil, err)
354− expected = errorMsg([]string{
351+ expected := errorMsg([]string{
355352 "real_client_ip_header (Forwarded) not accepted parameter value: the http header key (Forwarded) is either invalid or unsupported",
356353 })
357354 assert.Equal(t, expected, err.Error())
358− assert.Nil(t, o.realClientIPParser)
355+ assert.Nil(t, o.GetRealClientIPParser())
359356
360357 // Ensure invalid header format produces an error.
361358 o = testOptions()
362359 o.ReverseProxy = true
363360 o.RealClientIPHeader = "!934invalidheader-23:"
364− err = o.Validate()
361+ err = Validate(o)
365362 assert.NotEqual(t, nil, err)
366363 expected = errorMsg([]string{
367364 "real_client_ip_header (!934invalidheader-23:) not accepted parameter value: the http header key (!934invalidheader-23:) is either invalid or unsupported",
368365 })
369366 assert.Equal(t, expected, err.Error())
370− assert.Nil(t, o.realClientIPParser)
367+ assert.Nil(t, o.GetRealClientIPParser())
371368}