mirror of
https://github.com/pomerium/pomerium.git
synced 2025-08-06 10:21:05 +02:00
config: add support for host header rewriting (#1457)
* config: add support for host header rewriting * fix lint
This commit is contained in:
parent
29b2fa4e60
commit
6e385f800a
6 changed files with 189 additions and 29 deletions
|
@ -43,6 +43,12 @@ type Policy struct {
|
|||
RegexRewritePattern string `mapstructure:"regex_rewrite_pattern" yaml:"regex_rewrite_pattern,omitempty" json:"regex_rewrite_pattern,omitempty"`
|
||||
RegexRewriteSubstitution string `mapstructure:"regex_rewrite_substitution" yaml:"regex_rewrite_substitution,omitempty" json:"regex_rewrite_substitution,omitempty"` //nolint
|
||||
|
||||
// Host Rewrite Options
|
||||
HostRewrite string `mapstructure:"host_rewrite" yaml:"host_rewrite,omitempty" json:"host_rewrite,omitempty"`
|
||||
HostRewriteHeader string `mapstructure:"host_rewrite_header" yaml:"host_rewrite_header,omitempty" json:"host_rewrite_header,omitempty"`
|
||||
HostPathRegexRewritePattern string `mapstructure:"host_path_regex_rewrite_pattern" yaml:"host_path_regex_rewrite_pattern,omitempty" json:"host_path_regex_rewrite_pattern,omitempty"` //nolint
|
||||
HostPathRegexRewriteSubstitution string `mapstructure:"host_path_regex_rewrite_substitution" yaml:"host_path_regex_rewrite_substitution,omitempty" json:"host_path_regex_rewrite_substitution,omitempty"` //nolint
|
||||
|
||||
// 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"`
|
||||
|
|
|
@ -1057,6 +1057,28 @@ prefix_rewrite: /
|
|||
|
||||
A request to `https://from.example.com/admin` would be forwarded to `https://to.example.com/`.
|
||||
|
||||
### Host Rewrite
|
||||
|
||||
- `yaml`/`json` settings: `host_rewrite`, `host_rewrite_header`, `host_path_regex_rewrite_pattern`, `host_path_regex_rewrite_substitution`
|
||||
- Type: `string`
|
||||
- Optional
|
||||
- Example: `host_rewrite: "example.com"`
|
||||
|
||||
The `host` header can be customized via 3 mutually exclusive options:
|
||||
|
||||
1. `host_rewrite` which will rewrite the host to a new literal value.
|
||||
2. `host_rewrite_header` which will rewrite the host to match an incoming header value.
|
||||
3. `host_path_regex_rewrite_pattern`, `host_path_regex_rewrite_substitution` which will rewrite the host according to a regex matching the path. For example with the following config:
|
||||
|
||||
```yaml
|
||||
host_path_regex_rewrite_pattern: "^/(.+)/.+$"
|
||||
host_path_regex_rewrite_substitution: \1
|
||||
```
|
||||
|
||||
Would rewrite the host header to `example.com` given the path `/example.com/some/path`.
|
||||
|
||||
These options correspond to the envoy route action host related options, which can be found [here](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto.html#config-route-v3-routeaction).
|
||||
|
||||
### Public Access
|
||||
|
||||
- `yaml`/`json` setting: `allow_public_unauthenticated_access`
|
||||
|
|
3
go.mod
3
go.mod
|
@ -10,9 +10,8 @@ require (
|
|||
github.com/caddyserver/certmagic v0.12.0
|
||||
github.com/cenkalti/backoff/v4 v4.0.2
|
||||
github.com/cespare/xxhash/v2 v2.1.1
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354 // indirect
|
||||
github.com/coreos/go-oidc v2.2.1+incompatible
|
||||
github.com/envoyproxy/go-control-plane v0.9.6
|
||||
github.com/envoyproxy/go-control-plane v0.9.7-0.20200924180459-2fd2c9f35b9c
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/go-chi/chi v4.1.2+incompatible
|
||||
github.com/golang/mock v1.4.4
|
||||
|
|
6
go.sum
6
go.sum
|
@ -101,8 +101,6 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
|||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200313221541-5f7e5dd04533 h1:8wZizuKuZVu5COB7EsBYxBQz8nRcXXn5d4Gt91eJLvU=
|
||||
github.com/cncf/udpa/go v0.0.0-20200313221541-5f7e5dd04533/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354 h1:9kRtNpqLHbZVO/NNxhHp2ymxFxsHOe3x2efJGn//Tas=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6 h1:NmTXa/uVnDyp0TY5MKi197+3HWcnYWfnHGyaFthlnGw=
|
||||
|
@ -135,8 +133,8 @@ github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4s
|
|||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.6 h1:GgblEiDzxf5ajlAZY4aC8xp7DwkrGfauFNMGdB2bBv0=
|
||||
github.com/envoyproxy/go-control-plane v0.9.6/go.mod h1:GFqM7v0B62MraO4PWRedIbhThr/Rf7ev6aHOOPXeaDA=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7-0.20200924180459-2fd2c9f35b9c h1:BtHMDMV2uGF8AkmS5cTX9gii0WgO2RoPzxJWGwh7k6s=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7-0.20200924180459-2fd2c9f35b9c/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/golang/protobuf/ptypes/wrappers"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/httputil"
|
||||
|
@ -122,6 +123,29 @@ func buildPolicyRoutes(options *config.Options, domain string) []*envoy_config_r
|
|||
routeTimeout := getRouteTimeout(options, &policy)
|
||||
prefixRewrite, regexRewrite := getRewriteOptions(&policy)
|
||||
|
||||
routeAction := &envoy_config_route_v3.RouteAction{
|
||||
ClusterSpecifier: &envoy_config_route_v3.RouteAction_Cluster{
|
||||
Cluster: clusterName,
|
||||
},
|
||||
UpgradeConfigs: []*envoy_config_route_v3.RouteAction_UpgradeConfig{
|
||||
{
|
||||
UpgradeType: "websocket",
|
||||
Enabled: &wrappers.BoolValue{Value: policy.AllowWebsockets},
|
||||
},
|
||||
{
|
||||
UpgradeType: "spdy/3.1",
|
||||
Enabled: &wrappers.BoolValue{Value: policy.AllowSPDY},
|
||||
},
|
||||
},
|
||||
HostRewriteSpecifier: &envoy_config_route_v3.RouteAction_AutoHostRewrite{
|
||||
AutoHostRewrite: &wrappers.BoolValue{Value: !policy.PreserveHostHeader},
|
||||
},
|
||||
Timeout: routeTimeout,
|
||||
PrefixRewrite: prefixRewrite,
|
||||
RegexRewrite: regexRewrite,
|
||||
}
|
||||
setHostRewriteOptions(&policy, routeAction)
|
||||
|
||||
routes = append(routes, &envoy_config_route_v3.Route{
|
||||
Name: fmt.Sprintf("policy-%d", i),
|
||||
Match: match,
|
||||
|
@ -148,29 +172,7 @@ func buildPolicyRoutes(options *config.Options, domain string) []*envoy_config_r
|
|||
},
|
||||
},
|
||||
},
|
||||
Action: &envoy_config_route_v3.Route_Route{
|
||||
Route: &envoy_config_route_v3.RouteAction{
|
||||
ClusterSpecifier: &envoy_config_route_v3.RouteAction_Cluster{
|
||||
Cluster: clusterName,
|
||||
},
|
||||
UpgradeConfigs: []*envoy_config_route_v3.RouteAction_UpgradeConfig{
|
||||
{
|
||||
UpgradeType: "websocket",
|
||||
Enabled: &wrappers.BoolValue{Value: policy.AllowWebsockets},
|
||||
},
|
||||
{
|
||||
UpgradeType: "spdy/3.1",
|
||||
Enabled: &wrappers.BoolValue{Value: policy.AllowSPDY},
|
||||
},
|
||||
},
|
||||
HostRewriteSpecifier: &envoy_config_route_v3.RouteAction_AutoHostRewrite{
|
||||
AutoHostRewrite: &wrappers.BoolValue{Value: !policy.PreserveHostHeader},
|
||||
},
|
||||
Timeout: routeTimeout,
|
||||
PrefixRewrite: prefixRewrite,
|
||||
RegexRewrite: regexRewrite,
|
||||
},
|
||||
},
|
||||
Action: &envoy_config_route_v3.Route_Route{Route: routeAction},
|
||||
RequestHeadersToAdd: requestHeadersToAdd,
|
||||
RequestHeadersToRemove: requestHeadersToRemove,
|
||||
ResponseHeadersToAdd: responseHeadersToAdd,
|
||||
|
@ -269,6 +271,39 @@ func getRewriteOptions(policy *config.Policy) (prefixRewrite string, regexRewrit
|
|||
return prefixRewrite, regexRewrite
|
||||
}
|
||||
|
||||
func setHostRewriteOptions(policy *config.Policy, action *envoy_config_route_v3.RouteAction) {
|
||||
switch {
|
||||
case policy.HostRewrite != "":
|
||||
action.HostRewriteSpecifier = &envoy_config_route_v3.RouteAction_HostRewriteLiteral{
|
||||
HostRewriteLiteral: policy.HostRewrite,
|
||||
}
|
||||
case policy.HostRewriteHeader != "":
|
||||
action.HostRewriteSpecifier = &envoy_config_route_v3.RouteAction_HostRewriteHeader{
|
||||
HostRewriteHeader: policy.HostRewriteHeader,
|
||||
}
|
||||
case policy.HostPathRegexRewritePattern != "":
|
||||
action.HostRewriteSpecifier = &envoy_config_route_v3.RouteAction_HostRewritePathRegex{
|
||||
HostRewritePathRegex: &envoy_type_matcher_v3.RegexMatchAndSubstitute{
|
||||
Pattern: &envoy_type_matcher_v3.RegexMatcher{
|
||||
EngineType: &envoy_type_matcher_v3.RegexMatcher_GoogleRe2{
|
||||
GoogleRe2: &envoy_type_matcher_v3.RegexMatcher_GoogleRE2{},
|
||||
},
|
||||
Regex: policy.HostPathRegexRewritePattern,
|
||||
},
|
||||
Substitution: policy.HostPathRegexRewriteSubstitution,
|
||||
},
|
||||
}
|
||||
case policy.PreserveHostHeader:
|
||||
action.HostRewriteSpecifier = &envoy_config_route_v3.RouteAction_AutoHostRewrite{
|
||||
AutoHostRewrite: wrapperspb.Bool(false),
|
||||
}
|
||||
default:
|
||||
action.HostRewriteSpecifier = &envoy_config_route_v3.RouteAction_AutoHostRewrite{
|
||||
AutoHostRewrite: wrapperspb.Bool(true),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func hasPublicPolicyMatchingURL(options *config.Options, requestURL *url.URL) bool {
|
||||
for _, policy := range options.Policies {
|
||||
if policy.AllowPublicUnauthenticatedAccess && policy.Matches(requestURL) {
|
||||
|
|
|
@ -535,6 +535,25 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
|
|||
RegexRewritePattern: "^/service/([^/]+)(/.*)$",
|
||||
RegexRewriteSubstitution: "\\2/instance/\\1",
|
||||
},
|
||||
{
|
||||
Source: &config.StringURL{URL: mustParseURL("https://example.com")},
|
||||
Destination: mustParseURL("https://foo.example.com/bar"),
|
||||
PassIdentityHeaders: true,
|
||||
HostRewrite: "literal.example.com",
|
||||
},
|
||||
{
|
||||
Source: &config.StringURL{URL: mustParseURL("https://example.com")},
|
||||
Destination: mustParseURL("https://foo.example.com/bar"),
|
||||
PassIdentityHeaders: true,
|
||||
HostRewriteHeader: "HOST_HEADER",
|
||||
},
|
||||
{
|
||||
Source: &config.StringURL{URL: mustParseURL("https://example.com")},
|
||||
Destination: mustParseURL("https://foo.example.com/bar"),
|
||||
PassIdentityHeaders: true,
|
||||
HostPathRegexRewritePattern: "^/(.+)/.+$",
|
||||
HostPathRegexRewriteSubstitution: "\\1",
|
||||
},
|
||||
},
|
||||
}, "example.com")
|
||||
|
||||
|
@ -620,6 +639,87 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
|
|||
{ "enabled": false, "upgradeType": "spdy/3.1"}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "policy-3",
|
||||
"match": {
|
||||
"prefix": "/"
|
||||
},
|
||||
"metadata": {
|
||||
"filterMetadata": {
|
||||
"envoy.filters.http.lua": {
|
||||
"remove_impersonate_headers": false,
|
||||
"remove_pomerium_authorization": true,
|
||||
"remove_pomerium_cookie": "pomerium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"route": {
|
||||
"hostRewriteLiteral": "literal.example.com",
|
||||
"prefixRewrite": "/bar",
|
||||
"cluster": "policy-4",
|
||||
"timeout": "3s",
|
||||
"upgradeConfigs": [
|
||||
{ "enabled": false, "upgradeType": "websocket"},
|
||||
{ "enabled": false, "upgradeType": "spdy/3.1"}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "policy-4",
|
||||
"match": {
|
||||
"prefix": "/"
|
||||
},
|
||||
"metadata": {
|
||||
"filterMetadata": {
|
||||
"envoy.filters.http.lua": {
|
||||
"remove_impersonate_headers": false,
|
||||
"remove_pomerium_authorization": true,
|
||||
"remove_pomerium_cookie": "pomerium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"route": {
|
||||
"hostRewriteHeader": "HOST_HEADER",
|
||||
"prefixRewrite": "/bar",
|
||||
"cluster": "policy-5",
|
||||
"timeout": "3s",
|
||||
"upgradeConfigs": [
|
||||
{ "enabled": false, "upgradeType": "websocket"},
|
||||
{ "enabled": false, "upgradeType": "spdy/3.1"}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "policy-5",
|
||||
"match": {
|
||||
"prefix": "/"
|
||||
},
|
||||
"metadata": {
|
||||
"filterMetadata": {
|
||||
"envoy.filters.http.lua": {
|
||||
"remove_impersonate_headers": false,
|
||||
"remove_pomerium_authorization": true,
|
||||
"remove_pomerium_cookie": "pomerium"
|
||||
}
|
||||
}
|
||||
},
|
||||
"route": {
|
||||
"hostRewritePathRegex": {
|
||||
"pattern": {
|
||||
"googleRe2": {},
|
||||
"regex": "^/(.+)/.+$"
|
||||
},
|
||||
"substitution": "\\1"
|
||||
},
|
||||
"prefixRewrite": "/bar",
|
||||
"cluster": "policy-6",
|
||||
"timeout": "3s",
|
||||
"upgradeConfigs": [
|
||||
{ "enabled": false, "upgradeType": "websocket"},
|
||||
{ "enabled": false, "upgradeType": "spdy/3.1"}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
`, routes)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue