git.iko.soy/typeable/oauth2-proxy
on master help sign in
fix: propagate errors during route building (#3383)

* Propagate errors during route building This fixes cases such as invalid paths being silently discarded after creation by throwing a visible error in such cases. Due to the way gorilla/mux's fluent API is designed, it is necessary to manually call `.GetError()` to check for errors while building routes. Signed-off-by: Simon Engmann <simon.engmann@sovity.de> * Add test for route building error propagation Signed-off-by: Simon Engmann <simon.engmann@sovity.de> * Add route building error propagation to changelog Signed-off-by: Simon Engmann <simon.engmann@sovity.de> --------- Signed-off-by: Simon Engmann <simon.engmann@sovity.de> Co-authored-by: Simon Engmann <simon.engmann@sovity.de>

commit46be69c276f0ab17cd30d0cc0f309a187a23d92a authorJan Larwig <jan@larwig.com> dateMon 23 Mar 2026, 11:25 (6 months ago) parente2682f7
3 files changed+47−14
CHANGELOG.md+1 pkg/upstream/proxy.go+14−14 pkg/upstream/proxy_test.go+32
CHANGELOG.md+1
@@ -17,6 +17,7 @@
1717- [#3374](https://github.com/oauth2-proxy/oauth2-proxy/pull/3374) fix: handle Unix socket RemoteAddr in IP resolution (@H1net)
1818- [#3381](https://github.com/oauth2-proxy/oauth2-proxy/pull/3381) fix: do not log error for backend logout 204 (@artificiosus)
1919- [#3327](https://github.com/oauth2-proxy/oauth2-proxy/pull/3327) fix: improve logging when session refresh token is missing (@yosri-brh)
20+- [#2767](https://github.com/oauth2-proxy/oauth2-proxy/pull/2767) fix: propagate errors during route building (@sybereal)
2021
2122
2223## Release Highlights
pkg/upstream/proxy.go+14−14
@@ -58,7 +58,9 @@ func NewProxy(upstreams options.UpstreamConfig, sigData *options.SignatureData,
5858 }
5959 }
6060
61− registerTrailingSlashHandler(m.serveMux)
61+ if err := registerTrailingSlashHandler(m.serveMux); err != nil {
62+ return nil, fmt.Errorf("could not register trailing slash handler: %w", err)
63+ }
6264 return m, nil
6365}
6466
@@ -94,8 +96,7 @@ func (m *multiUpstreamProxy) registerHTTPUpstreamProxy(upstream options.Upstream
9496// registerHandler ensures the given handler is regiestered with the serveMux.
9597func (m *multiUpstreamProxy) registerHandler(upstream options.Upstream, handler http.Handler, writer pagewriter.Writer) error {
9698 if upstream.RewriteTarget == "" {
97− m.registerSimpleHandler(upstream.Path, handler)
98− return nil
99+ return m.registerSimpleHandler(upstream.Path, handler)
99100 }
100101
101102 return m.registerRewriteHandler(upstream, handler, writer)
@@ -103,12 +104,12 @@ func (m *multiUpstreamProxy) registerHandler(upstream options.Upstream, handler
103104
104105// registerSimpleHandler maintains the behaviour of the go standard serveMux
105106// by ensuring any path with a trailing `/` matches all paths under that prefix.
106−func (m *multiUpstreamProxy) registerSimpleHandler(path string, handler http.Handler) {
107+func (m *multiUpstreamProxy) registerSimpleHandler(path string, handler http.Handler) error {
107108 if strings.HasSuffix(path, "/") {
108− m.serveMux.PathPrefix(path).Handler(handler)
109− } else {
110− m.serveMux.Path(path).Handler(handler)
109+ return m.serveMux.PathPrefix(path).Handler(handler).GetError()
111110 }
111+
112+ return m.serveMux.Path(path).Handler(handler).GetError()
112113}
113114
114115// registerRewriteHandler ensures the handler is registered for all paths
@@ -123,19 +124,18 @@ func (m *multiUpstreamProxy) registerRewriteHandler(upstream options.Upstream, h
123124
124125 rewrite := newRewritePath(rewriteRegExp, upstream.RewriteTarget, writer)
125126 h := alice.New(rewrite).Then(handler)
126− m.serveMux.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool {
127− return rewriteRegExp.MatchString(req.URL.Path)
128− }).Handler(h)
129127
130− return nil
128+ return m.serveMux.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool {
129+ return rewriteRegExp.MatchString(req.URL.Path)
130+ }).Handler(h).GetError()
131131}
132132
133133// registerTrailingSlashHandler creates a new matcher that will check if the
134134// requested path would match if it had a trailing slash appended.
135135// If the path matches with a trailing slash, we send back a redirect.
136136// This allows us to be consistent with the built in go servemux implementation.
137−func registerTrailingSlashHandler(serveMux *mux.Router) {
138− serveMux.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool {
137+func registerTrailingSlashHandler(serveMux *mux.Router) error {
138+ return serveMux.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool {
139139 if strings.HasSuffix(req.URL.Path, "/") {
140140 return false
141141 }
@@ -149,7 +149,7 @@ func registerTrailingSlashHandler(serveMux *mux.Router) {
149149 return serveMux.Match(slashReq, m)
150150 }).Handler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
151151 http.Redirect(rw, req, req.URL.String()+"/", http.StatusMovedPermanently)
152− }))
152+ })).GetError()
153153}
154154
155155// sortByPathLongest ensures that the upstreams are sorted by longest path.
pkg/upstream/proxy_test.go+32
@@ -383,6 +383,38 @@ var _ = Describe("Proxy Suite", func() {
383383 )
384384 })
385385
386+ Context("multiUpstreamProxy errors", func() {
387+ type proxyErrorTableInput struct {
388+ upstreams options.UpstreamConfig
389+ expectedError string
390+ }
391+
392+ DescribeTable("NewProxy", func(in *proxyErrorTableInput) {
393+ sigData := &options.SignatureData{Hash: crypto.SHA256, Key: "secret"}
394+
395+ writer := &pagewriter.WriterFuncs{
396+ ProxyErrorFunc: func(rw http.ResponseWriter, _ *http.Request, _ error) {
397+ rw.WriteHeader(502)
398+ rw.Write([]byte("Proxy Error"))
399+ },
400+ }
401+
402+ _, err := NewProxy(in.upstreams, sigData, writer)
403+ Expect(err).To(MatchError(in.expectedError))
404+ },
405+ Entry("regex matcher without rewrite target", &proxyErrorTableInput{
406+ upstreams: options.UpstreamConfig{
407+ Upstreams: []options.Upstream{{
408+ ID: "api",
409+ Path: "^/api/$",
410+ URI: "http://example.com",
411+ }},
412+ },
413+ expectedError: `could not register http upstream "api": mux: path must start with a slash, got "^/api/$"`,
414+ }),
415+ )
416+ })
417+
386418 Context("sortByPathLongest", func() {
387419 type sortByPathLongestTableInput struct {
388420 input []options.Upstream