feat: add alpha config support for proxy options
Signed-off-by: Jan Larwig <jan@larwig.com>
This commit is contained in:
parent
d0dfb9bdbb
commit
71da397ee7
2
main.go
2
main.go
|
|
@ -67,7 +67,7 @@ func main() {
|
|||
logger.Fatalf("%s", err)
|
||||
}
|
||||
|
||||
validator := NewValidator(opts.EmailDomains, opts.AuthenticatedEmailsFile)
|
||||
validator := NewValidator(opts.ProxyOptions.EmailDomains, opts.ProxyOptions.AuthenticatedEmailsFile)
|
||||
oauthproxy, err := NewOAuthProxy(opts, validator)
|
||||
if err != nil {
|
||||
logger.Fatalf("ERROR: Failed to initialise OAuth2 Proxy: %v", err)
|
||||
|
|
|
|||
20
main_test.go
20
main_test.go
|
|
@ -34,9 +34,15 @@ google_target_principal="principal"
|
|||
|
||||
cookie_secret="OQINaROshtE9TcZkNAm-5Zs2Pv3xaWytBmc5W7sPX7w="
|
||||
cookie_secure="false"
|
||||
|
||||
email_domains="example.com"
|
||||
redirect_url="http://localhost:4180/oauth2/callback"
|
||||
`
|
||||
|
||||
const testAlphaConfig = `
|
||||
proxyOptions:
|
||||
emailDomains: ["example.com"]
|
||||
redirectUrl: http://localhost:4180/oauth2/callback
|
||||
upstreamConfig:
|
||||
upstreams:
|
||||
- id: /
|
||||
|
|
@ -116,19 +122,21 @@ providers:
|
|||
- force
|
||||
`
|
||||
|
||||
const testCoreConfig = `
|
||||
email_domains="example.com"
|
||||
redirect_url="http://localhost:4180/oauth2/callback"
|
||||
`
|
||||
const testCoreConfig = ``
|
||||
|
||||
testExpectedOptions := func() *options.Options {
|
||||
opts, err := options.NewLegacyOptions().ToOptions()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
opts.Cookie.Secret = &options.SecretSource{Value: []byte("OQINaROshtE9TcZkNAm-5Zs2Pv3xaWytBmc5W7sPX7w=")}
|
||||
opts.EmailDomains = []string{"example.com"}
|
||||
opts.Cookie.Insecure = ptr.To(true)
|
||||
opts.RawRedirectURL = "http://localhost:4180/oauth2/callback"
|
||||
|
||||
opts.ProxyOptions = options.ProxyOptions{
|
||||
ProxyPrefix: "/oauth2",
|
||||
RealClientIPHeader: "X-Real-IP",
|
||||
EmailDomains: []string{"example.com"},
|
||||
RedirectURL: "http://localhost:4180/oauth2/callback",
|
||||
}
|
||||
|
||||
opts.UpstreamServers = options.UpstreamConfig{
|
||||
ProxyRawPath: ptr.To(false),
|
||||
|
|
|
|||
134
oauthproxy.go
134
oauthproxy.go
|
|
@ -83,28 +83,22 @@ type apiRoute struct {
|
|||
|
||||
// OAuthProxy is the main authentication proxy
|
||||
type OAuthProxy struct {
|
||||
ProxyOptions *options.ProxyOptions
|
||||
CookieOptions *options.Cookie
|
||||
Validator func(string) bool
|
||||
|
||||
Validator func(string) bool
|
||||
|
||||
SignInPath string
|
||||
|
||||
allowedRoutes []allowedRoute
|
||||
apiRoutes []apiRoute
|
||||
redirectURL *url.URL // the url to receive requests at
|
||||
relativeRedirectURL bool
|
||||
whitelistDomains []string
|
||||
provider providers.Provider
|
||||
sessionStore sessionsapi.SessionStore
|
||||
ProxyPrefix string
|
||||
basicAuthValidator basic.Validator
|
||||
basicAuthGroups []string
|
||||
SkipProviderButton bool
|
||||
skipAuthPreflight bool
|
||||
skipJwtBearerTokens bool
|
||||
forceJSONErrors bool
|
||||
allowQuerySemicolons bool
|
||||
realClientIPParser ipapi.RealClientIPParser
|
||||
trustedIPs *ip.NetSet
|
||||
allowedRoutes []allowedRoute
|
||||
apiRoutes []apiRoute
|
||||
redirectURL *url.URL // the url to receive requests at
|
||||
provider providers.Provider
|
||||
sessionStore sessionsapi.SessionStore
|
||||
basicAuthValidator basic.Validator
|
||||
basicAuthGroups []string
|
||||
realClientIPParser ipapi.RealClientIPParser
|
||||
trustedIPs *ip.NetSet
|
||||
|
||||
sessionChain alice.Chain
|
||||
headersChain alice.Chain
|
||||
|
|
@ -115,8 +109,6 @@ type OAuthProxy struct {
|
|||
serveMux *mux.Router
|
||||
redirectValidator redirect.Validator
|
||||
appDirector redirect.AppDirector
|
||||
|
||||
encodeState bool
|
||||
}
|
||||
|
||||
// NewOAuthProxy creates a new instance of OAuthProxy from the options provided
|
||||
|
|
@ -127,10 +119,10 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
|
|||
}
|
||||
|
||||
var basicAuthValidator basic.Validator
|
||||
if opts.HtpasswdFile != "" {
|
||||
logger.Printf("using htpasswd file: %s", opts.HtpasswdFile)
|
||||
if opts.ProxyOptions.HtpasswdFile != "" {
|
||||
logger.Printf("using htpasswd file: %s", opts.ProxyOptions.HtpasswdFile)
|
||||
var err error
|
||||
basicAuthValidator, err = basic.NewHTPasswdValidator(opts.HtpasswdFile)
|
||||
basicAuthValidator, err = basic.NewHTPasswdValidator(opts.ProxyOptions.HtpasswdFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not validate htpasswd: %v", err)
|
||||
}
|
||||
|
|
@ -144,7 +136,7 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
|
|||
pageWriter, err := pagewriter.NewWriter(pagewriter.Opts{
|
||||
TemplatesPath: opts.Templates.Path,
|
||||
CustomLogo: opts.Templates.CustomLogo,
|
||||
ProxyPrefix: opts.ProxyPrefix,
|
||||
ProxyPrefix: opts.ProxyOptions.ProxyPrefix,
|
||||
Footer: opts.Templates.Footer,
|
||||
Version: version.VERSION,
|
||||
Debug: opts.Templates.Debug,
|
||||
|
|
@ -161,19 +153,19 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
|
|||
return nil, fmt.Errorf("error initialising upstream proxy: %v", err)
|
||||
}
|
||||
|
||||
if opts.SkipJwtBearerTokens {
|
||||
if opts.ProxyOptions.SkipJwtBearerTokens {
|
||||
logger.Printf("Skipping JWT tokens from configured OIDC issuer: %q", opts.Providers[0].OIDCConfig.IssuerURL)
|
||||
for _, issuer := range opts.ExtraJwtIssuers {
|
||||
for _, issuer := range opts.ProxyOptions.ExtraJwtIssuers {
|
||||
logger.Printf("Skipping JWT tokens from extra JWT issuer: %q", issuer)
|
||||
}
|
||||
if !opts.BearerTokenLoginFallback {
|
||||
if !opts.ProxyOptions.BearerTokenLoginFallback {
|
||||
logger.Println("Denying requests with invalid JWT tokens")
|
||||
}
|
||||
|
||||
}
|
||||
redirectURL := opts.GetRedirectURL()
|
||||
if redirectURL.Path == "" {
|
||||
redirectURL.Path = fmt.Sprintf("%s/callback", opts.ProxyPrefix)
|
||||
redirectURL.Path = fmt.Sprintf("%s/callback", opts.ProxyOptions.ProxyPrefix)
|
||||
}
|
||||
|
||||
logger.Printf("OAuthProxy configured for %s Client ID: %s", provider.Data().ProviderName, opts.Providers[0].ClientID)
|
||||
|
|
@ -185,7 +177,7 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
|
|||
logger.Printf("Cookie settings: name:%s insecure(http):%v scriptaccess:%v expiry:%s domains:%s path:%s samesite:%s refresh:%s", opts.Cookie.Name, *opts.Cookie.Insecure, opts.Cookie.ScriptAccess, opts.Cookie.Expire, strings.Join(opts.Cookie.Domains, ","), opts.Cookie.Path, opts.Cookie.SameSite, refresh)
|
||||
|
||||
trustedIPs := ip.NewNetSet()
|
||||
for _, ipStr := range opts.TrustedIPs {
|
||||
for _, ipStr := range opts.ProxyOptions.TrustedIPs {
|
||||
if ipNet := ip.ParseIPNet(ipStr); ipNet != nil {
|
||||
trustedIPs.AddIPNet(*ipNet)
|
||||
} else {
|
||||
|
|
@ -213,36 +205,29 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
|
|||
return nil, fmt.Errorf("could not build headers chain: %v", err)
|
||||
}
|
||||
|
||||
redirectValidator := redirect.NewValidator(opts.WhitelistDomains)
|
||||
redirectValidator := redirect.NewValidator(opts.ProxyOptions.WhitelistDomains)
|
||||
appDirector := redirect.NewAppDirector(redirect.AppDirectorOpts{
|
||||
ProxyPrefix: opts.ProxyPrefix,
|
||||
ProxyPrefix: opts.ProxyOptions.ProxyPrefix,
|
||||
Validator: redirectValidator,
|
||||
})
|
||||
|
||||
p := &OAuthProxy{
|
||||
ProxyOptions: &opts.ProxyOptions,
|
||||
CookieOptions: &opts.Cookie,
|
||||
Validator: validator,
|
||||
|
||||
SignInPath: fmt.Sprintf("%s/sign_in", opts.ProxyPrefix),
|
||||
SignInPath: fmt.Sprintf("%s/sign_in", opts.ProxyOptions.ProxyPrefix),
|
||||
|
||||
ProxyPrefix: opts.ProxyPrefix,
|
||||
provider: provider,
|
||||
sessionStore: sessionStore,
|
||||
redirectURL: redirectURL,
|
||||
relativeRedirectURL: opts.RelativeRedirectURL,
|
||||
apiRoutes: apiRoutes,
|
||||
allowedRoutes: allowedRoutes,
|
||||
whitelistDomains: opts.WhitelistDomains,
|
||||
skipAuthPreflight: opts.SkipAuthPreflight,
|
||||
skipJwtBearerTokens: opts.SkipJwtBearerTokens,
|
||||
realClientIPParser: opts.GetRealClientIPParser(),
|
||||
SkipProviderButton: opts.SkipProviderButton,
|
||||
forceJSONErrors: opts.ForceJSONErrors,
|
||||
allowQuerySemicolons: opts.AllowQuerySemicolons,
|
||||
trustedIPs: trustedIPs,
|
||||
provider: provider,
|
||||
sessionStore: sessionStore,
|
||||
redirectURL: redirectURL,
|
||||
apiRoutes: apiRoutes,
|
||||
allowedRoutes: allowedRoutes,
|
||||
realClientIPParser: opts.GetRealClientIPParser(),
|
||||
trustedIPs: trustedIPs,
|
||||
|
||||
basicAuthValidator: basicAuthValidator,
|
||||
basicAuthGroups: opts.HtpasswdUserGroups,
|
||||
basicAuthGroups: opts.ProxyOptions.HtpasswdUserGroups,
|
||||
sessionChain: sessionChain,
|
||||
headersChain: headersChain,
|
||||
preAuthChain: preAuthChain,
|
||||
|
|
@ -250,9 +235,8 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
|
|||
upstreamProxy: upstreamProxy,
|
||||
redirectValidator: redirectValidator,
|
||||
appDirector: appDirector,
|
||||
encodeState: opts.EncodeState,
|
||||
}
|
||||
p.buildServeMux(opts.ProxyPrefix)
|
||||
p.buildServeMux(opts.ProxyOptions.ProxyPrefix)
|
||||
|
||||
if err := p.setupServer(opts); err != nil {
|
||||
return nil, fmt.Errorf("error setting up server: %v", err)
|
||||
|
|
@ -290,7 +274,7 @@ func (p *OAuthProxy) setupServer(opts *options.Options) error {
|
|||
}
|
||||
|
||||
// Option: AllowQuerySemicolons
|
||||
if opts.AllowQuerySemicolons {
|
||||
if opts.ProxyOptions.AllowQuerySemicolons {
|
||||
serverOpts.Handler = http.AllowQuerySemicolons(serverOpts.Handler)
|
||||
}
|
||||
|
||||
|
|
@ -346,7 +330,7 @@ func (p *OAuthProxy) buildProxySubrouter(s *mux.Router) {
|
|||
s.Path(oauthCallbackPath).HandlerFunc(p.OAuthCallback)
|
||||
|
||||
// Static file paths
|
||||
s.PathPrefix(staticPathPrefix).Handler(http.StripPrefix(p.ProxyPrefix, http.FileServer(http.FS(staticFiles))))
|
||||
s.PathPrefix(staticPathPrefix).Handler(http.StripPrefix(p.ProxyOptions.ProxyPrefix, http.FileServer(http.FS(staticFiles))))
|
||||
|
||||
// The userinfo and logout endpoints needs to load sessions before handling the request
|
||||
s.Path(userInfoPath).Handler(p.sessionChain.ThenFunc(p.UserInfo))
|
||||
|
|
@ -357,9 +341,9 @@ func (p *OAuthProxy) buildProxySubrouter(s *mux.Router) {
|
|||
// the OAuth2 Proxy authentication logic kicks in.
|
||||
// For example forcing HTTPS or health checks.
|
||||
func buildPreAuthChain(opts *options.Options, sessionStore sessionsapi.SessionStore) (alice.Chain, error) {
|
||||
chain := alice.New(middleware.NewScope(opts.ReverseProxy, opts.Logging.RequestIDHeader))
|
||||
chain := alice.New(middleware.NewScope(opts.ProxyOptions.ReverseProxy, opts.Logging.RequestIDHeader))
|
||||
|
||||
if opts.ForceHTTPS {
|
||||
if opts.ProxyOptions.ForceHTTPS {
|
||||
_, httpsPort, err := net.SplitHostPort(opts.Server.SecureBindAddress)
|
||||
if err != nil {
|
||||
return alice.Chain{}, fmt.Errorf("invalid HTTPS address %q: %v", opts.Server.SecureBindAddress, err)
|
||||
|
|
@ -399,7 +383,7 @@ func buildPreAuthChain(opts *options.Options, sessionStore sessionsapi.SessionSt
|
|||
func buildSessionChain(opts *options.Options, provider providers.Provider, sessionStore sessionsapi.SessionStore, validator basic.Validator) alice.Chain {
|
||||
chain := alice.New()
|
||||
|
||||
if opts.SkipJwtBearerTokens {
|
||||
if opts.ProxyOptions.SkipJwtBearerTokens {
|
||||
verifiers := opts.GetJWTBearerVerifiers()
|
||||
|
||||
sessionLoaders := make([]middlewareapi.TokenToSessionFunc, 0, len(verifiers)+1)
|
||||
|
|
@ -410,11 +394,11 @@ func buildSessionChain(opts *options.Options, provider providers.Provider, sessi
|
|||
middlewareapi.CreateTokenToSessionFunc(verifier.Verify))
|
||||
}
|
||||
|
||||
chain = chain.Append(middleware.NewJwtSessionLoader(sessionLoaders, opts.BearerTokenLoginFallback))
|
||||
chain = chain.Append(middleware.NewJwtSessionLoader(sessionLoaders, opts.ProxyOptions.BearerTokenLoginFallback))
|
||||
}
|
||||
|
||||
if validator != nil {
|
||||
chain = chain.Append(middleware.NewBasicAuthSessionLoader(validator, opts.HtpasswdUserGroups, opts.LegacyPreferEmailToUser))
|
||||
chain = chain.Append(middleware.NewBasicAuthSessionLoader(validator, opts.ProxyOptions.HtpasswdUserGroups, opts.LegacyPreferEmailToUser))
|
||||
}
|
||||
|
||||
chain = chain.Append(middleware.NewStoredSessionLoader(&middleware.StoredSessionLoaderOptions{
|
||||
|
|
@ -449,11 +433,11 @@ func buildSignInMessage(opts *options.Options) string {
|
|||
} else {
|
||||
msg = opts.Templates.Banner
|
||||
}
|
||||
} else if len(opts.EmailDomains) != 0 && opts.AuthenticatedEmailsFile == "" {
|
||||
if len(opts.EmailDomains) > 1 {
|
||||
msg = fmt.Sprintf("Authenticate using one of the following domains: %v", strings.Join(opts.EmailDomains, ", "))
|
||||
} else if opts.EmailDomains[0] != "*" {
|
||||
msg = fmt.Sprintf("Authenticate using %v", opts.EmailDomains[0])
|
||||
} else if len(opts.ProxyOptions.EmailDomains) != 0 && opts.ProxyOptions.AuthenticatedEmailsFile == "" {
|
||||
if len(opts.ProxyOptions.EmailDomains) > 1 {
|
||||
msg = fmt.Sprintf("Authenticate using one of the following domains: %v", strings.Join(opts.ProxyOptions.EmailDomains, ", "))
|
||||
} else if opts.ProxyOptions.EmailDomains[0] != "*" {
|
||||
msg = fmt.Sprintf("Authenticate using %v", opts.ProxyOptions.EmailDomains[0])
|
||||
}
|
||||
}
|
||||
return msg
|
||||
|
|
@ -470,9 +454,9 @@ func buildProviderName(p providers.Provider, override string) string {
|
|||
// SkipAuthRegex option (paths only support) or newer SkipAuthRoutes option
|
||||
// (method=path support)
|
||||
func buildRoutesAllowlist(opts *options.Options) ([]allowedRoute, error) {
|
||||
routes := make([]allowedRoute, 0, len(opts.SkipAuthRegex)+len(opts.SkipAuthRoutes))
|
||||
routes := make([]allowedRoute, 0, len(opts.ProxyOptions.SkipAuthRegex)+len(opts.ProxyOptions.SkipAuthRoutes))
|
||||
|
||||
for _, path := range opts.SkipAuthRegex {
|
||||
for _, path := range opts.ProxyOptions.SkipAuthRegex {
|
||||
compiledRegex, err := regexp.Compile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -484,7 +468,7 @@ func buildRoutesAllowlist(opts *options.Options) ([]allowedRoute, error) {
|
|||
})
|
||||
}
|
||||
|
||||
for _, methodPath := range opts.SkipAuthRoutes {
|
||||
for _, methodPath := range opts.ProxyOptions.SkipAuthRoutes {
|
||||
var (
|
||||
method string
|
||||
path string
|
||||
|
|
@ -517,9 +501,9 @@ func buildRoutesAllowlist(opts *options.Options) ([]allowedRoute, error) {
|
|||
|
||||
// buildAPIRoutes builds an []apiRoute from ApiRoutes option
|
||||
func buildAPIRoutes(opts *options.Options) ([]apiRoute, error) {
|
||||
routes := make([]apiRoute, 0, len(opts.APIRoutes))
|
||||
routes := make([]apiRoute, 0, len(opts.ProxyOptions.APIRoutes))
|
||||
|
||||
for _, path := range opts.APIRoutes {
|
||||
for _, path := range opts.ProxyOptions.APIRoutes {
|
||||
compiledRegex, err := regexp.Compile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -575,7 +559,7 @@ func (p *OAuthProxy) ErrorPage(rw http.ResponseWriter, req *http.Request, code i
|
|||
|
||||
// IsAllowedRequest is used to check if auth should be skipped for this request
|
||||
func (p *OAuthProxy) IsAllowedRequest(req *http.Request) bool {
|
||||
isPreflightRequestAllowed := p.skipAuthPreflight && req.Method == "OPTIONS"
|
||||
isPreflightRequestAllowed := p.ProxyOptions.SkipAuthPreflight && req.Method == "OPTIONS"
|
||||
return isPreflightRequestAllowed || p.isAllowedRoute(req) || p.isTrustedIP(req)
|
||||
}
|
||||
|
||||
|
|
@ -694,7 +678,7 @@ func (p *OAuthProxy) SignIn(rw http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
http.Redirect(rw, req, redirect, http.StatusFound)
|
||||
} else {
|
||||
if p.SkipProviderButton {
|
||||
if p.ProxyOptions.SkipProviderButton {
|
||||
p.OAuthStart(rw, req)
|
||||
} else {
|
||||
// TODO - should we pass on /oauth2/sign_in query params to /oauth2/start?
|
||||
|
|
@ -855,7 +839,7 @@ func (p *OAuthProxy) doOAuthStart(rw http.ResponseWriter, req *http.Request, ove
|
|||
callbackRedirect := p.getOAuthRedirectURI(req)
|
||||
loginURL := p.provider.GetLoginURL(
|
||||
callbackRedirect,
|
||||
encodeState(csrf.HashOAuthState(), appRedirect, p.encodeState),
|
||||
encodeState(csrf.HashOAuthState(), appRedirect, p.ProxyOptions.EncodeState),
|
||||
csrf.HashOIDCNonce(),
|
||||
extraParams,
|
||||
)
|
||||
|
|
@ -891,7 +875,7 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
nonce, appRedirect, err := decodeState(req.Form.Get("state"), p.encodeState)
|
||||
nonce, appRedirect, err := decodeState(req.Form.Get("state"), p.ProxyOptions.EncodeState)
|
||||
if err != nil {
|
||||
logger.Errorf("Error while parsing OAuth2 state: %v", err)
|
||||
p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
|
||||
|
|
@ -1042,7 +1026,7 @@ func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) {
|
|||
p.headersChain.Then(p.upstreamProxy).ServeHTTP(rw, req)
|
||||
case ErrNeedsLogin:
|
||||
// we need to send the user to a login screen
|
||||
if p.forceJSONErrors || isAjax(req) || p.isAPIPath(req) {
|
||||
if p.ProxyOptions.ForceJSONErrors || isAjax(req) || p.isAPIPath(req) {
|
||||
logger.Printf("No valid authentication in request. Access Denied.")
|
||||
// no point redirecting an AJAX request
|
||||
p.errorJSON(rw, http.StatusUnauthorized)
|
||||
|
|
@ -1050,7 +1034,7 @@ func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
logger.Printf("No valid authentication in request. Initiating login.")
|
||||
if p.SkipProviderButton {
|
||||
if p.ProxyOptions.SkipProviderButton {
|
||||
// start OAuth flow, but only with the default login URL params - do not
|
||||
// consider this request's query params as potential overrides, since
|
||||
// the user did not explicitly start the login flow
|
||||
|
|
@ -1060,7 +1044,7 @@ func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) {
|
|||
}
|
||||
|
||||
case ErrAccessDenied:
|
||||
if p.forceJSONErrors {
|
||||
if p.ProxyOptions.ForceJSONErrors {
|
||||
p.errorJSON(rw, http.StatusForbidden)
|
||||
} else {
|
||||
p.ErrorPage(rw, req, http.StatusForbidden, "The session failed authorization checks")
|
||||
|
|
@ -1100,7 +1084,7 @@ func prepareNoCacheMiddleware(next http.Handler) http.Handler {
|
|||
// This is usually the OAuthProxy callback URL.
|
||||
func (p *OAuthProxy) getOAuthRedirectURI(req *http.Request) string {
|
||||
// if `p.redirectURL` already has a host, return it
|
||||
if p.relativeRedirectURL || p.redirectURL.Host != "" {
|
||||
if p.ProxyOptions.RelativeRedirectURL || p.redirectURL.Host != "" {
|
||||
return p.redirectURL.String()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -591,7 +591,7 @@ func NewSignInPageTest(skipProvider bool) (*SignInPageTest, error) {
|
|||
var sipTest SignInPageTest
|
||||
|
||||
sipTest.opts = baseTestOptions()
|
||||
sipTest.opts.SkipProviderButton = skipProvider
|
||||
sipTest.opts.ProxyOptions.SkipProviderButton = skipProvider
|
||||
err := validation.Validate(sipTest.opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -627,7 +627,7 @@ func TestManualSignInStoresUserGroupsInTheSession(t *testing.T) {
|
|||
userGroups := []string{"somegroup", "someothergroup"}
|
||||
|
||||
opts := baseTestOptions()
|
||||
opts.HtpasswdUserGroups = userGroups
|
||||
opts.ProxyOptions.HtpasswdUserGroups = userGroups
|
||||
err := validation.Validate(opts)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -987,7 +987,7 @@ func NewUserInfoEndpointTest() (*ProcessCookieTest, error) {
|
|||
return nil, err
|
||||
}
|
||||
pcTest.req, _ = http.NewRequest("GET",
|
||||
pcTest.opts.ProxyPrefix+"/userinfo", nil)
|
||||
pcTest.opts.ProxyOptions.ProxyPrefix+"/userinfo", nil)
|
||||
return pcTest, nil
|
||||
}
|
||||
|
||||
|
|
@ -1095,7 +1095,7 @@ func NewAuthOnlyEndpointTest(querystring string, modifiers ...OptionsModifier) (
|
|||
}
|
||||
pcTest.req, _ = http.NewRequest(
|
||||
"GET",
|
||||
fmt.Sprintf("%s/auth%s", pcTest.opts.ProxyPrefix, querystring),
|
||||
fmt.Sprintf("%s/auth%s", pcTest.opts.ProxyOptions.ProxyPrefix, querystring),
|
||||
nil)
|
||||
return pcTest, nil
|
||||
}
|
||||
|
|
@ -1234,7 +1234,7 @@ func TestAuthOnlyEndpointSetXAuthRequestHeaders(t *testing.T) {
|
|||
|
||||
pcTest.rw = httptest.NewRecorder()
|
||||
pcTest.req, _ = http.NewRequest("GET",
|
||||
pcTest.opts.ProxyPrefix+authOnlyPath, nil)
|
||||
pcTest.opts.ProxyOptions.ProxyPrefix+authOnlyPath, nil)
|
||||
|
||||
created := time.Now()
|
||||
startSession := &sessions.SessionState{
|
||||
|
|
@ -1327,7 +1327,7 @@ func TestAuthOnlyEndpointSetBasicAuthTrueRequestHeaders(t *testing.T) {
|
|||
|
||||
pcTest.rw = httptest.NewRecorder()
|
||||
pcTest.req, _ = http.NewRequest("GET",
|
||||
pcTest.opts.ProxyPrefix+authOnlyPath, nil)
|
||||
pcTest.opts.ProxyOptions.ProxyPrefix+authOnlyPath, nil)
|
||||
|
||||
created := time.Now()
|
||||
startSession := &sessions.SessionState{
|
||||
|
|
@ -1407,7 +1407,7 @@ func TestAuthOnlyEndpointSetBasicAuthFalseRequestHeaders(t *testing.T) {
|
|||
|
||||
pcTest.rw = httptest.NewRecorder()
|
||||
pcTest.req, _ = http.NewRequest("GET",
|
||||
pcTest.opts.ProxyPrefix+authOnlyPath, nil)
|
||||
pcTest.opts.ProxyOptions.ProxyPrefix+authOnlyPath, nil)
|
||||
|
||||
created := time.Now()
|
||||
startSession := &sessions.SessionState{
|
||||
|
|
@ -1442,7 +1442,7 @@ func TestAuthSkippedForPreflightRequests(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
opts.SkipAuthPreflight = true
|
||||
opts.ProxyOptions.SkipAuthPreflight = true
|
||||
err := validation.Validate(opts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
|
@ -1502,7 +1502,7 @@ type SignatureTest struct {
|
|||
|
||||
func NewSignatureTest() (*SignatureTest, error) {
|
||||
opts := baseTestOptions()
|
||||
opts.EmailDomains = []string{"acm.org"}
|
||||
opts.ProxyOptions.EmailDomains = []string{"acm.org"}
|
||||
|
||||
authenticator := &SignatureAuthenticator{}
|
||||
upstreamServer := httptest.NewServer(
|
||||
|
|
@ -1637,7 +1637,7 @@ func TestRequestSignature(t *testing.T) {
|
|||
}
|
||||
t.Cleanup(st.Close)
|
||||
if tc.key != "" {
|
||||
st.opts.SignatureKey = fmt.Sprintf("sha1:%s", tc.key)
|
||||
st.opts.ProxyOptions.LegacySignatureKey = fmt.Sprintf("sha1:%s", tc.key)
|
||||
}
|
||||
err = st.MakeRequestWithExpectedKey(tc.method, tc.body, tc.key)
|
||||
assert.NoError(t, err)
|
||||
|
|
@ -1655,7 +1655,7 @@ type ajaxRequestTest struct {
|
|||
func newAjaxRequestTest(forceJSONErrors bool) (*ajaxRequestTest, error) {
|
||||
test := &ajaxRequestTest{}
|
||||
test.opts = baseTestOptions()
|
||||
test.opts.ForceJSONErrors = forceJSONErrors
|
||||
test.opts.ProxyOptions.ForceJSONErrors = forceJSONErrors
|
||||
err := validation.Validate(test.opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -1907,7 +1907,7 @@ func TestGetJwtSession(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
opts.SkipJwtBearerTokens = true
|
||||
opts.ProxyOptions.SkipJwtBearerTokens = true
|
||||
opts.SetJWTBearerVerifiers(append(opts.GetJWTBearerVerifiers(), internalVerifier))
|
||||
})
|
||||
if err != nil {
|
||||
|
|
@ -1974,7 +1974,7 @@ func Test_noCacheHeaders(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
opts.SkipAuthRegex = []string{".*"}
|
||||
opts.ProxyOptions.SkipAuthRegex = []string{".*"}
|
||||
err := validation.Validate(opts)
|
||||
assert.NoError(t, err)
|
||||
proxy, err := NewOAuthProxy(opts, func(_ string) bool { return true })
|
||||
|
|
@ -2090,7 +2090,7 @@ func baseTestOptions() *options.Options {
|
|||
opts.Providers[0].ID = "providerID"
|
||||
opts.Providers[0].ClientID = clientID
|
||||
opts.Providers[0].ClientSecret = clientSecret
|
||||
opts.EmailDomains = []string{"*"}
|
||||
opts.ProxyOptions.EmailDomains = []string{"*"}
|
||||
|
||||
// Default injected headers for legacy configuration
|
||||
opts.InjectRequestHeaders = []options.Header{
|
||||
|
|
@ -2312,9 +2312,9 @@ func TestTrustedIPs(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
opts.TrustedIPs = tt.trustedIPs
|
||||
opts.ReverseProxy = tt.reverseProxy
|
||||
opts.RealClientIPHeader = tt.realClientIPHeader
|
||||
opts.ProxyOptions.TrustedIPs = tt.trustedIPs
|
||||
opts.ProxyOptions.ReverseProxy = tt.reverseProxy
|
||||
opts.ProxyOptions.RealClientIPHeader = tt.realClientIPHeader
|
||||
err := validation.Validate(opts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
|
@ -2488,8 +2488,10 @@ func Test_buildRoutesAllowlist(t *testing.T) {
|
|||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
opts := &options.Options{
|
||||
SkipAuthRegex: tc.skipAuthRegex,
|
||||
SkipAuthRoutes: tc.skipAuthRoutes,
|
||||
ProxyOptions: options.ProxyOptions{
|
||||
SkipAuthRegex: tc.skipAuthRegex,
|
||||
SkipAuthRoutes: tc.skipAuthRoutes,
|
||||
},
|
||||
}
|
||||
routes, err := buildRoutesAllowlist(opts)
|
||||
if tc.shouldError {
|
||||
|
|
@ -2557,10 +2559,10 @@ func TestApiRoutes(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
opts.APIRoutes = []string{
|
||||
opts.ProxyOptions.APIRoutes = []string{
|
||||
"^/api",
|
||||
}
|
||||
opts.SkipProviderButton = true
|
||||
opts.ProxyOptions.SkipProviderButton = true
|
||||
err := validation.Validate(opts)
|
||||
assert.NoError(t, err)
|
||||
proxy, err := NewOAuthProxy(opts, func(_ string) bool { return true })
|
||||
|
|
@ -2638,10 +2640,10 @@ func TestAllowedRequest(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
opts.SkipAuthRegex = []string{
|
||||
opts.ProxyOptions.SkipAuthRegex = []string{
|
||||
"^/skip/auth/regex$",
|
||||
}
|
||||
opts.SkipAuthRoutes = []string{
|
||||
opts.ProxyOptions.SkipAuthRoutes = []string{
|
||||
"GET=^/skip/auth/routes/get",
|
||||
}
|
||||
err := validation.Validate(opts)
|
||||
|
|
@ -2727,7 +2729,7 @@ func TestAllowedRequestWithForwardedUriHeader(t *testing.T) {
|
|||
t.Cleanup(upstreamServer.Close)
|
||||
|
||||
opts := baseTestOptions()
|
||||
opts.ReverseProxy = true
|
||||
opts.ProxyOptions.ReverseProxy = true
|
||||
opts.UpstreamServers = options.UpstreamConfig{
|
||||
Upstreams: []options.Upstream{
|
||||
{
|
||||
|
|
@ -2737,10 +2739,10 @@ func TestAllowedRequestWithForwardedUriHeader(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
opts.SkipAuthRegex = []string{
|
||||
opts.ProxyOptions.SkipAuthRegex = []string{
|
||||
"^/skip/auth/regex$",
|
||||
}
|
||||
opts.SkipAuthRoutes = []string{
|
||||
opts.ProxyOptions.SkipAuthRoutes = []string{
|
||||
"GET=^/skip/auth/routes/get",
|
||||
}
|
||||
err := validation.Validate(opts)
|
||||
|
|
@ -2802,7 +2804,7 @@ func TestAllowedRequestWithForwardedUriHeader(t *testing.T) {
|
|||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
req, err := http.NewRequest(tc.method, opts.ProxyPrefix+authOnlyPath, nil)
|
||||
req, err := http.NewRequest(tc.method, opts.ProxyOptions.ProxyPrefix+authOnlyPath, nil)
|
||||
req.Header.Set("X-Forwarded-Uri", tc.url)
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
|
@ -2838,7 +2840,7 @@ func TestAllowedRequestNegateWithoutMethod(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
opts.SkipAuthRoutes = []string{
|
||||
opts.ProxyOptions.SkipAuthRoutes = []string{
|
||||
"!=^/api", // any non-api routes
|
||||
"POST=^/api/public-entity/?$",
|
||||
}
|
||||
|
|
@ -2938,7 +2940,7 @@ func TestAllowedRequestNegateWithMethod(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
opts.SkipAuthRoutes = []string{
|
||||
opts.ProxyOptions.SkipAuthRoutes = []string{
|
||||
"GET!=^/api", // any non-api routes
|
||||
"POST=^/api/public-entity/?$",
|
||||
}
|
||||
|
|
@ -3319,8 +3321,8 @@ func TestAuthOnlyAllowedGroupsWithSkipMethods(t *testing.T) {
|
|||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
test, err := NewAuthOnlyEndpointTest("?allowed_groups=a,b", func(opts *options.Options) {
|
||||
opts.SkipAuthPreflight = true
|
||||
opts.TrustedIPs = []string{"1.2.3.4"}
|
||||
opts.ProxyOptions.SkipAuthPreflight = true
|
||||
opts.ProxyOptions.TrustedIPs = []string{"1.2.3.4"}
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -3566,7 +3568,7 @@ func TestGetOAuthRedirectURI(t *testing.T) {
|
|||
{
|
||||
name: "relative redirect url",
|
||||
setupOpts: func(baseOpts *options.Options) *options.Options {
|
||||
baseOpts.RelativeRedirectURL = true
|
||||
baseOpts.ProxyOptions.RelativeRedirectURL = true
|
||||
return baseOpts
|
||||
},
|
||||
req: &http.Request{},
|
||||
|
|
@ -3575,7 +3577,7 @@ func TestGetOAuthRedirectURI(t *testing.T) {
|
|||
{
|
||||
name: "proxy prefix",
|
||||
setupOpts: func(baseOpts *options.Options) *options.Options {
|
||||
baseOpts.ProxyPrefix = "/prefix"
|
||||
baseOpts.ProxyOptions.ProxyPrefix = "/prefix"
|
||||
return baseOpts
|
||||
},
|
||||
req: &http.Request{
|
||||
|
|
@ -3589,8 +3591,8 @@ func TestGetOAuthRedirectURI(t *testing.T) {
|
|||
{
|
||||
name: "proxy prefix with relative redirect",
|
||||
setupOpts: func(baseOpts *options.Options) *options.Options {
|
||||
baseOpts.ProxyPrefix = "/prefix"
|
||||
baseOpts.RelativeRedirectURL = true
|
||||
baseOpts.ProxyOptions.ProxyPrefix = "/prefix"
|
||||
baseOpts.ProxyOptions.RelativeRedirectURL = true
|
||||
return baseOpts
|
||||
},
|
||||
req: &http.Request{
|
||||
|
|
@ -3618,7 +3620,7 @@ func TestGetOAuthRedirectURI(t *testing.T) {
|
|||
|
||||
func TestIdTokenPlaceholderInSignOut(t *testing.T) {
|
||||
opts := baseTestOptions()
|
||||
opts.WhitelistDomains = []string{"my-oidc-provider.example.com"}
|
||||
opts.ProxyOptions.WhitelistDomains = []string{"my-oidc-provider.example.com"}
|
||||
|
||||
err := validation.Validate(opts)
|
||||
assert.NoError(t, err)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ package options
|
|||
// They may change between releases without notice.
|
||||
// :::
|
||||
type AlphaOptions struct {
|
||||
// ProxyOptions
|
||||
ProxyOptions ProxyOptions `yaml:"proxyOptions,omitempty"`
|
||||
|
||||
// UpstreamConfig is used to configure upstream servers.
|
||||
// Once a user is authenticated, requests to the server will be proxied to
|
||||
// these upstream servers based on the path mappings defined in this list.
|
||||
|
|
@ -65,6 +68,7 @@ func NewAlphaOptions(opts *Options) *AlphaOptions {
|
|||
// ExtractFrom populates the fields in the AlphaOptions with the values from
|
||||
// the Options
|
||||
func (a *AlphaOptions) ExtractFrom(opts *Options) {
|
||||
a.ProxyOptions = opts.ProxyOptions
|
||||
a.UpstreamConfig = opts.UpstreamServers
|
||||
a.InjectRequestHeaders = opts.InjectRequestHeaders
|
||||
a.InjectResponseHeaders = opts.InjectResponseHeaders
|
||||
|
|
@ -78,6 +82,7 @@ func (a *AlphaOptions) ExtractFrom(opts *Options) {
|
|||
// MergeOptionsWithDefaults replaces alpha options in the Options struct
|
||||
// with the values from the AlphaOptions and ensures the defaults
|
||||
func (a *AlphaOptions) MergeOptionsWithDefaults(opts *Options) {
|
||||
opts.ProxyOptions = a.ProxyOptions
|
||||
opts.UpstreamServers = a.UpstreamConfig
|
||||
opts.InjectRequestHeaders = a.InjectRequestHeaders
|
||||
opts.InjectResponseHeaders = a.InjectResponseHeaders
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ import (
|
|||
)
|
||||
|
||||
type LegacyOptions struct {
|
||||
// Legacy options for the overall proxy behaviour
|
||||
LegacyProxyOptions LegacyProxyOptions `cfg:",squash"`
|
||||
|
||||
// Legacy options related to upstream servers
|
||||
LegacyUpstreams LegacyUpstreams `cfg:",squash"`
|
||||
|
||||
|
|
@ -31,6 +34,13 @@ type LegacyOptions struct {
|
|||
|
||||
func NewLegacyOptions() *LegacyOptions {
|
||||
return &LegacyOptions{
|
||||
LegacyProxyOptions: LegacyProxyOptions{
|
||||
ProxyPrefix: "/oauth2",
|
||||
RealClientIPHeader: "X-Real-IP",
|
||||
ForceHTTPS: false,
|
||||
SkipAuthPreflight: false,
|
||||
},
|
||||
|
||||
LegacyUpstreams: LegacyUpstreams{
|
||||
PassHostHeader: true,
|
||||
ProxyWebSockets: true,
|
||||
|
|
@ -92,6 +102,7 @@ func NewLegacyOptions() *LegacyOptions {
|
|||
func NewLegacyFlagSet() *pflag.FlagSet {
|
||||
flagSet := NewFlagSet()
|
||||
|
||||
flagSet.AddFlagSet(legacyProxyOptionsFlagSet())
|
||||
flagSet.AddFlagSet(legacyUpstreamsFlagSet())
|
||||
flagSet.AddFlagSet(legacyHeadersFlagSet())
|
||||
flagSet.AddFlagSet(legacyServerFlagset())
|
||||
|
|
@ -104,6 +115,8 @@ func NewLegacyFlagSet() *pflag.FlagSet {
|
|||
}
|
||||
|
||||
func (l *LegacyOptions) ToOptions() (*Options, error) {
|
||||
l.Options.ProxyOptions = l.LegacyProxyOptions.convert()
|
||||
|
||||
upstreams, err := l.LegacyUpstreams.convert()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error converting upstreams: %v", err)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,99 @@
|
|||
package options
|
||||
|
||||
import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
type LegacyProxyOptions struct {
|
||||
AllowQuerySemicolons bool `flag:"allow-query-semicolons" cfg:"allow_query_semicolons"`
|
||||
ProxyPrefix string `flag:"proxy-prefix" cfg:"proxy_prefix"`
|
||||
RealClientIPHeader string `flag:"real-client-ip-header" cfg:"real_client_ip_header"`
|
||||
ReverseProxy bool `flag:"reverse-proxy" cfg:"reverse_proxy"`
|
||||
TrustedIPs []string `flag:"trusted-ip" cfg:"trusted_ips"`
|
||||
ForceHTTPS bool `flag:"force-https" cfg:"force_https"`
|
||||
SSLInsecureSkipVerify bool `flag:"ssl-insecure-skip-verify" cfg:"ssl_insecure_skip_verify"`
|
||||
ForceJSONErrors bool `flag:"force-json-errors" cfg:"force_json_errors"`
|
||||
SkipAuthRegex []string `flag:"skip-auth-regex" cfg:"skip_auth_regex"`
|
||||
SkipAuthRoutes []string `flag:"skip-auth-route" cfg:"skip_auth_routes"`
|
||||
AuthenticatedEmailsFile string `flag:"authenticated-emails-file" cfg:"authenticated_emails_file"`
|
||||
EmailDomains []string `flag:"email-domain" cfg:"email_domains"`
|
||||
WhitelistDomains []string `flag:"whitelist-domain" cfg:"whitelist_domains"`
|
||||
HtpasswdFile string `flag:"htpasswd-file" cfg:"htpasswd_file"`
|
||||
HtpasswdUserGroups []string `flag:"htpasswd-user-group" cfg:"htpasswd_user_groups"`
|
||||
RawRedirectURL string `flag:"redirect-url" cfg:"redirect_url"`
|
||||
RelativeRedirectURL bool `flag:"relative-redirect-url" cfg:"relative_redirect_url"`
|
||||
APIRoutes []string `flag:"api-route" cfg:"api_routes"`
|
||||
SkipJwtBearerTokens bool `flag:"skip-jwt-bearer-tokens" cfg:"skip_jwt_bearer_tokens"`
|
||||
BearerTokenLoginFallback bool `flag:"bearer-token-login-fallback" cfg:"bearer_token_login_fallback"`
|
||||
ExtraJwtIssuers []string `flag:"extra-jwt-issuers" cfg:"extra_jwt_issuers"`
|
||||
SkipProviderButton bool `flag:"skip-provider-button" cfg:"skip_provider_button"`
|
||||
SkipAuthPreflight bool `flag:"skip-auth-preflight" cfg:"skip_auth_preflight"`
|
||||
SignatureKey string `flag:"signature-key" cfg:"signature_key"`
|
||||
EncodeState bool `flag:"encode-state" cfg:"encode_state"`
|
||||
}
|
||||
|
||||
func legacyProxyOptionsFlagSet() *pflag.FlagSet {
|
||||
flagSet := pflag.NewFlagSet("proxy", pflag.ExitOnError)
|
||||
flagSet.Bool("reverse-proxy", false, "are we running behind a reverse proxy, controls whether headers like X-Real-Ip are accepted")
|
||||
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)")
|
||||
flagSet.StringSlice("trusted-ip", []string{}, "list of IPs or CIDR ranges to allow to bypass authentication. WARNING: trusting by IP has inherent security flaws, read the configuration documentation for more information.")
|
||||
flagSet.Bool("force-https", false, "force HTTPS redirect for HTTP requests")
|
||||
flagSet.String("redirect-url", "", "the OAuth Redirect URL. ie: \"https://internalapp.yourcompany.com/oauth2/callback\"")
|
||||
flagSet.Bool("relative-redirect-url", false, "allow relative OAuth Redirect URL.")
|
||||
flagSet.StringSlice("skip-auth-regex", []string{}, "(DEPRECATED for --skip-auth-route) bypass authentication for requests path's that match (may be given multiple times)")
|
||||
flagSet.StringSlice("skip-auth-route", []string{}, "bypass authentication for requests that match the method & path. Format: method=path_regex OR method!=path_regex. For all methods: path_regex OR !=path_regex")
|
||||
flagSet.StringSlice("api-route", []string{}, "return HTTP 401 instead of redirecting to authentication server if token is not valid. Format: path_regex")
|
||||
flagSet.Bool("skip-provider-button", false, "will skip sign-in-page to directly reach the next step: oauth/start")
|
||||
flagSet.Bool("skip-auth-preflight", false, "will skip authentication for OPTIONS requests")
|
||||
flagSet.Bool("ssl-insecure-skip-verify", false, "skip validation of certificates presented when using HTTPS providers")
|
||||
flagSet.Bool("skip-jwt-bearer-tokens", false, "will skip requests that have verified JWT bearer tokens (default false)")
|
||||
flagSet.Bool("bearer-token-login-fallback", true, "if skip-jwt-bearer-tokens is set, fall back to normal login redirect with an invalid JWT. If false, 403 instead")
|
||||
flagSet.Bool("force-json-errors", false, "will force JSON errors instead of HTTP error pages or redirects")
|
||||
flagSet.Bool("encode-state", false, "will encode oauth state with base64")
|
||||
flagSet.Bool("allow-query-semicolons", false, "allow the use of semicolons in query args")
|
||||
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)")
|
||||
flagSet.StringSlice("email-domain", []string{}, "authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email")
|
||||
flagSet.StringSlice("whitelist-domain", []string{}, "allowed domains for redirection after authentication. Prefix domain with a . or a *. to allow subdomains (eg .example.com, *.example.com)")
|
||||
flagSet.String("authenticated-emails-file", "", "authenticate against emails via file (one per line)")
|
||||
flagSet.String("htpasswd-file", "", "additionally authenticate against a htpasswd file. Entries must be created with \"htpasswd -B\" for bcrypt encryption")
|
||||
flagSet.StringSlice("htpasswd-user-group", []string{}, "the groups to be set on sessions for htpasswd users (may be given multiple times)")
|
||||
flagSet.String("proxy-prefix", "/oauth2", "the url root path that this proxy should be nested under (e.g. /<oauth2>/sign_in)")
|
||||
flagSet.String("signature-key", "", "GAP-Signature request signature key (algorithm:secretkey)")
|
||||
return flagSet
|
||||
}
|
||||
|
||||
func (l *LegacyProxyOptions) convert() ProxyOptions {
|
||||
return ProxyOptions{
|
||||
// security
|
||||
AllowQuerySemicolons: l.AllowQuerySemicolons,
|
||||
ForceHTTPS: l.ForceHTTPS,
|
||||
SkipAuthRegex: l.SkipAuthRegex,
|
||||
SkipAuthRoutes: l.SkipAuthRoutes,
|
||||
SkipAuthPreflight: l.SkipAuthPreflight,
|
||||
SSLInsecureSkipVerify: l.SSLInsecureSkipVerify,
|
||||
TrustedIPs: l.TrustedIPs,
|
||||
|
||||
// authentication
|
||||
AuthenticatedEmailsFile: l.AuthenticatedEmailsFile,
|
||||
EmailDomains: l.EmailDomains,
|
||||
WhitelistDomains: l.WhitelistDomains,
|
||||
HtpasswdFile: l.HtpasswdFile,
|
||||
HtpasswdUserGroups: l.HtpasswdUserGroups,
|
||||
SkipJwtBearerTokens: l.SkipJwtBearerTokens,
|
||||
BearerTokenLoginFallback: l.BearerTokenLoginFallback,
|
||||
ExtraJwtIssuers: l.ExtraJwtIssuers,
|
||||
ForceJSONErrors: l.ForceJSONErrors,
|
||||
|
||||
// routing
|
||||
APIRoutes: l.APIRoutes,
|
||||
ReverseProxy: l.ReverseProxy,
|
||||
ProxyPrefix: l.ProxyPrefix,
|
||||
RedirectURL: l.RawRedirectURL,
|
||||
RelativeRedirectURL: l.RelativeRedirectURL,
|
||||
RealClientIPHeader: l.RealClientIPHeader,
|
||||
SkipProviderButton: l.SkipProviderButton,
|
||||
EncodeState: l.EncodeState,
|
||||
|
||||
LegacySignatureKey: l.SignatureKey,
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,11 @@ var _ = Describe("Load", func() {
|
|||
optionsWithNilProvider.Providers = nil
|
||||
|
||||
legacyOptionsWithNilProvider := &LegacyOptions{
|
||||
LegacyProxyOptions: LegacyProxyOptions{
|
||||
ProxyPrefix: "/oauth2",
|
||||
RealClientIPHeader: "X-Real-IP",
|
||||
BearerTokenLoginFallback: true,
|
||||
},
|
||||
LegacyUpstreams: LegacyUpstreams{
|
||||
PassHostHeader: true,
|
||||
ProxyWebSockets: true,
|
||||
|
|
@ -68,15 +73,10 @@ var _ = Describe("Load", func() {
|
|||
},
|
||||
|
||||
Options: Options{
|
||||
BearerTokenLoginFallback: true,
|
||||
ProxyPrefix: "/oauth2",
|
||||
PingPath: "/ping",
|
||||
ReadyPath: "/ready",
|
||||
RealClientIPHeader: "X-Real-IP",
|
||||
ForceHTTPS: false,
|
||||
Templates: templatesDefaults(),
|
||||
SkipAuthPreflight: false,
|
||||
Logging: loggingDefaults(),
|
||||
PingPath: "/ping",
|
||||
ReadyPath: "/ready",
|
||||
Templates: templatesDefaults(),
|
||||
Logging: loggingDefaults(),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,27 +18,17 @@ type SignatureData struct {
|
|||
// Options holds Configuration Options that can be set by Command Line Flag,
|
||||
// or Config File
|
||||
type Options struct {
|
||||
ProxyPrefix string `flag:"proxy-prefix" cfg:"proxy_prefix"`
|
||||
PingPath string `flag:"ping-path" cfg:"ping_path"`
|
||||
PingUserAgent string `flag:"ping-user-agent" cfg:"ping_user_agent"`
|
||||
ReadyPath string `flag:"ready-path" cfg:"ready_path"`
|
||||
ReverseProxy bool `flag:"reverse-proxy" cfg:"reverse_proxy"`
|
||||
RealClientIPHeader string `flag:"real-client-ip-header" cfg:"real_client_ip_header"`
|
||||
TrustedIPs []string `flag:"trusted-ip" cfg:"trusted_ips"`
|
||||
ForceHTTPS bool `flag:"force-https" cfg:"force_https"`
|
||||
RawRedirectURL string `flag:"redirect-url" cfg:"redirect_url"`
|
||||
RelativeRedirectURL bool `flag:"relative-redirect-url" cfg:"relative_redirect_url"`
|
||||
PingPath string `flag:"ping-path" cfg:"ping_path"`
|
||||
PingUserAgent string `flag:"ping-user-agent" cfg:"ping_user_agent"`
|
||||
ReadyPath string `flag:"ready-path" cfg:"ready_path"`
|
||||
|
||||
AuthenticatedEmailsFile string `flag:"authenticated-emails-file" cfg:"authenticated_emails_file"`
|
||||
EmailDomains []string `flag:"email-domain" cfg:"email_domains"`
|
||||
WhitelistDomains []string `flag:"whitelist-domain" cfg:"whitelist_domains"`
|
||||
HtpasswdFile string `flag:"htpasswd-file" cfg:"htpasswd_file"`
|
||||
HtpasswdUserGroups []string `flag:"htpasswd-user-group" cfg:"htpasswd_user_groups"`
|
||||
ProxyOptions ProxyOptions `cfg:",internal"`
|
||||
Cookie Cookie `cfg:",internal"`
|
||||
Session SessionOptions `cfg:",internal"`
|
||||
Logging Logging `cfg:",squash"`
|
||||
Templates Templates `cfg:",squash"`
|
||||
|
||||
Cookie Cookie `cfg:",internal"`
|
||||
Session SessionOptions `cfg:",internal"`
|
||||
Logging Logging `cfg:",squash"`
|
||||
Templates Templates `cfg:",squash"`
|
||||
GCPHealthChecks bool `flag:"gcp-healthchecks" cfg:"gcp_healthchecks"`
|
||||
|
||||
// Not used in the legacy config, name not allowed to match an external key (upstreams)
|
||||
// TODO(JoelSpeed): Rename when legacy config is removed
|
||||
|
|
@ -52,22 +42,6 @@ type Options struct {
|
|||
|
||||
Providers Providers `cfg:",internal"`
|
||||
|
||||
APIRoutes []string `flag:"api-route" cfg:"api_routes"`
|
||||
SkipAuthRegex []string `flag:"skip-auth-regex" cfg:"skip_auth_regex"`
|
||||
SkipAuthRoutes []string `flag:"skip-auth-route" cfg:"skip_auth_routes"`
|
||||
SkipJwtBearerTokens bool `flag:"skip-jwt-bearer-tokens" cfg:"skip_jwt_bearer_tokens"`
|
||||
BearerTokenLoginFallback bool `flag:"bearer-token-login-fallback" cfg:"bearer_token_login_fallback"`
|
||||
ExtraJwtIssuers []string `flag:"extra-jwt-issuers" cfg:"extra_jwt_issuers"`
|
||||
SkipProviderButton bool `flag:"skip-provider-button" cfg:"skip_provider_button"`
|
||||
SSLInsecureSkipVerify bool `flag:"ssl-insecure-skip-verify" cfg:"ssl_insecure_skip_verify"`
|
||||
SkipAuthPreflight bool `flag:"skip-auth-preflight" cfg:"skip_auth_preflight"`
|
||||
ForceJSONErrors bool `flag:"force-json-errors" cfg:"force_json_errors"`
|
||||
EncodeState bool `flag:"encode-state" cfg:"encode_state"`
|
||||
AllowQuerySemicolons bool `flag:"allow-query-semicolons" cfg:"allow_query_semicolons"`
|
||||
|
||||
SignatureKey string `flag:"signature-key" cfg:"signature_key"`
|
||||
GCPHealthChecks bool `flag:"gcp-healthchecks" cfg:"gcp_healthchecks"`
|
||||
|
||||
// This is used for backwards compatibility for basic auth users
|
||||
LegacyPreferEmailToUser bool `cfg:",internal"`
|
||||
|
||||
|
|
@ -98,16 +72,12 @@ func (o *Options) SetRealClientIPParser(s ipapi.RealClientIPParser) { o.re
|
|||
// NewOptions constructs a new Options with defaulted values
|
||||
func NewOptions() *Options {
|
||||
return &Options{
|
||||
BearerTokenLoginFallback: true,
|
||||
ProxyPrefix: "/oauth2",
|
||||
Providers: providerDefaults(),
|
||||
PingPath: "/ping",
|
||||
ReadyPath: "/ready",
|
||||
RealClientIPHeader: "X-Real-IP",
|
||||
ForceHTTPS: false,
|
||||
Templates: templatesDefaults(),
|
||||
SkipAuthPreflight: false,
|
||||
Logging: loggingDefaults(),
|
||||
ProxyOptions: proxyOptionsDefaults(),
|
||||
Providers: providerDefaults(),
|
||||
PingPath: "/ping",
|
||||
ReadyPath: "/ready",
|
||||
Templates: templatesDefaults(),
|
||||
Logging: loggingDefaults(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -115,35 +85,9 @@ func NewOptions() *Options {
|
|||
func NewFlagSet() *pflag.FlagSet {
|
||||
flagSet := pflag.NewFlagSet("oauth2-proxy", pflag.ExitOnError)
|
||||
|
||||
flagSet.Bool("reverse-proxy", false, "are we running behind a reverse proxy, controls whether headers like X-Real-Ip are accepted")
|
||||
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, X-ProxyUser-IP, X-Envoy-External-Address, or CF-Connecting-IP)")
|
||||
flagSet.StringSlice("trusted-ip", []string{}, "list of IPs or CIDR ranges to allow to bypass authentication. WARNING: trusting by IP has inherent security flaws, read the configuration documentation for more information.")
|
||||
flagSet.Bool("force-https", false, "force HTTPS redirect for HTTP requests")
|
||||
flagSet.String("redirect-url", "", "the OAuth Redirect URL. ie: \"https://internalapp.yourcompany.com/oauth2/callback\"")
|
||||
flagSet.Bool("relative-redirect-url", false, "allow relative OAuth Redirect URL.")
|
||||
flagSet.StringSlice("skip-auth-regex", []string{}, "(DEPRECATED for --skip-auth-route) bypass authentication for requests path's that match (may be given multiple times)")
|
||||
flagSet.StringSlice("skip-auth-route", []string{}, "bypass authentication for requests that match the method & path. Format: method=path_regex OR method!=path_regex. For all methods: path_regex OR !=path_regex")
|
||||
flagSet.StringSlice("api-route", []string{}, "return HTTP 401 instead of redirecting to authentication server if token is not valid. Format: path_regex")
|
||||
flagSet.Bool("skip-provider-button", false, "will skip sign-in-page to directly reach the next step: oauth/start")
|
||||
flagSet.Bool("skip-auth-preflight", false, "will skip authentication for OPTIONS requests")
|
||||
flagSet.Bool("ssl-insecure-skip-verify", false, "skip validation of certificates presented when using HTTPS providers")
|
||||
flagSet.Bool("skip-jwt-bearer-tokens", false, "will skip requests that have verified JWT bearer tokens (default false)")
|
||||
flagSet.Bool("bearer-token-login-fallback", true, "if skip-jwt-bearer-tokens is set, fall back to normal login redirect with an invalid JWT. If false, 403 instead")
|
||||
flagSet.Bool("force-json-errors", false, "will force JSON errors instead of HTTP error pages or redirects")
|
||||
flagSet.Bool("encode-state", false, "will encode oauth state with base64")
|
||||
flagSet.Bool("allow-query-semicolons", false, "allow the use of semicolons in query args")
|
||||
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)")
|
||||
|
||||
flagSet.StringSlice("email-domain", []string{}, "authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email")
|
||||
flagSet.StringSlice("whitelist-domain", []string{}, "allowed domains for redirection after authentication. Prefix domain with a . or a *. to allow subdomains (eg .example.com, *.example.com)")
|
||||
flagSet.String("authenticated-emails-file", "", "authenticate against emails via file (one per line)")
|
||||
flagSet.String("htpasswd-file", "", "additionally authenticate against a htpasswd file. Entries must be created with \"htpasswd -B\" for bcrypt encryption")
|
||||
flagSet.StringSlice("htpasswd-user-group", []string{}, "the groups to be set on sessions for htpasswd users (may be given multiple times)")
|
||||
flagSet.String("proxy-prefix", "/oauth2", "the url root path that this proxy should be nested under (e.g. /<oauth2>/sign_in)")
|
||||
flagSet.String("ping-path", "/ping", "the ping endpoint that can be used for basic health checks")
|
||||
flagSet.String("ping-user-agent", "", "special User-Agent that will be used for basic health checks")
|
||||
flagSet.String("ready-path", "/ready", "the ready endpoint that can be used for deep health checks")
|
||||
flagSet.String("signature-key", "", "GAP-Signature request signature key (algorithm:secretkey)")
|
||||
flagSet.Bool("gcp-healthchecks", false, "Enable GCP/GKE healthcheck endpoints")
|
||||
|
||||
flagSet.AddFlagSet(loggingFlagSet())
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
package options
|
||||
|
||||
type ProxyOptions struct {
|
||||
// security
|
||||
AllowQuerySemicolons bool `yaml:"allowQuerySemicolons,omitempty"`
|
||||
ForceHTTPS bool `yaml:"forceHttps,omitempty"`
|
||||
SkipAuthRegex []string `yaml:"skipAuthRegex,omitempty"`
|
||||
SkipAuthRoutes []string `yaml:"skipAuthRoutes,omitempty"`
|
||||
SkipAuthPreflight bool `yaml:"skipAuthPreflight,omitempty"`
|
||||
SSLInsecureSkipVerify bool `yaml:"sslInsecureSkipVerify,omitempty"`
|
||||
TrustedIPs []string `yaml:"trustedIPs,omitempty"`
|
||||
|
||||
// authentication
|
||||
AuthenticatedEmailsFile string `yaml:"authenticatedEmailsFile,omitempty"`
|
||||
EmailDomains []string `yaml:"emailDomains,omitempty"`
|
||||
WhitelistDomains []string `yaml:"whitelistDomains,omitempty"`
|
||||
HtpasswdFile string `yaml:"htpasswdFile,omitempty"`
|
||||
HtpasswdUserGroups []string `yaml:"htpasswdUserGroups,omitempty"`
|
||||
SkipJwtBearerTokens bool `yaml:"skipJwtBearerTokens,omitempty"`
|
||||
BearerTokenLoginFallback bool `yaml:"bearerTokenLoginFallback,omitempty"`
|
||||
ExtraJwtIssuers []string `yaml:"extraJwtIssuers,omitempty"`
|
||||
|
||||
// routing
|
||||
APIRoutes []string `yaml:"apiRoutes,omitempty"`
|
||||
ReverseProxy bool `yaml:"reverseProxy,omitempty"`
|
||||
ProxyPrefix string `yaml:"proxyPrefix,omitempty"`
|
||||
RedirectURL string `yaml:"redirectUrl,omitempty"`
|
||||
RelativeRedirectURL bool `yaml:"relativeRedirectUrl,omitempty"`
|
||||
RealClientIPHeader string `yaml:"realClientIPHeader,omitempty"`
|
||||
SkipProviderButton bool `yaml:"skipProviderButton,omitempty"`
|
||||
EncodeState bool `yaml:"encodeState,omitempty"`
|
||||
|
||||
// Force oauth2-proxy error responses to be JSON
|
||||
ForceJSONErrors bool `yaml:"forceJsonErrors,omitempty"`
|
||||
|
||||
// This is used for backwards compatibility
|
||||
LegacyPreferEmailToUser bool `yaml:"legacyPreferEmailToUser,omitempty"`
|
||||
LegacySignatureKey string `yaml:"legacySignatureKey,omitempty"`
|
||||
}
|
||||
|
||||
func proxyOptionsDefaults() ProxyOptions {
|
||||
return ProxyOptions{
|
||||
ProxyPrefix: "/oauth2",
|
||||
RealClientIPHeader: "X-Real-IP",
|
||||
BearerTokenLoginFallback: true,
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,7 @@ func validateAllowlists(o *options.Options) []string {
|
|||
msgs = append(msgs, validateAuthRegexes(o)...)
|
||||
msgs = append(msgs, validateTrustedIPs(o)...)
|
||||
|
||||
if len(o.TrustedIPs) > 0 && o.ReverseProxy {
|
||||
if len(o.ProxyOptions.TrustedIPs) > 0 && o.ProxyOptions.ReverseProxy {
|
||||
_, err := fmt.Fprintln(os.Stderr, "WARNING: mixing --trusted-ip with --reverse-proxy is a potential security vulnerability. An attacker can inject a trusted IP into an X-Real-IP or X-Forwarded-For header if they aren't properly protected outside of oauth2-proxy")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
@ -31,7 +31,7 @@ func validateAllowlists(o *options.Options) []string {
|
|||
// validateAuthRoutes validates method=path routes passed with options.SkipAuthRoutes
|
||||
func validateAuthRoutes(o *options.Options) []string {
|
||||
msgs := []string{}
|
||||
for _, route := range o.SkipAuthRoutes {
|
||||
for _, route := range o.ProxyOptions.SkipAuthRoutes {
|
||||
var regex string
|
||||
parts := strings.SplitN(route, "=", 2)
|
||||
if len(parts) == 1 {
|
||||
|
|
@ -49,13 +49,13 @@ func validateAuthRoutes(o *options.Options) []string {
|
|||
|
||||
// validateAuthRegexes validates regex paths passed with options.SkipAuthRegex
|
||||
func validateAuthRegexes(o *options.Options) []string {
|
||||
return validateRegexes(o.SkipAuthRegex)
|
||||
return validateRegexes(o.ProxyOptions.SkipAuthRegex)
|
||||
}
|
||||
|
||||
// validateTrustedIPs validates IP/CIDRs for IP based allowlists
|
||||
func validateTrustedIPs(o *options.Options) []string {
|
||||
msgs := []string{}
|
||||
for i, ipStr := range o.TrustedIPs {
|
||||
for i, ipStr := range o.ProxyOptions.TrustedIPs {
|
||||
if nil == ip.ParseIPNet(ipStr) {
|
||||
msgs = append(msgs, fmt.Sprintf("trusted_ips[%d] (%s) could not be recognized", i, ipStr))
|
||||
}
|
||||
|
|
@ -65,7 +65,7 @@ func validateTrustedIPs(o *options.Options) []string {
|
|||
|
||||
// validateAPIRoutes validates regex paths passed with options.ApiRoutes
|
||||
func validateAPIRoutes(o *options.Options) []string {
|
||||
return validateRegexes(o.APIRoutes)
|
||||
return validateRegexes(o.ProxyOptions.APIRoutes)
|
||||
}
|
||||
|
||||
// validateRegexes validates all regexes and returns a list of messages in case of error
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ var _ = Describe("Allowlist", func() {
|
|||
DescribeTable("validateRoutes",
|
||||
func(r *validateRoutesTableInput) {
|
||||
opts := &options.Options{
|
||||
SkipAuthRoutes: r.routes,
|
||||
ProxyOptions: options.ProxyOptions{
|
||||
SkipAuthRoutes: r.routes,
|
||||
},
|
||||
}
|
||||
Expect(validateAuthRoutes(opts)).To(ConsistOf(r.errStrings))
|
||||
},
|
||||
|
|
@ -58,7 +60,9 @@ var _ = Describe("Allowlist", func() {
|
|||
DescribeTable("validateRegexes",
|
||||
func(r *validateRegexesTableInput) {
|
||||
opts := &options.Options{
|
||||
SkipAuthRegex: r.regexes,
|
||||
ProxyOptions: options.ProxyOptions{
|
||||
SkipAuthRegex: r.regexes,
|
||||
},
|
||||
}
|
||||
Expect(validateAuthRegexes(opts)).To(ConsistOf(r.errStrings))
|
||||
},
|
||||
|
|
@ -90,7 +94,9 @@ var _ = Describe("Allowlist", func() {
|
|||
DescribeTable("validateTrustedIPs",
|
||||
func(t *validateTrustedIPsTableInput) {
|
||||
opts := &options.Options{
|
||||
TrustedIPs: t.trustedIPs,
|
||||
ProxyOptions: options.ProxyOptions{
|
||||
TrustedIPs: t.trustedIPs,
|
||||
},
|
||||
}
|
||||
Expect(validateTrustedIPs(opts)).To(ConsistOf(t.errStrings))
|
||||
},
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ func Validate(o *options.Options) error {
|
|||
msgs = configureLogger(o.Logging, msgs)
|
||||
msgs = parseSignatureKey(o, msgs)
|
||||
|
||||
if o.SSLInsecureSkipVerify {
|
||||
if o.ProxyOptions.SSLInsecureSkipVerify {
|
||||
transport := requests.DefaultTransport.(*http.Transport)
|
||||
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} // #nosec G402 -- InsecureSkipVerify is a configurable option we allow
|
||||
} else if len(o.Providers[0].CAFiles) > 0 {
|
||||
|
|
@ -47,16 +47,16 @@ func Validate(o *options.Options) error {
|
|||
}
|
||||
}
|
||||
|
||||
if o.AuthenticatedEmailsFile == "" && len(o.EmailDomains) == 0 && o.HtpasswdFile == "" {
|
||||
if o.ProxyOptions.AuthenticatedEmailsFile == "" && len(o.ProxyOptions.EmailDomains) == 0 && o.ProxyOptions.HtpasswdFile == "" {
|
||||
msgs = append(msgs, "missing setting for email validation: email-domain or authenticated-emails-file required."+
|
||||
"\n use email-domain=* to authorize all email addresses")
|
||||
}
|
||||
|
||||
if o.SkipJwtBearerTokens {
|
||||
if o.ProxyOptions.SkipJwtBearerTokens {
|
||||
// Configure extra issuers
|
||||
if len(o.ExtraJwtIssuers) > 0 {
|
||||
if len(o.ProxyOptions.ExtraJwtIssuers) > 0 {
|
||||
var jwtIssuers []jwtIssuer
|
||||
jwtIssuers, msgs = parseJwtIssuers(o.ExtraJwtIssuers, msgs)
|
||||
jwtIssuers, msgs = parseJwtIssuers(o.ProxyOptions.ExtraJwtIssuers, msgs)
|
||||
for _, jwtIssuer := range jwtIssuers {
|
||||
verifier, err := newVerifierFromJwtIssuer(
|
||||
o.Providers[0].OIDCConfig.AudienceClaims,
|
||||
|
|
@ -72,18 +72,18 @@ func Validate(o *options.Options) error {
|
|||
}
|
||||
|
||||
var redirectURL *url.URL
|
||||
redirectURL, msgs = parseURL(o.RawRedirectURL, "redirect", msgs)
|
||||
redirectURL, msgs = parseURL(o.ProxyOptions.RedirectURL, "redirect", msgs)
|
||||
o.SetRedirectURL(redirectURL)
|
||||
if o.RawRedirectURL == "" && ptr.Deref(o.Cookie.Insecure, options.DefaultCookieInsecure) && !o.ReverseProxy {
|
||||
if o.ProxyOptions.RedirectURL == "" && ptr.Deref(o.Cookie.Insecure, options.DefaultCookieInsecure) && !o.ProxyOptions.ReverseProxy {
|
||||
logger.Print("WARNING: no explicit redirect URL: redirects will default to insecure HTTP")
|
||||
}
|
||||
|
||||
msgs = append(msgs, validateUpstreams(o.UpstreamServers)...)
|
||||
|
||||
if o.ReverseProxy {
|
||||
parser, err := ip.GetRealClientIPParser(o.RealClientIPHeader)
|
||||
if o.ProxyOptions.ReverseProxy {
|
||||
parser, err := ip.GetRealClientIPParser(o.ProxyOptions.RealClientIPHeader)
|
||||
if err != nil {
|
||||
msgs = append(msgs, fmt.Sprintf("real_client_ip_header (%s) not accepted parameter value: %v", o.RealClientIPHeader, err))
|
||||
msgs = append(msgs, fmt.Sprintf("real_client_ip_header (%s) not accepted parameter value: %v", o.ProxyOptions.RealClientIPHeader, err))
|
||||
}
|
||||
o.SetRealClientIPParser(parser)
|
||||
|
||||
|
|
@ -104,22 +104,22 @@ func Validate(o *options.Options) error {
|
|||
}
|
||||
|
||||
func parseSignatureKey(o *options.Options, msgs []string) []string {
|
||||
if o.SignatureKey == "" {
|
||||
if o.ProxyOptions.LegacySignatureKey == "" {
|
||||
return msgs
|
||||
}
|
||||
|
||||
logger.Print("WARNING: `--signature-key` is deprecated. It will be removed in a future release")
|
||||
|
||||
components := strings.Split(o.SignatureKey, ":")
|
||||
components := strings.Split(o.ProxyOptions.LegacySignatureKey, ":")
|
||||
if len(components) != 2 {
|
||||
return append(msgs, "invalid signature hash:key spec: "+
|
||||
o.SignatureKey)
|
||||
o.ProxyOptions.LegacySignatureKey)
|
||||
}
|
||||
|
||||
algorithm, secretKey := components[0], components[1]
|
||||
hash, err := hmacauth.DigestNameToCryptoHash(algorithm)
|
||||
if err != nil {
|
||||
return append(msgs, "unsupported signature hash algorithm: "+o.SignatureKey)
|
||||
return append(msgs, "unsupported signature hash algorithm: "+o.ProxyOptions.LegacySignatureKey)
|
||||
}
|
||||
o.SetSignatureData(&options.SignatureData{Hash: hash, Key: secretKey})
|
||||
return msgs
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ func testOptions() *options.Options {
|
|||
o.Providers[0].ID = providerID
|
||||
o.Providers[0].ClientID = clientID
|
||||
o.Providers[0].ClientSecret = clientSecret
|
||||
o.EmailDomains = []string{"*"}
|
||||
o.ProxyOptions.EmailDomains = []string{"*"}
|
||||
o.EnsureDefaults()
|
||||
return o
|
||||
}
|
||||
|
|
@ -48,7 +48,7 @@ func errorMsg(msgs []string) string {
|
|||
|
||||
func TestNewOptions(t *testing.T) {
|
||||
o := options.NewOptions()
|
||||
o.EmailDomains = []string{"*"}
|
||||
o.ProxyOptions.EmailDomains = []string{"*"}
|
||||
o.EnsureDefaults()
|
||||
|
||||
err := Validate(o)
|
||||
|
|
@ -117,7 +117,7 @@ func TestInitializedOptions(t *testing.T) {
|
|||
// seems to parse damn near anything.
|
||||
func TestRedirectURL(t *testing.T) {
|
||||
o := testOptions()
|
||||
o.RawRedirectURL = "https://myhost.com/oauth2/callback"
|
||||
o.ProxyOptions.RedirectURL = "https://myhost.com/oauth2/callback"
|
||||
assert.Equal(t, nil, Validate(o))
|
||||
expected := &url.URL{
|
||||
Scheme: "https", Host: "myhost.com", Path: "/oauth2/callback"}
|
||||
|
|
@ -163,7 +163,7 @@ func TestBase64CookieSecret(t *testing.T) {
|
|||
|
||||
func TestValidateSignatureKey(t *testing.T) {
|
||||
o := testOptions()
|
||||
o.SignatureKey = "sha1:secret"
|
||||
o.ProxyOptions.LegacySignatureKey = "sha1:secret"
|
||||
assert.Equal(t, nil, Validate(o))
|
||||
assert.Equal(t, o.GetSignatureData().Hash, crypto.SHA1)
|
||||
assert.Equal(t, o.GetSignatureData().Key, "secret")
|
||||
|
|
@ -171,18 +171,18 @@ func TestValidateSignatureKey(t *testing.T) {
|
|||
|
||||
func TestValidateSignatureKeyInvalidSpec(t *testing.T) {
|
||||
o := testOptions()
|
||||
o.SignatureKey = "invalid spec"
|
||||
o.ProxyOptions.LegacySignatureKey = "invalid spec"
|
||||
err := Validate(o)
|
||||
assert.Equal(t, err.Error(), "invalid configuration:\n"+
|
||||
" invalid signature hash:key spec: "+o.SignatureKey)
|
||||
" invalid signature hash:key spec: "+o.ProxyOptions.LegacySignatureKey)
|
||||
}
|
||||
|
||||
func TestValidateSignatureKeyUnsupportedAlgorithm(t *testing.T) {
|
||||
o := testOptions()
|
||||
o.SignatureKey = "unsupported:default secret"
|
||||
o.ProxyOptions.LegacySignatureKey = "unsupported:default secret"
|
||||
err := Validate(o)
|
||||
assert.Equal(t, err.Error(), "invalid configuration:\n"+
|
||||
" unsupported signature hash algorithm: "+o.SignatureKey)
|
||||
" unsupported signature hash algorithm: "+o.ProxyOptions.LegacySignatureKey)
|
||||
}
|
||||
|
||||
func TestGCPHealthcheck(t *testing.T) {
|
||||
|
|
@ -194,21 +194,21 @@ func TestGCPHealthcheck(t *testing.T) {
|
|||
func TestRealClientIPHeader(t *testing.T) {
|
||||
// Ensure nil if ReverseProxy not set.
|
||||
o := testOptions()
|
||||
o.RealClientIPHeader = "X-Real-IP"
|
||||
o.ProxyOptions.RealClientIPHeader = "X-Real-IP"
|
||||
assert.Equal(t, nil, Validate(o))
|
||||
assert.Nil(t, o.GetRealClientIPParser())
|
||||
|
||||
// Ensure simple use case works.
|
||||
o = testOptions()
|
||||
o.ReverseProxy = true
|
||||
o.RealClientIPHeader = "X-Forwarded-For"
|
||||
o.ProxyOptions.ReverseProxy = true
|
||||
o.ProxyOptions.RealClientIPHeader = "X-Forwarded-For"
|
||||
assert.Equal(t, nil, Validate(o))
|
||||
assert.NotNil(t, o.GetRealClientIPParser())
|
||||
|
||||
// Ensure unknown header format process an error.
|
||||
o = testOptions()
|
||||
o.ReverseProxy = true
|
||||
o.RealClientIPHeader = "Forwarded"
|
||||
o.ProxyOptions.ReverseProxy = true
|
||||
o.ProxyOptions.RealClientIPHeader = "Forwarded"
|
||||
err := Validate(o)
|
||||
assert.NotEqual(t, nil, err)
|
||||
expected := errorMsg([]string{
|
||||
|
|
@ -219,8 +219,8 @@ func TestRealClientIPHeader(t *testing.T) {
|
|||
|
||||
// Ensure invalid header format produces an error.
|
||||
o = testOptions()
|
||||
o.ReverseProxy = true
|
||||
o.RealClientIPHeader = "!934invalidheader-23:"
|
||||
o.ProxyOptions.ReverseProxy = true
|
||||
o.ProxyOptions.RealClientIPHeader = "!934invalidheader-23:"
|
||||
err = Validate(o)
|
||||
assert.NotEqual(t, nil, err)
|
||||
expected = errorMsg([]string{
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ func validateProviders(o *options.Options) []string {
|
|||
if len(o.Providers) == 0 {
|
||||
msgs = append(msgs, "at least one provider has to be defined")
|
||||
}
|
||||
if o.SkipProviderButton && len(o.Providers) > 1 {
|
||||
if o.ProxyOptions.SkipProviderButton && len(o.Providers) > 1 {
|
||||
msgs = append(msgs, "SkipProviderButton and multiple providers are mutually exclusive")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -100,7 +100,9 @@ var _ = Describe("Providers", func() {
|
|||
}),
|
||||
Entry("with multiple providers and skip provider button", &validateProvidersTableInput{
|
||||
options: &options.Options{
|
||||
SkipProviderButton: true,
|
||||
ProxyOptions: options.ProxyOptions{
|
||||
SkipProviderButton: true,
|
||||
},
|
||||
Providers: options.Providers{
|
||||
validProvider,
|
||||
validLoginGovProvider,
|
||||
|
|
|
|||
Loading…
Reference in New Issue