mirror of
https://github.com/pomerium/pomerium.git
synced 2025-06-07 05:12:45 +02:00
config: add support for wildcard from addresses (#4131)
* config: add support for wildcards * update policy matching, header generation * remove deprecated field * fix test
This commit is contained in:
parent
949454e886
commit
18bc86d632
12 changed files with 445 additions and 115 deletions
|
@ -38,6 +38,7 @@ type Request struct {
|
||||||
// RequestHTTP is the HTTP field in the request.
|
// RequestHTTP is the HTTP field in the request.
|
||||||
type RequestHTTP struct {
|
type RequestHTTP struct {
|
||||||
Method string `json:"method"`
|
Method string `json:"method"`
|
||||||
|
Hostname string `json:"hostname"`
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
Headers map[string]string `json:"headers"`
|
Headers map[string]string `json:"headers"`
|
||||||
|
@ -55,6 +56,7 @@ func NewRequestHTTP(
|
||||||
) RequestHTTP {
|
) RequestHTTP {
|
||||||
return RequestHTTP{
|
return RequestHTTP{
|
||||||
Method: method,
|
Method: method,
|
||||||
|
Hostname: requestURL.Hostname(),
|
||||||
Path: requestURL.Path,
|
Path: requestURL.Path,
|
||||||
URL: requestURL.String(),
|
URL: requestURL.String(),
|
||||||
Headers: headers,
|
Headers: headers,
|
||||||
|
@ -162,7 +164,7 @@ func (e *Evaluator) Evaluate(ctx context.Context, req *Request) (*Result, error)
|
||||||
|
|
||||||
var headersOutput *HeadersResponse
|
var headersOutput *HeadersResponse
|
||||||
eg.Go(func() error {
|
eg.Go(func() error {
|
||||||
headersReq := NewHeadersRequestFromPolicy(req.Policy)
|
headersReq := NewHeadersRequestFromPolicy(req.Policy, req.HTTP.Hostname)
|
||||||
headersReq.Session = req.Session
|
headersReq.Session = req.Session
|
||||||
var err error
|
var err error
|
||||||
headersOutput, err = e.headersEvaluators.Evaluate(ectx, headersReq)
|
headersOutput, err = e.headersEvaluators.Evaluate(ectx, headersReq)
|
||||||
|
|
|
@ -12,7 +12,6 @@ import (
|
||||||
"github.com/pomerium/pomerium/authorize/internal/store"
|
"github.com/pomerium/pomerium/authorize/internal/store"
|
||||||
"github.com/pomerium/pomerium/config"
|
"github.com/pomerium/pomerium/config"
|
||||||
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
||||||
"github.com/pomerium/pomerium/internal/urlutil"
|
|
||||||
configpb "github.com/pomerium/pomerium/pkg/grpc/config"
|
configpb "github.com/pomerium/pomerium/pkg/grpc/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -29,14 +28,12 @@ type HeadersRequest struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHeadersRequestFromPolicy creates a new HeadersRequest from a policy.
|
// NewHeadersRequestFromPolicy creates a new HeadersRequest from a policy.
|
||||||
func NewHeadersRequestFromPolicy(policy *config.Policy) *HeadersRequest {
|
func NewHeadersRequestFromPolicy(policy *config.Policy, hostname string) *HeadersRequest {
|
||||||
input := new(HeadersRequest)
|
input := new(HeadersRequest)
|
||||||
input.EnableGoogleCloudServerlessAuthentication = policy.EnableGoogleCloudServerlessAuthentication
|
input.EnableGoogleCloudServerlessAuthentication = policy.EnableGoogleCloudServerlessAuthentication
|
||||||
input.EnableRoutingKey = policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_RING_HASH ||
|
input.EnableRoutingKey = policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_RING_HASH ||
|
||||||
policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_MAGLEV
|
policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_MAGLEV
|
||||||
if u, err := urlutil.ParseAndValidateURL(policy.From); err == nil {
|
input.Issuer = hostname
|
||||||
input.Issuer = u.Hostname()
|
|
||||||
}
|
|
||||||
input.KubernetesServiceAccountToken = policy.KubernetesServiceAccountToken
|
input.KubernetesServiceAccountToken = policy.KubernetesServiceAccountToken
|
||||||
for _, wu := range policy.To {
|
for _, wu := range policy.To {
|
||||||
input.ToAudience = "https://" + wu.URL.Hostname()
|
input.ToAudience = "https://" + wu.URL.Hostname()
|
||||||
|
|
|
@ -22,13 +22,13 @@ import (
|
||||||
func TestNewHeadersRequestFromPolicy(t *testing.T) {
|
func TestNewHeadersRequestFromPolicy(t *testing.T) {
|
||||||
req := NewHeadersRequestFromPolicy(&config.Policy{
|
req := NewHeadersRequestFromPolicy(&config.Policy{
|
||||||
EnableGoogleCloudServerlessAuthentication: true,
|
EnableGoogleCloudServerlessAuthentication: true,
|
||||||
From: "https://from.example.com",
|
From: "https://*.example.com",
|
||||||
To: config.WeightedURLs{
|
To: config.WeightedURLs{
|
||||||
{
|
{
|
||||||
URL: *mustParseURL("http://to.example.com"),
|
URL: *mustParseURL("http://to.example.com"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
}, "from.example.com")
|
||||||
assert.Equal(t, &HeadersRequest{
|
assert.Equal(t, &HeadersRequest{
|
||||||
EnableGoogleCloudServerlessAuthentication: true,
|
EnableGoogleCloudServerlessAuthentication: true,
|
||||||
Issuer: "from.example.com",
|
Issuer: "from.example.com",
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||||
|
@ -570,7 +571,13 @@ func getAllRouteableHosts(options *config.Options, addr string) ([]string, error
|
||||||
allHosts.Add(hosts...)
|
allHosts.Add(hosts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return allHosts.ToSlice(), nil
|
var filtered []string
|
||||||
|
for _, host := range allHosts.ToSlice() {
|
||||||
|
if !strings.Contains(host, "*") {
|
||||||
|
filtered = append(filtered, host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filtered, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func urlsMatchHost(urls []*url.URL, host string) bool {
|
func urlsMatchHost(urls []*url.URL, host string) bool {
|
||||||
|
|
|
@ -78,7 +78,7 @@ func (b *Builder) buildMainRouteConfiguration(
|
||||||
|
|
||||||
// if we're the proxy, add all the policy routes
|
// if we're the proxy, add all the policy routes
|
||||||
if config.IsProxy(cfg.Options.Services) {
|
if config.IsProxy(cfg.Options.Services) {
|
||||||
rs, err := b.buildPolicyRoutes(cfg.Options, host, requireStrictTransportSecurity)
|
rs, err := b.buildRoutesForPoliciesWithHost(cfg, host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -94,6 +94,14 @@ func (b *Builder) buildMainRouteConfiguration(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if config.IsProxy(cfg.Options.Services) {
|
||||||
|
rs, err := b.buildRoutesForPoliciesWithCatchAll(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
vh.Routes = append(vh.Routes, rs...)
|
||||||
|
}
|
||||||
|
|
||||||
virtualHosts = append(virtualHosts, vh)
|
virtualHosts = append(virtualHosts, vh)
|
||||||
|
|
||||||
rc, err := b.buildRouteConfiguration("main", virtualHosts)
|
rc, err := b.buildRouteConfiguration("main", virtualHosts)
|
||||||
|
|
141
config/envoyconfig/route_configurations_test.go
Normal file
141
config/envoyconfig/route_configurations_test.go
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
package envoyconfig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"google.golang.org/protobuf/encoding/protojson"
|
||||||
|
|
||||||
|
"github.com/pomerium/pomerium/config"
|
||||||
|
"github.com/pomerium/pomerium/config/envoyconfig/filemgr"
|
||||||
|
"github.com/pomerium/pomerium/internal/testutil"
|
||||||
|
"github.com/pomerium/pomerium/pkg/cryptutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBuilder_buildMainRouteConfiguration(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
cfg := &config.Config{Options: &config.Options{
|
||||||
|
CookieName: "pomerium",
|
||||||
|
DefaultUpstreamTimeout: time.Second * 3,
|
||||||
|
SharedKey: cryptutil.NewBase64Key(),
|
||||||
|
Services: "proxy",
|
||||||
|
Policies: []config.Policy{
|
||||||
|
{
|
||||||
|
From: "https://*.example.com",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
b := New("grpc", "http", "metrics", filemgr.NewManager(), nil)
|
||||||
|
routeConfiguration, err := b.buildMainRouteConfiguration(ctx, cfg)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
testutil.AssertProtoJSONEqual(t, `{
|
||||||
|
"name": "main",
|
||||||
|
"validateClusters": false,
|
||||||
|
"virtualHosts": [
|
||||||
|
{
|
||||||
|
"name": "catch-all",
|
||||||
|
"domains": ["*"],
|
||||||
|
"routes": [
|
||||||
|
`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/.pomerium/jwt", true, false))+`,
|
||||||
|
`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/.pomerium/webauthn", true, false))+`,
|
||||||
|
`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/ping", false, false))+`,
|
||||||
|
`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/healthz", false, false))+`,
|
||||||
|
`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/.pomerium", false, false))+`,
|
||||||
|
`+protojson.Format(b.buildControlPlanePrefixRoute(cfg.Options, "/.pomerium/", false, false))+`,
|
||||||
|
`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/.well-known/pomerium", false, false))+`,
|
||||||
|
`+protojson.Format(b.buildControlPlanePrefixRoute(cfg.Options, "/.well-known/pomerium/", false, false))+`,
|
||||||
|
`+protojson.Format(b.buildControlPlanePathRoute(cfg.Options, "/robots.txt", false, false))+`,
|
||||||
|
{
|
||||||
|
"name": "policy-0",
|
||||||
|
"match": {
|
||||||
|
"headers": [
|
||||||
|
{ "name": ":authority", "stringMatch": { "safeRegex": { "regex": "^(.*)\\.example\\.com$" } }}
|
||||||
|
],
|
||||||
|
"prefix": "/"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"filterMetadata": {
|
||||||
|
"envoy.filters.http.lua": {
|
||||||
|
"remove_impersonate_headers": false,
|
||||||
|
"remove_pomerium_authorization": true,
|
||||||
|
"remove_pomerium_cookie": "pomerium",
|
||||||
|
"rewrite_response_headers": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"requestHeadersToRemove": [
|
||||||
|
"x-pomerium-jwt-assertion",
|
||||||
|
"x-pomerium-jwt-assertion-for",
|
||||||
|
"x-pomerium-reproxy-policy",
|
||||||
|
"x-pomerium-reproxy-policy-hmac"
|
||||||
|
],
|
||||||
|
"responseHeadersToAdd": [
|
||||||
|
{ "appendAction": "OVERWRITE_IF_EXISTS_OR_ADD", "header": { "key": "X-Frame-Options", "value": "SAMEORIGIN" } },
|
||||||
|
{ "appendAction": "OVERWRITE_IF_EXISTS_OR_ADD", "header": { "key": "X-XSS-Protection", "value": "1; mode=block" } }
|
||||||
|
],
|
||||||
|
"route": {
|
||||||
|
"autoHostRewrite": true,
|
||||||
|
"cluster": "route-0",
|
||||||
|
"hashPolicy": [
|
||||||
|
{ "header": { "headerName": "x-pomerium-routing-key" }, "terminal": true },
|
||||||
|
{ "connectionProperties": { "sourceIp": true }, "terminal": true }
|
||||||
|
],
|
||||||
|
"timeout": "3s",
|
||||||
|
"upgradeConfigs": [
|
||||||
|
{ "enabled": false, "upgradeType": "websocket" },
|
||||||
|
{ "enabled": false, "upgradeType": "spdy/3.1" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "policy-0",
|
||||||
|
"match": {
|
||||||
|
"headers": [
|
||||||
|
{ "name": ":authority", "stringMatch": { "safeRegex": { "regex": "^(.*)\\.example\\.com:443$" } }}
|
||||||
|
],
|
||||||
|
"prefix": "/"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"filterMetadata": {
|
||||||
|
"envoy.filters.http.lua": {
|
||||||
|
"remove_impersonate_headers": false,
|
||||||
|
"remove_pomerium_authorization": true,
|
||||||
|
"remove_pomerium_cookie": "pomerium",
|
||||||
|
"rewrite_response_headers": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"requestHeadersToRemove": [
|
||||||
|
"x-pomerium-jwt-assertion",
|
||||||
|
"x-pomerium-jwt-assertion-for",
|
||||||
|
"x-pomerium-reproxy-policy",
|
||||||
|
"x-pomerium-reproxy-policy-hmac"
|
||||||
|
],
|
||||||
|
"responseHeadersToAdd": [
|
||||||
|
{ "appendAction": "OVERWRITE_IF_EXISTS_OR_ADD", "header": { "key": "X-Frame-Options", "value": "SAMEORIGIN" } },
|
||||||
|
{ "appendAction": "OVERWRITE_IF_EXISTS_OR_ADD", "header": { "key": "X-XSS-Protection", "value": "1; mode=block" } }
|
||||||
|
],
|
||||||
|
"route": {
|
||||||
|
"autoHostRewrite": true,
|
||||||
|
"cluster": "route-0",
|
||||||
|
"hashPolicy": [
|
||||||
|
{ "header": { "headerName": "x-pomerium-routing-key" }, "terminal": true },
|
||||||
|
{ "connectionProperties": { "sourceIp": true }, "terminal": true }
|
||||||
|
],
|
||||||
|
"timeout": "3s",
|
||||||
|
"upgradeConfigs": [
|
||||||
|
{ "enabled": false, "upgradeType": "websocket" },
|
||||||
|
{ "enabled": false, "upgradeType": "spdy/3.1" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
}`, routeConfiguration)
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||||
envoy_config_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
envoy_config_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
||||||
|
@ -18,6 +19,7 @@ import (
|
||||||
"github.com/pomerium/pomerium/config"
|
"github.com/pomerium/pomerium/config"
|
||||||
"github.com/pomerium/pomerium/internal/httputil"
|
"github.com/pomerium/pomerium/internal/httputil"
|
||||||
"github.com/pomerium/pomerium/internal/urlutil"
|
"github.com/pomerium/pomerium/internal/urlutil"
|
||||||
|
"github.com/pomerium/pomerium/pkg/cryptutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -189,14 +191,12 @@ func getClusterStatsName(policy *config.Policy) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) buildPolicyRoutes(
|
func (b *Builder) buildRoutesForPoliciesWithHost(
|
||||||
options *config.Options,
|
cfg *config.Config,
|
||||||
host string,
|
host string,
|
||||||
requireStrictTransportSecurity bool,
|
|
||||||
) ([]*envoy_config_route_v3.Route, error) {
|
) ([]*envoy_config_route_v3.Route, error) {
|
||||||
var routes []*envoy_config_route_v3.Route
|
var routes []*envoy_config_route_v3.Route
|
||||||
|
for i, p := range cfg.Options.GetAllPolicies() {
|
||||||
for i, p := range options.GetAllPolicies() {
|
|
||||||
policy := p
|
policy := p
|
||||||
fromURL, err := urlutil.ParseAndValidateURL(policy.From)
|
fromURL, err := urlutil.ParseAndValidateURL(policy.From)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -207,27 +207,109 @@ func (b *Builder) buildPolicyRoutes(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
match := mkRouteMatch(&policy)
|
policyRoutes, err := b.buildRoutesForPolicy(cfg, &policy, fmt.Sprintf("policy-%d", i))
|
||||||
envoyRoute := &envoy_config_route_v3.Route{
|
if err != nil {
|
||||||
Name: fmt.Sprintf("policy-%d", i),
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
routes = append(routes, policyRoutes...)
|
||||||
|
}
|
||||||
|
return routes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) buildRoutesForPoliciesWithCatchAll(
|
||||||
|
cfg *config.Config,
|
||||||
|
) ([]*envoy_config_route_v3.Route, error) {
|
||||||
|
var routes []*envoy_config_route_v3.Route
|
||||||
|
for i, p := range cfg.Options.GetAllPolicies() {
|
||||||
|
policy := p
|
||||||
|
fromURL, err := urlutil.ParseAndValidateURL(policy.From)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(fromURL.Host, "*") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
policyRoutes, err := b.buildRoutesForPolicy(cfg, &policy, fmt.Sprintf("policy-%d", i))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
routes = append(routes, policyRoutes...)
|
||||||
|
}
|
||||||
|
return routes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) buildRoutesForPolicy(
|
||||||
|
cfg *config.Config,
|
||||||
|
policy *config.Policy,
|
||||||
|
name string,
|
||||||
|
) ([]*envoy_config_route_v3.Route, error) {
|
||||||
|
fromURL, err := urlutil.ParseAndValidateURL(policy.From)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var routes []*envoy_config_route_v3.Route
|
||||||
|
if strings.Contains(fromURL.Host, "*") {
|
||||||
|
// we have to match '*.example.com' and '*.example.com:443', so there are two routes
|
||||||
|
for _, host := range urlutil.GetDomainsForURL(fromURL) {
|
||||||
|
route, err := b.buildRouteForPolicyAndMatch(cfg, policy, name, mkRouteMatchForHost(policy, host))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
routes = append(routes, route)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
route, err := b.buildRouteForPolicyAndMatch(cfg, policy, name, mkRouteMatch(policy))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
routes = append(routes, route)
|
||||||
|
}
|
||||||
|
return routes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) buildRouteForPolicyAndMatch(
|
||||||
|
cfg *config.Config,
|
||||||
|
policy *config.Policy,
|
||||||
|
name string,
|
||||||
|
match *envoy_config_route_v3.RouteMatch,
|
||||||
|
) (*envoy_config_route_v3.Route, error) {
|
||||||
|
fromURL, err := urlutil.ParseAndValidateURL(policy.From)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
certs, err := getAllCertificates(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
requireStrictTransportSecurity := cryptutil.HasCertificateForServerName(certs, fromURL.Hostname())
|
||||||
|
|
||||||
|
route := &envoy_config_route_v3.Route{
|
||||||
|
Name: name,
|
||||||
Match: match,
|
Match: match,
|
||||||
Metadata: &envoy_config_core_v3.Metadata{},
|
Metadata: &envoy_config_core_v3.Metadata{},
|
||||||
RequestHeadersToAdd: toEnvoyHeaders(policy.SetRequestHeaders),
|
RequestHeadersToAdd: toEnvoyHeaders(policy.SetRequestHeaders),
|
||||||
RequestHeadersToRemove: getRequestHeadersToRemove(options, &policy),
|
RequestHeadersToRemove: getRequestHeadersToRemove(cfg.Options, policy),
|
||||||
ResponseHeadersToAdd: toEnvoyHeaders(options.GetSetResponseHeadersForPolicy(&policy, requireStrictTransportSecurity)),
|
ResponseHeadersToAdd: toEnvoyHeaders(cfg.Options.GetSetResponseHeadersForPolicy(policy, requireStrictTransportSecurity)),
|
||||||
}
|
}
|
||||||
if policy.Redirect != nil {
|
if policy.Redirect != nil {
|
||||||
action, err := b.buildPolicyRouteRedirectAction(policy.Redirect)
|
action, err := b.buildPolicyRouteRedirectAction(policy.Redirect)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
envoyRoute.Action = &envoy_config_route_v3.Route_Redirect{Redirect: action}
|
route.Action = &envoy_config_route_v3.Route_Redirect{Redirect: action}
|
||||||
} else {
|
} else {
|
||||||
action, err := b.buildPolicyRouteRouteAction(options, &policy)
|
action, err := b.buildPolicyRouteRouteAction(cfg.Options, policy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
envoyRoute.Action = &envoy_config_route_v3.Route_Route{Route: action}
|
route.Action = &envoy_config_route_v3.Route_Route{Route: action}
|
||||||
}
|
}
|
||||||
|
|
||||||
luaMetadata := map[string]*structpb.Value{
|
luaMetadata := map[string]*structpb.Value{
|
||||||
|
@ -235,18 +317,18 @@ func (b *Builder) buildPolicyRoutes(
|
||||||
}
|
}
|
||||||
|
|
||||||
// disable authentication entirely when the proxy is fronting authenticate
|
// disable authentication entirely when the proxy is fronting authenticate
|
||||||
isFrontingAuthenticate, err := isProxyFrontingAuthenticate(options, host)
|
isFrontingAuthenticate, err := isProxyFrontingAuthenticate(cfg.Options, fromURL.Hostname())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if isFrontingAuthenticate {
|
if isFrontingAuthenticate {
|
||||||
envoyRoute.TypedPerFilterConfig = map[string]*any.Any{
|
route.TypedPerFilterConfig = map[string]*any.Any{
|
||||||
"envoy.filters.http.ext_authz": disableExtAuthz,
|
"envoy.filters.http.ext_authz": disableExtAuthz,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
luaMetadata["remove_pomerium_cookie"] = &structpb.Value{
|
luaMetadata["remove_pomerium_cookie"] = &structpb.Value{
|
||||||
Kind: &structpb.Value_StringValue{
|
Kind: &structpb.Value_StringValue{
|
||||||
StringValue: options.CookieName,
|
StringValue: cfg.Options.CookieName,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
luaMetadata["remove_pomerium_authorization"] = &structpb.Value{
|
luaMetadata["remove_pomerium_authorization"] = &structpb.Value{
|
||||||
|
@ -264,7 +346,7 @@ func (b *Builder) buildPolicyRoutes(
|
||||||
if policy.IsForKubernetes() {
|
if policy.IsForKubernetes() {
|
||||||
policyID, _ := policy.RouteID()
|
policyID, _ := policy.RouteID()
|
||||||
for _, hdr := range b.reproxy.GetPolicyIDHeaders(policyID) {
|
for _, hdr := range b.reproxy.GetPolicyIDHeaders(policyID) {
|
||||||
envoyRoute.RequestHeadersToAdd = append(envoyRoute.RequestHeadersToAdd,
|
route.RequestHeadersToAdd = append(route.RequestHeadersToAdd,
|
||||||
&envoy_config_core_v3.HeaderValueOption{
|
&envoy_config_core_v3.HeaderValueOption{
|
||||||
Header: &envoy_config_core_v3.HeaderValue{
|
Header: &envoy_config_core_v3.HeaderValue{
|
||||||
Key: hdr[0],
|
Key: hdr[0],
|
||||||
|
@ -275,13 +357,10 @@ func (b *Builder) buildPolicyRoutes(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
envoyRoute.Metadata.FilterMetadata = map[string]*structpb.Struct{
|
route.Metadata.FilterMetadata = map[string]*structpb.Struct{
|
||||||
"envoy.filters.http.lua": {Fields: luaMetadata},
|
"envoy.filters.http.lua": {Fields: luaMetadata},
|
||||||
}
|
}
|
||||||
|
return route, nil
|
||||||
routes = append(routes, envoyRoute)
|
|
||||||
}
|
|
||||||
return routes, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) buildPolicyRouteRedirectAction(r *config.PolicyRedirect) (*envoy_config_route_v3.RedirectAction, error) {
|
func (b *Builder) buildPolicyRouteRedirectAction(r *config.PolicyRedirect) (*envoy_config_route_v3.RedirectAction, error) {
|
||||||
|
@ -420,9 +499,6 @@ func mkRouteMatch(policy *config.Policy) *envoy_config_route_v3.RouteMatch {
|
||||||
case policy.Regex != "":
|
case policy.Regex != "":
|
||||||
match.PathSpecifier = &envoy_config_route_v3.RouteMatch_SafeRegex{
|
match.PathSpecifier = &envoy_config_route_v3.RouteMatch_SafeRegex{
|
||||||
SafeRegex: &envoy_type_matcher_v3.RegexMatcher{
|
SafeRegex: &envoy_type_matcher_v3.RegexMatcher{
|
||||||
EngineType: &envoy_type_matcher_v3.RegexMatcher_GoogleRe2{
|
|
||||||
GoogleRe2: &envoy_type_matcher_v3.RegexMatcher_GoogleRE2{},
|
|
||||||
},
|
|
||||||
Regex: policy.Regex,
|
Regex: policy.Regex,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -436,6 +512,26 @@ func mkRouteMatch(policy *config.Policy) *envoy_config_route_v3.RouteMatch {
|
||||||
return match
|
return match
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mkRouteMatchForHost(
|
||||||
|
policy *config.Policy,
|
||||||
|
host string,
|
||||||
|
) *envoy_config_route_v3.RouteMatch {
|
||||||
|
match := mkRouteMatch(policy)
|
||||||
|
match.Headers = append(match.Headers, &envoy_config_route_v3.HeaderMatcher{
|
||||||
|
Name: ":authority",
|
||||||
|
HeaderMatchSpecifier: &envoy_config_route_v3.HeaderMatcher_StringMatch{
|
||||||
|
StringMatch: &envoy_type_matcher_v3.StringMatcher{
|
||||||
|
MatchPattern: &envoy_type_matcher_v3.StringMatcher_SafeRegex{
|
||||||
|
SafeRegex: &envoy_type_matcher_v3.RegexMatcher{
|
||||||
|
Regex: config.WildcardToRegex(host),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
|
||||||
func getRequestHeadersToRemove(options *config.Options, policy *config.Policy) []string {
|
func getRequestHeadersToRemove(options *config.Options, policy *config.Policy) []string {
|
||||||
requestHeadersToRemove := policy.RemoveRequestHeaders
|
requestHeadersToRemove := policy.RemoveRequestHeaders
|
||||||
if !policy.PassIdentityHeaders {
|
if !policy.PassIdentityHeaders {
|
||||||
|
@ -489,9 +585,6 @@ func getRewriteOptions(policy *config.Policy) (prefixRewrite string, regexRewrit
|
||||||
} else if policy.RegexRewritePattern != "" {
|
} else if policy.RegexRewritePattern != "" {
|
||||||
regexRewrite = &envoy_type_matcher_v3.RegexMatchAndSubstitute{
|
regexRewrite = &envoy_type_matcher_v3.RegexMatchAndSubstitute{
|
||||||
Pattern: &envoy_type_matcher_v3.RegexMatcher{
|
Pattern: &envoy_type_matcher_v3.RegexMatcher{
|
||||||
EngineType: &envoy_type_matcher_v3.RegexMatcher_GoogleRe2{
|
|
||||||
GoogleRe2: &envoy_type_matcher_v3.RegexMatcher_GoogleRE2{},
|
|
||||||
},
|
|
||||||
Regex: policy.RegexRewritePattern,
|
Regex: policy.RegexRewritePattern,
|
||||||
},
|
},
|
||||||
Substitution: policy.RegexRewriteSubstitution,
|
Substitution: policy.RegexRewriteSubstitution,
|
||||||
|
@ -517,9 +610,6 @@ func setHostRewriteOptions(policy *config.Policy, action *envoy_config_route_v3.
|
||||||
action.HostRewriteSpecifier = &envoy_config_route_v3.RouteAction_HostRewritePathRegex{
|
action.HostRewriteSpecifier = &envoy_config_route_v3.RouteAction_HostRewritePathRegex{
|
||||||
HostRewritePathRegex: &envoy_type_matcher_v3.RegexMatchAndSubstitute{
|
HostRewritePathRegex: &envoy_type_matcher_v3.RegexMatchAndSubstitute{
|
||||||
Pattern: &envoy_type_matcher_v3.RegexMatcher{
|
Pattern: &envoy_type_matcher_v3.RegexMatcher{
|
||||||
EngineType: &envoy_type_matcher_v3.RegexMatcher_GoogleRe2{
|
|
||||||
GoogleRe2: &envoy_type_matcher_v3.RegexMatcher_GoogleRE2{},
|
|
||||||
},
|
|
||||||
Regex: policy.HostPathRegexRewritePattern,
|
Regex: policy.HostPathRegexRewritePattern,
|
||||||
},
|
},
|
||||||
Substitution: policy.HostPathRegexRewriteSubstitution,
|
Substitution: policy.HostPathRegexRewriteSubstitution,
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/pomerium/pomerium/config/envoyconfig/filemgr"
|
"github.com/pomerium/pomerium/config/envoyconfig/filemgr"
|
||||||
"github.com/pomerium/pomerium/internal/testutil"
|
"github.com/pomerium/pomerium/internal/testutil"
|
||||||
"github.com/pomerium/pomerium/internal/urlutil"
|
"github.com/pomerium/pomerium/internal/urlutil"
|
||||||
|
"github.com/pomerium/pomerium/pkg/cryptutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func policyNameFunc() func(*config.Policy) string {
|
func policyNameFunc() func(*config.Policy) string {
|
||||||
|
@ -293,9 +294,10 @@ func TestTimeouts(t *testing.T) {
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
b := &Builder{filemgr: filemgr.NewManager()}
|
b := &Builder{filemgr: filemgr.NewManager()}
|
||||||
routes, err := b.buildPolicyRoutes(&config.Options{
|
routes, err := b.buildRoutesForPoliciesWithHost(&config.Config{Options: &config.Options{
|
||||||
CookieName: "pomerium",
|
CookieName: "pomerium",
|
||||||
DefaultUpstreamTimeout: time.Second * 3,
|
DefaultUpstreamTimeout: time.Second * 3,
|
||||||
|
SharedKey: cryptutil.NewBase64Key(),
|
||||||
Policies: []config.Policy{
|
Policies: []config.Policy{
|
||||||
{
|
{
|
||||||
From: "https://example.com",
|
From: "https://example.com",
|
||||||
|
@ -305,7 +307,7 @@ func TestTimeouts(t *testing.T) {
|
||||||
AllowWebsockets: tc.allowWebsockets,
|
AllowWebsockets: tc.allowWebsockets,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, "example.com", false)
|
}}, "example.com")
|
||||||
if !assert.NoError(t, err, "%v", tc) || !assert.Len(t, routes, 1, tc) || !assert.NotNil(t, routes[0].GetRoute(), "%v", tc) {
|
if !assert.NoError(t, err, "%v", tc) || !assert.Len(t, routes, 1, tc) || !assert.NotNil(t, routes[0].GetRoute(), "%v", tc) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -347,9 +349,10 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
ten := time.Second * 10
|
ten := time.Second * 10
|
||||||
|
|
||||||
b := &Builder{filemgr: filemgr.NewManager()}
|
b := &Builder{filemgr: filemgr.NewManager()}
|
||||||
routes, err := b.buildPolicyRoutes(&config.Options{
|
routes, err := b.buildRoutesForPoliciesWithHost(&config.Config{Options: &config.Options{
|
||||||
CookieName: "pomerium",
|
CookieName: "pomerium",
|
||||||
DefaultUpstreamTimeout: time.Second * 3,
|
DefaultUpstreamTimeout: time.Second * 3,
|
||||||
|
SharedKey: cryptutil.NewBase64Key(),
|
||||||
Policies: []config.Policy{
|
Policies: []config.Policy{
|
||||||
{
|
{
|
||||||
From: "https://ignore.example.com",
|
From: "https://ignore.example.com",
|
||||||
|
@ -409,7 +412,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
UpstreamTimeout: &ten,
|
UpstreamTimeout: &ten,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, "example.com", false)
|
}}, "example.com")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
testutil.AssertProtoJSONEqual(t, `
|
testutil.AssertProtoJSONEqual(t, `
|
||||||
|
@ -603,7 +606,6 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
"name": "policy-4",
|
"name": "policy-4",
|
||||||
"match": {
|
"match": {
|
||||||
"safeRegex": {
|
"safeRegex": {
|
||||||
"googleRe2": {},
|
|
||||||
"regex": "^/[a]+$"
|
"regex": "^/[a]+$"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -904,18 +906,19 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
`, routes)
|
`, routes)
|
||||||
|
|
||||||
t.Run("fronting-authenticate", func(t *testing.T) {
|
t.Run("fronting-authenticate", func(t *testing.T) {
|
||||||
routes, err := b.buildPolicyRoutes(&config.Options{
|
routes, err := b.buildRoutesForPoliciesWithHost(&config.Config{Options: &config.Options{
|
||||||
AuthenticateURLString: "https://authenticate.example.com",
|
AuthenticateURLString: "https://authenticate.example.com",
|
||||||
Services: "proxy",
|
Services: "proxy",
|
||||||
CookieName: "pomerium",
|
CookieName: "pomerium",
|
||||||
DefaultUpstreamTimeout: time.Second * 3,
|
DefaultUpstreamTimeout: time.Second * 3,
|
||||||
|
SharedKey: cryptutil.NewBase64Key(),
|
||||||
Policies: []config.Policy{
|
Policies: []config.Policy{
|
||||||
{
|
{
|
||||||
From: "https://authenticate.example.com",
|
From: "https://authenticate.example.com",
|
||||||
PassIdentityHeaders: true,
|
PassIdentityHeaders: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, "authenticate.example.com", false)
|
}}, "authenticate.example.com")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
testutil.AssertProtoJSONEqual(t, `
|
testutil.AssertProtoJSONEqual(t, `
|
||||||
|
@ -987,9 +990,10 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("tcp", func(t *testing.T) {
|
t.Run("tcp", func(t *testing.T) {
|
||||||
routes, err := b.buildPolicyRoutes(&config.Options{
|
routes, err := b.buildRoutesForPoliciesWithHost(&config.Config{Options: &config.Options{
|
||||||
CookieName: "pomerium",
|
CookieName: "pomerium",
|
||||||
DefaultUpstreamTimeout: time.Second * 3,
|
DefaultUpstreamTimeout: time.Second * 3,
|
||||||
|
SharedKey: cryptutil.NewBase64Key(),
|
||||||
Policies: []config.Policy{
|
Policies: []config.Policy{
|
||||||
{
|
{
|
||||||
From: "tcp+https://example.com:22",
|
From: "tcp+https://example.com:22",
|
||||||
|
@ -1001,7 +1005,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
UpstreamTimeout: &ten,
|
UpstreamTimeout: &ten,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, "example.com:22", false)
|
}}, "example.com:22")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
testutil.AssertProtoJSONEqual(t, `
|
testutil.AssertProtoJSONEqual(t, `
|
||||||
|
@ -1133,11 +1137,12 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("remove-pomerium-headers", func(t *testing.T) {
|
t.Run("remove-pomerium-headers", func(t *testing.T) {
|
||||||
routes, err := b.buildPolicyRoutes(&config.Options{
|
routes, err := b.buildRoutesForPoliciesWithHost(&config.Config{Options: &config.Options{
|
||||||
AuthenticateURLString: "https://authenticate.example.com",
|
AuthenticateURLString: "https://authenticate.example.com",
|
||||||
Services: "proxy",
|
Services: "proxy",
|
||||||
CookieName: "pomerium",
|
CookieName: "pomerium",
|
||||||
DefaultUpstreamTimeout: time.Second * 3,
|
DefaultUpstreamTimeout: time.Second * 3,
|
||||||
|
SharedKey: cryptutil.NewBase64Key(),
|
||||||
JWTClaimsHeaders: map[string]string{
|
JWTClaimsHeaders: map[string]string{
|
||||||
"x-email": "email",
|
"x-email": "email",
|
||||||
},
|
},
|
||||||
|
@ -1146,7 +1151,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
From: "https://from.example.com",
|
From: "https://from.example.com",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, "from.example.com", false)
|
}}, "from.example.com")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
testutil.AssertProtoJSONEqual(t, `
|
testutil.AssertProtoJSONEqual(t, `
|
||||||
|
@ -1224,9 +1229,10 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
|
||||||
}(getClusterID)
|
}(getClusterID)
|
||||||
getClusterID = policyNameFunc()
|
getClusterID = policyNameFunc()
|
||||||
b := &Builder{filemgr: filemgr.NewManager()}
|
b := &Builder{filemgr: filemgr.NewManager()}
|
||||||
routes, err := b.buildPolicyRoutes(&config.Options{
|
routes, err := b.buildRoutesForPoliciesWithHost(&config.Config{Options: &config.Options{
|
||||||
CookieName: "pomerium",
|
CookieName: "pomerium",
|
||||||
DefaultUpstreamTimeout: time.Second * 3,
|
DefaultUpstreamTimeout: time.Second * 3,
|
||||||
|
SharedKey: cryptutil.NewBase64Key(),
|
||||||
Policies: []config.Policy{
|
Policies: []config.Policy{
|
||||||
{
|
{
|
||||||
From: "https://example.com",
|
From: "https://example.com",
|
||||||
|
@ -1266,7 +1272,7 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
|
||||||
HostPathRegexRewriteSubstitution: "\\1",
|
HostPathRegexRewriteSubstitution: "\\1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, "example.com", false)
|
}}, "example.com")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
testutil.AssertProtoJSONEqual(t, `
|
testutil.AssertProtoJSONEqual(t, `
|
||||||
|
@ -1410,7 +1416,6 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
|
||||||
"autoHostRewrite": true,
|
"autoHostRewrite": true,
|
||||||
"regexRewrite": {
|
"regexRewrite": {
|
||||||
"pattern": {
|
"pattern": {
|
||||||
"googleRe2": {},
|
|
||||||
"regex": "^/service/([^/]+)(/.*)$"
|
"regex": "^/service/([^/]+)(/.*)$"
|
||||||
},
|
},
|
||||||
"substitution": "\\2/instance/\\1"
|
"substitution": "\\2/instance/\\1"
|
||||||
|
@ -1595,7 +1600,6 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
|
||||||
"route": {
|
"route": {
|
||||||
"hostRewritePathRegex": {
|
"hostRewritePathRegex": {
|
||||||
"pattern": {
|
"pattern": {
|
||||||
"googleRe2": {},
|
|
||||||
"regex": "^/(.+)/.+$"
|
"regex": "^/(.+)/.+$"
|
||||||
},
|
},
|
||||||
"substitution": "\\1"
|
"substitution": "\\1"
|
||||||
|
|
47
config/from.go
Normal file
47
config/from.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pomerium/pomerium/internal/urlutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FromURLMatchesRequestURL returns true if the from URL matches the request URL.
|
||||||
|
func FromURLMatchesRequestURL(fromURL, requestURL *url.URL) bool {
|
||||||
|
for _, domain := range urlutil.GetDomainsForURL(fromURL) {
|
||||||
|
if domain == requestURL.Host {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(domain, "*") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
reStr := WildcardToRegex(domain)
|
||||||
|
re := regexp.MustCompile(reStr)
|
||||||
|
if re.MatchString(requestURL.Host) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// WildcardToRegex converts a wildcard string to a regular expression.
|
||||||
|
func WildcardToRegex(wildcard string) string {
|
||||||
|
var b strings.Builder
|
||||||
|
b.WriteByte('^')
|
||||||
|
for {
|
||||||
|
idx := strings.IndexByte(wildcard, '*')
|
||||||
|
if idx < 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
b.WriteString(regexp.QuoteMeta(wildcard[:idx]))
|
||||||
|
b.WriteString("(.*)")
|
||||||
|
wildcard = wildcard[idx+1:]
|
||||||
|
}
|
||||||
|
b.WriteString(regexp.QuoteMeta(wildcard))
|
||||||
|
b.WriteByte('$')
|
||||||
|
return b.String()
|
||||||
|
}
|
38
config/from_test.go
Normal file
38
config/from_test.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/pomerium/pomerium/internal/urlutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFromURLMatchesRequestURL(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
for _, tc := range []struct {
|
||||||
|
pattern string
|
||||||
|
input string
|
||||||
|
matches bool
|
||||||
|
}{
|
||||||
|
{"https://from.example.com", "https://from.example.com/some/path", true},
|
||||||
|
{"https://from.example.com", "https://to.example.com/some/path", false},
|
||||||
|
{"https://*.example.com", "https://from.example.com/some/path", true},
|
||||||
|
{"https://*.example.com", "https://example.com/some/path", false},
|
||||||
|
} {
|
||||||
|
fromURL := urlutil.MustParseAndValidateURL(tc.pattern)
|
||||||
|
requestURL := urlutil.MustParseAndValidateURL(tc.input)
|
||||||
|
assert.Equal(t, tc.matches, FromURLMatchesRequestURL(&fromURL, &requestURL),
|
||||||
|
"from-url: %s\nrequest-url: %s", tc.pattern, tc.input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWildcardToRegex(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
re, err := regexp.Compile(WildcardToRegex("*.internal.*.example.com"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, re.MatchString("a.internal.b.example.com"))
|
||||||
|
}
|
|
@ -595,12 +595,7 @@ func (p *Policy) Matches(requestURL url.URL) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure one of the host domains matches the incoming url
|
if !FromURLMatchesRequestURL(fromURL, &requestURL) {
|
||||||
found := false
|
|
||||||
for _, host := range urlutil.GetDomainsForURL(fromURL) {
|
|
||||||
found = found || host == requestURL.Host
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
@ -439,7 +440,7 @@ func sourceHostnames(cfg *config.Config) []string {
|
||||||
|
|
||||||
dedupe := map[string]struct{}{}
|
dedupe := map[string]struct{}{}
|
||||||
for _, p := range policies {
|
for _, p := range policies {
|
||||||
if u, _ := urlutil.ParseAndValidateURL(p.From); u != nil {
|
if u, _ := urlutil.ParseAndValidateURL(p.From); u != nil && !strings.Contains(u.Host, "*") {
|
||||||
dedupe[u.Hostname()] = struct{}{}
|
dedupe[u.Hostname()] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue