authorize: fix x-forwarded-uri

This commit is contained in:
Caleb Doxsey 2022-07-13 15:28:39 -06:00
parent 24a9d627cd
commit 36b819af82
3 changed files with 70 additions and 22 deletions

View file

@ -11,7 +11,6 @@ import (
"github.com/pomerium/pomerium/authorize/evaluator"
"github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/internal/httputil"
"github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/internal/sessions"
"github.com/pomerium/pomerium/internal/telemetry/trace"
@ -44,7 +43,7 @@ func (a *Authorize) Check(ctx context.Context, in *envoy_service_auth_v3.CheckRe
isForwardAuth := a.isForwardAuth(in)
if isForwardAuth {
// update the incoming http request's uri to match the forwarded URI
fwdAuthURI := getForwardAuthURL(hreq)
fwdAuthURI := urlutil.GetForwardAuthURL(hreq)
in.Attributes.Request.Http.Scheme = fwdAuthURI.Scheme
in.Attributes.Request.Http.Host = fwdAuthURI.Host
in.Attributes.Request.Http.Path = fwdAuthURI.EscapedPath()
@ -103,26 +102,6 @@ func (a *Authorize) Check(ctx context.Context, in *envoy_service_auth_v3.CheckRe
return a.handleResultDenied(ctx, in, req, res, isForwardAuthVerify, res.Allow.Reasons)
}
func getForwardAuthURL(r *http.Request) *url.URL {
urqQuery := r.URL.Query().Get("uri")
u, _ := urlutil.ParseAndValidateURL(urqQuery)
if u == nil {
u = &url.URL{
Scheme: r.Header.Get(httputil.HeaderForwardedProto),
Host: r.Header.Get(httputil.HeaderForwardedHost),
Path: r.Header.Get(httputil.HeaderForwardedURI),
}
}
originalURL := r.Header.Get(httputil.HeaderOriginalURL)
if originalURL != "" {
k, _ := urlutil.ParseAndValidateURL(originalURL)
if k != nil {
u = k
}
}
return u
}
// isForwardAuth returns if the current request is a forward auth route.
func (a *Authorize) isForwardAuth(req *envoy_service_auth_v3.CheckRequest) bool {
opts := a.currentOptions.Load()

View file

@ -0,0 +1,47 @@
package urlutil
import (
"net/http"
"net/url"
"strings"
)
// Forward headers contains information from the client-facing side of proxy
// servers that is altered or lost when a proxy is involved in the path of the
// request.
//
// https://tools.ietf.org/html/rfc7239
// https://en.wikipedia.org/wiki/X-Forwarded-For
const (
HeaderForwardedHost = "X-Forwarded-Host"
HeaderForwardedProto = "X-Forwarded-Proto"
HeaderForwardedURI = "X-Forwarded-Uri" // traefik
HeaderOriginalURL = "X-Original-Url" // nginx
)
// GetForwardAuthURL gets the forward-auth URL for the given request.
func GetForwardAuthURL(r *http.Request) *url.URL {
urqQuery := r.URL.Query().Get("uri")
u, _ := ParseAndValidateURL(urqQuery)
if u == nil {
u = &url.URL{
Scheme: r.Header.Get(HeaderForwardedProto),
Host: r.Header.Get(HeaderForwardedHost),
}
rawPath := r.Header.Get(HeaderForwardedURI)
if idx := strings.Index(rawPath, "?"); idx >= 0 {
u.RawPath = rawPath[:idx]
u.RawQuery = rawPath[idx+1:]
} else {
u.RawPath = rawPath
}
}
originalURL := r.Header.Get(HeaderOriginalURL)
if originalURL != "" {
k, _ := ParseAndValidateURL(originalURL)
if k != nil {
u = k
}
}
return u
}

View file

@ -0,0 +1,22 @@
package urlutil
import (
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGetForwardAuthURL(t *testing.T) {
t.Run("double-escaping", func(t *testing.T) {
req, err := http.NewRequest("GET", "https://example.com", nil)
require.NoError(t, err)
req.Header.Set("X-Forwarded-Proto", "https")
req.Header.Set("X-Forwarded-Host", "protected-host.tld")
req.Header.Set("X-Forwarded-Uri", "/example?a=b&c=d")
u := GetForwardAuthURL(req)
assert.Equal(t, "https://protected-host.tld?a=b&c=d", u.String())
})
}