diff --git a/config/constants.go b/config/constants.go index 06e6202ee8e..93fdd62db2c 100644 --- a/config/constants.go +++ b/config/constants.go @@ -34,6 +34,7 @@ var ViperPolicyHooks = viper.DecodeHook(mapstructure.ComposeDecodeHookFunc( DecodePolicyHookFunc(), // parse base-64 encoded POLICY that is bound to environment variable DecodePolicyBase64Hook(), + decodeNullBoolHookFunc(), decodeJWTClaimHeadersHookFunc(), decodeCodecTypeHookFunc(), decodePPLPolicyHookFunc(), diff --git a/config/custom.go b/config/custom.go index bb68e24691d..812a8f42367 100644 --- a/config/custom.go +++ b/config/custom.go @@ -13,16 +13,35 @@ import ( envoy_config_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" "github.com/mitchellh/mapstructure" + "github.com/volatiletech/null/v9" "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" "gopkg.in/yaml.v3" - "github.com/pomerium/pomerium/pkg/policy/parser" - "github.com/pomerium/pomerium/internal/httputil" "github.com/pomerium/pomerium/internal/urlutil" + "github.com/pomerium/pomerium/pkg/policy/parser" ) +func decodeNullBoolHookFunc() mapstructure.DecodeHookFunc { + return func(f, t reflect.Type, data interface{}) (interface{}, error) { + if t != reflect.TypeOf(null.Bool{}) { + return data, nil + } + + bs, err := json.Marshal(data) + if err != nil { + return nil, err + } + var value null.Bool + err = json.Unmarshal(bs, &value) + if err != nil { + return nil, err + } + return value, nil + } +} + // JWTClaimHeaders are headers to add to a request based on IDP claims. type JWTClaimHeaders map[string]string diff --git a/config/envoyconfig/clusters.go b/config/envoyconfig/clusters.go index 7169b351d95..22a604e48dc 100644 --- a/config/envoyconfig/clusters.go +++ b/config/envoyconfig/clusters.go @@ -137,6 +137,28 @@ func (b *Builder) buildPolicyCluster(ctx context.Context, options *config.Option cluster := new(envoy_config_cluster_v3.Cluster) proto.Merge(cluster, policy.EnvoyOpts) + if options.EnvoyBindConfigFreebind.IsSet() || options.EnvoyBindConfigSourceAddress != "" { + cluster.UpstreamBindConfig = new(envoy_config_core_v3.BindConfig) + if options.EnvoyBindConfigFreebind.IsSet() { + cluster.UpstreamBindConfig.Freebind = wrapperspb.Bool(options.EnvoyBindConfigFreebind.Bool) + } + if options.EnvoyBindConfigSourceAddress != "" { + cluster.UpstreamBindConfig.SourceAddress = &envoy_config_core_v3.SocketAddress{ + Address: options.EnvoyBindConfigSourceAddress, + PortSpecifier: &envoy_config_core_v3.SocketAddress_PortValue{ + PortValue: 0, + }, + } + } else { + cluster.UpstreamBindConfig.SourceAddress = &envoy_config_core_v3.SocketAddress{ + Address: "0.0.0.0", + PortSpecifier: &envoy_config_core_v3.SocketAddress_PortValue{ + PortValue: 0, + }, + } + } + } + cluster.AltStatName = getClusterStatsName(policy) upstreamProtocol := getUpstreamProtocolForPolicy(ctx, policy) diff --git a/config/envoyconfig/clusters_test.go b/config/envoyconfig/clusters_test.go index b1e18ff65b2..0fcf5a1bffb 100644 --- a/config/envoyconfig/clusters_test.go +++ b/config/envoyconfig/clusters_test.go @@ -6,10 +6,12 @@ import ( "os" "path/filepath" "testing" + "time" envoy_config_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/volatiletech/null/v9" "google.golang.org/protobuf/types/known/wrapperspb" "github.com/pomerium/pomerium/config" @@ -828,6 +830,56 @@ func Test_validateClusters(t *testing.T) { } } +func Test_bindConfig(t *testing.T) { + ctx, clearTimeout := context.WithTimeout(context.Background(), time.Second*10) + defer clearTimeout() + + b := New("local-grpc", "local-http", filemgr.NewManager(), nil) + t.Run("no bind config", func(t *testing.T) { + cluster, err := b.buildPolicyCluster(ctx, &config.Options{}, &config.Policy{ + From: "https://from.example.com", + To: mustParseWeightedURLs(t, "https://to.example.com"), + }) + assert.NoError(t, err) + assert.Nil(t, cluster.UpstreamBindConfig) + }) + t.Run("freebind", func(t *testing.T) { + cluster, err := b.buildPolicyCluster(ctx, &config.Options{ + EnvoyBindConfigFreebind: null.BoolFrom(true), + }, &config.Policy{ + From: "https://from.example.com", + To: mustParseWeightedURLs(t, "https://to.example.com"), + }) + assert.NoError(t, err) + testutil.AssertProtoJSONEqual(t, ` + { + "freebind": true, + "sourceAddress": { + "address": "0.0.0.0", + "portValue": 0 + } + } + `, cluster.UpstreamBindConfig) + }) + t.Run("source address", func(t *testing.T) { + cluster, err := b.buildPolicyCluster(ctx, &config.Options{ + EnvoyBindConfigSourceAddress: "192.168.0.1", + }, &config.Policy{ + From: "https://from.example.com", + To: mustParseWeightedURLs(t, "https://to.example.com"), + }) + assert.NoError(t, err) + testutil.AssertProtoJSONEqual(t, ` + { + "sourceAddress": { + "address": "192.168.0.1", + "portValue": 0 + } + } + `, cluster.UpstreamBindConfig) + }) +} + func mustParseWeightedURLs(t *testing.T, urls ...string) []config.WeightedURL { wu, err := config.ParseWeightedUrls(urls...) require.NoError(t, err) diff --git a/config/options.go b/config/options.go index 18f6a50fdf7..ef0cdc71c3f 100644 --- a/config/options.go +++ b/config/options.go @@ -18,6 +18,7 @@ import ( "github.com/mitchellh/mapstructure" "github.com/spf13/viper" + "github.com/volatiletech/null/v9" "github.com/pomerium/pomerium/internal/directory/azure" "github.com/pomerium/pomerium/internal/directory/github" @@ -279,10 +280,12 @@ type Options struct { // see https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers.html?highlight=xff_num_trusted_hops#x-forwarded-for XffNumTrustedHops uint32 `mapstructure:"xff_num_trusted_hops" yaml:"xff_num_trusted_hops,omitempty" json:"xff_num_trusted_hops,omitempty"` - // Envoy bootstrap admin options. These do not support dynamic updates. - EnvoyAdminAccessLogPath string `mapstructure:"envoy_admin_access_log_path" yaml:"envoy_admin_access_log_path"` - EnvoyAdminProfilePath string `mapstructure:"envoy_admin_profile_path" yaml:"envoy_admin_profile_path"` - EnvoyAdminAddress string `mapstructure:"envoy_admin_address" yaml:"envoy_admin_address"` + // Envoy bootstrap options. These do not support dynamic updates. + EnvoyAdminAccessLogPath string `mapstructure:"envoy_admin_access_log_path" yaml:"envoy_admin_access_log_path"` + EnvoyAdminProfilePath string `mapstructure:"envoy_admin_profile_path" yaml:"envoy_admin_profile_path"` + EnvoyAdminAddress string `mapstructure:"envoy_admin_address" yaml:"envoy_admin_address"` + EnvoyBindConfigSourceAddress string `mapstructure:"envoy_bind_config_source_address" yaml:"envoy_bind_config_source_address,omitempty"` + EnvoyBindConfigFreebind null.Bool `mapstructure:"envoy_bind_config_freebind" yaml:"envoy_bind_config_freebind,omitempty"` // ProgrammaticRedirectDomainWhitelist restricts the allowed redirect URLs when using programmatic login. ProgrammaticRedirectDomainWhitelist []string `mapstructure:"programmatic_redirect_domain_whitelist" yaml:"programmatic_redirect_domain_whitelist,omitempty" json:"programmatic_redirect_domain_whitelist,omitempty"` //nolint diff --git a/docs/reference/readme.md b/docs/reference/readme.md index 7d74b676f59..3d7fb9da127 100644 --- a/docs/reference/readme.md +++ b/docs/reference/readme.md @@ -674,13 +674,13 @@ tracing_zipkin_endpoint | Url to the Zipkin HTTP endpoint. | ✅ Setting `use_proxy_protocol` will configure Pomerium to require the [HAProxy proxy protocol](https://www.haproxy.org/download/1.9/doc/proxy-protocol.txt) on incoming connections. Versions 1 and 2 of the protocol are supported. -### Envoy Admin Options -- Environment Variable: `ENVOY_ADMIN_ADDRESS`, `ENVOY_ADMIN_ACCESS_LOG_PATH`, `ENVOY_ADMIN_PROFILE_PATH` -- Config File Keys: `envoy_admin_address`, `envoy_admin_access_log_path`, `envoy_admin_profile_path` +### Envoy Bootstrap Options +- Environment Variable: `ENVOY_ADMIN_ADDRESS`, `ENVOY_ADMIN_ACCESS_LOG_PATH`, `ENVOY_ADMIN_PROFILE_PATH`, `ENVOY_BIND_CONFIG_FREEBIND`, `ENVOY_BIND_CONFIG_SOURCE_ADDRESS` +- Config File Keys: `envoy_admin_address`, `envoy_admin_access_log_path`, `envoy_admin_profile_path`, `envoy_bind_config_freebind`, `envoy_bind_config_source_address` - Type: `string` - Optional -These options customize Envoy's [bootstrap configuration](https://www.envoyproxy.io/docs/envoy/latest/operations/admin#operations-admin-interface). They cannot be modified at runtime. +The `envoy_admin` keys customize Envoy's [bootstrap configuration](https://www.envoyproxy.io/docs/envoy/latest/operations/admin#operations-admin-interface). The `envoy_bind_config` keys modify the [ClusterManager](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/bootstrap/v3/bootstrap.proto.html#config-bootstrap-v3-clustermanager) configuration. These options cannot be modified at runtime. ## Authenticate Service diff --git a/docs/reference/settings.yaml b/docs/reference/settings.yaml index d41904b6fdb..235e2d34fbf 100644 --- a/docs/reference/settings.yaml +++ b/docs/reference/settings.yaml @@ -764,15 +764,15 @@ settings: - Optional doc: | Setting `use_proxy_protocol` will configure Pomerium to require the [HAProxy proxy protocol](https://www.haproxy.org/download/1.9/doc/proxy-protocol.txt) on incoming connections. Versions 1 and 2 of the protocol are supported. - - name: "Envoy Admin Options" - keys: ["envoy_admin_options"] + - name: "Envoy Bootstrap Options" + keys: ["envoy_admin_address","envoy_admin_access_log_path","envoy_admin_profile_path","envoy_bind_config_freebind","envoy_bind_config_source_address"] attributes: | - - Environment Variable: `ENVOY_ADMIN_ADDRESS`, `ENVOY_ADMIN_ACCESS_LOG_PATH`, `ENVOY_ADMIN_PROFILE_PATH` - - Config File Keys: `envoy_admin_address`, `envoy_admin_access_log_path`, `envoy_admin_profile_path` + - Environment Variable: `ENVOY_ADMIN_ADDRESS`, `ENVOY_ADMIN_ACCESS_LOG_PATH`, `ENVOY_ADMIN_PROFILE_PATH`, `ENVOY_BIND_CONFIG_FREEBIND`, `ENVOY_BIND_CONFIG_SOURCE_ADDRESS` + - Config File Keys: `envoy_admin_address`, `envoy_admin_access_log_path`, `envoy_admin_profile_path`, `envoy_bind_config_freebind`, `envoy_bind_config_source_address` - Type: `string` - Optional doc: | - These options customize Envoy's [bootstrap configuration](https://www.envoyproxy.io/docs/envoy/latest/operations/admin#operations-admin-interface). They cannot be modified at runtime. + The `envoy_admin` keys customize Envoy's [bootstrap configuration](https://www.envoyproxy.io/docs/envoy/latest/operations/admin#operations-admin-interface). The `envoy_bind_config` keys modify the [ClusterManager](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/bootstrap/v3/bootstrap.proto.html#config-bootstrap-v3-clustermanager) configuration. These options cannot be modified at runtime. - name: "Authenticate Service" settings: - name: "Authenticate Callback Path" diff --git a/go.mod b/go.mod index 74f589d3b55..d56ce451e23 100644 --- a/go.mod +++ b/go.mod @@ -59,6 +59,7 @@ require ( github.com/tniswong/go.rfcx v0.0.0-20181019234604-07783c52761f github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 github.com/vektah/gqlparser v1.3.1 + github.com/volatiletech/null/v9 v9.0.0 github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da go.opencensus.io v0.23.0 go.uber.org/zap v1.19.1 diff --git a/go.sum b/go.sum index 0d5d5931abb..7417e14a61d 100644 --- a/go.sum +++ b/go.sum @@ -1327,6 +1327,8 @@ github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:tw github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/volatiletech/null/v9 v9.0.0 h1:JCdlHEiSRVxOi7/MABiEfdsqmuj9oTV20Ao7VvZ0JkE= +github.com/volatiletech/null/v9 v9.0.0/go.mod h1:zRFghPVahaiIMRXiUJrc6gsoG83Cm3ZoAfSTw7VHGQc= github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=