mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-29 02:16:28 +02:00
config: add prefix, path and regex options
proxy: support prefix, path and regex options
This commit is contained in:
parent
15972b9956
commit
7027f458dd
3 changed files with 106 additions and 2 deletions
|
@ -24,6 +24,11 @@ type Policy struct {
|
|||
Source *HostnameURL `yaml:",omitempty" json:"source,omitempty"`
|
||||
Destination *url.URL `yaml:",omitempty" json:"destination,omitempty"`
|
||||
|
||||
// Additional route matching options
|
||||
Prefix string `mapstructure:"prefix" yaml:"prefix,omitempty" json:"prefix,omitempty"`
|
||||
Path string `mapstructure:"path" yaml:"path,omitempty" json:"path,omitempty"`
|
||||
Regex string `mapstructure:"regex" yaml:"regex,omitempty" json:"regex,omitempty"`
|
||||
|
||||
// Allow unauthenticated HTTP OPTIONS requests as per the CORS spec
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests
|
||||
CORSAllowPreflight bool `mapstructure:"cors_allow_preflight" yaml:"cors_allow_preflight,omitempty"`
|
||||
|
|
|
@ -14,6 +14,8 @@ import (
|
|||
"net/http"
|
||||
stdhttputil "net/http/httputil"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
@ -230,8 +232,8 @@ func (p *Proxy) reverseProxyHandler(r *mux.Router, policy config.Policy) *mux.Ro
|
|||
|
||||
// 4. Override any custom transport settings (e.g. TLS settings, etc)
|
||||
proxy.Transport = p.roundTripperFromPolicy(&policy)
|
||||
// 5. Create a sub-router for a given route's hostname (`httpbin.corp.example.com`)
|
||||
rp := r.Host(policy.Source.Host).Subrouter()
|
||||
// 5. Create a sub-router with a matcher derived from the policy (host, path, etc...)
|
||||
rp := r.MatcherFunc(routeMatcherFuncFromPolicy(policy)).Subrouter()
|
||||
rp.PathPrefix("/").Handler(proxy)
|
||||
|
||||
// Optional: If websockets are enabled, do not set a handler request timeout
|
||||
|
@ -323,3 +325,44 @@ func (p *Proxy) roundTripperFromPolicy(policy *config.Policy) http.RoundTripper
|
|||
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
p.Handler.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
|
||||
// routeMatcherFuncFromPolicy returns a mux matcher function which compares an http request with a policy.
|
||||
//
|
||||
// Routes can be filtered by the `source`, `prefix`, `path` and `regex` fields in the policy config.
|
||||
func routeMatcherFuncFromPolicy(policy config.Policy) mux.MatcherFunc {
|
||||
// match by source
|
||||
sourceMatches := func(r *http.Request) bool {
|
||||
return r.Host == policy.Source.Host &&
|
||||
strings.HasPrefix(r.URL.Path, policy.Source.Path)
|
||||
}
|
||||
|
||||
// match by prefix
|
||||
prefixMatches := func(r *http.Request) bool {
|
||||
return policy.Prefix == "" ||
|
||||
strings.HasPrefix(r.URL.Path, policy.Prefix)
|
||||
}
|
||||
|
||||
// match by path
|
||||
pathMatches := func(r *http.Request) bool {
|
||||
return policy.Path == "" ||
|
||||
r.URL.Path == policy.Path
|
||||
}
|
||||
|
||||
// match by path regex
|
||||
var regexMatches func(*http.Request) bool
|
||||
if policy.Regex == "" {
|
||||
regexMatches = func(r *http.Request) bool { return true }
|
||||
} else if re, err := regexp.Compile(policy.Regex); err == nil {
|
||||
regexMatches = func(r *http.Request) bool {
|
||||
return re.MatchString(r.URL.Path)
|
||||
}
|
||||
} else {
|
||||
log.Error().Err(err).Str("regex", policy.Regex).Msg("proxy: invalid regex in policy, ignoring route")
|
||||
regexMatches = func(r *http.Request) bool { return false }
|
||||
}
|
||||
|
||||
return func(r *http.Request, rm *mux.RouteMatch) bool {
|
||||
return sourceMatches(r) && prefixMatches(r) && pathMatches(r) && regexMatches(r)
|
||||
}
|
||||
}
|
|
@ -276,3 +276,59 @@ func TestNewReverseProxy(t *testing.T) {
|
|||
t.Errorf("got body %q; expected %q", g, e)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouteMatcherFuncFromPolicy(t *testing.T) {
|
||||
tests := []struct {
|
||||
source, prefix, path, regex string
|
||||
incomingURL string
|
||||
expect bool
|
||||
msg string
|
||||
}{
|
||||
// host in source
|
||||
{"https://www.example.com", "", "", "",
|
||||
"https://www.example.com", true,
|
||||
"should match when host is the same as source host"},
|
||||
{"https://www.example.com", "", "", "",
|
||||
"https://www.google.com", false,
|
||||
"should not match when host is different from source host"},
|
||||
|
||||
// path prefix in source
|
||||
{"https://www.example.com/admin", "", "", "",
|
||||
"https://www.example.com/admin/someaction", true,
|
||||
"should match when path begins with source path"},
|
||||
{"https://www.example.com/admin", "", "", "",
|
||||
"https://www.example.com/notadmin", false,
|
||||
"should not match when path does not begin with source path"},
|
||||
|
||||
// path prefix
|
||||
{"https://www.example.com", "/admin", "", "",
|
||||
"https://www.example.com/admin/someaction", true,
|
||||
"should match when path begins with prefix"},
|
||||
{"https://www.example.com", "/admin", "", "",
|
||||
"https://www.example.com/notadmin", false,
|
||||
"should not match when path does not begin with prefix"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
srcURL, err := url.Parse(tt.source)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
src := &config.HostnameURL{URL: srcURL}
|
||||
matcher := routeMatcherFuncFromPolicy(config.Policy{
|
||||
Source: src,
|
||||
Prefix: tt.prefix,
|
||||
Path: tt.path,
|
||||
Regex: tt.regex,
|
||||
})
|
||||
req, err := http.NewRequest("GET", tt.incomingURL, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
actual := matcher(req, nil)
|
||||
if actual != tt.expect {
|
||||
t.Errorf("%s (source=%s prefix=%s path=%s regex=%s incoming-url=%s)",
|
||||
tt.msg, tt.source, tt.prefix, tt.path, tt.regex, tt.incomingURL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue