config: add support for codec_type (#2156)

* config: add support for codec_type

* add comma

* fix warning block

* fix docs
This commit is contained in:
Caleb Doxsey 2021-04-30 07:21:40 -06:00 committed by GitHub
parent 0adbf4f24c
commit 699ebf061a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 838 additions and 646 deletions

75
config/codec_type.go Normal file
View file

@ -0,0 +1,75 @@
package config
import (
"encoding/json"
"fmt"
"reflect"
"strings"
envoy_http_connection_manager "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
"github.com/mitchellh/mapstructure"
)
// The CodecType specifies which codec to use for downstream connections.
type CodecType string
// CodecTypes
const (
CodecTypeAuto CodecType = "auto"
CodecTypeHTTP1 CodecType = "http1"
CodecTypeHTTP2 CodecType = "http2"
)
// ParseCodecType parses the codec type.
func ParseCodecType(raw string) (CodecType, error) {
switch CodecType(strings.TrimSpace(strings.ToLower(raw))) {
case CodecTypeAuto:
return CodecTypeAuto, nil
case CodecTypeHTTP1:
return CodecTypeHTTP1, nil
case CodecTypeHTTP2:
return CodecTypeHTTP2, nil
}
return CodecTypeAuto, fmt.Errorf("invalid codec type: %s", raw)
}
// CodecTypeFromEnvoy converts an envoy codec type into a config codec type.
func CodecTypeFromEnvoy(envoyCodecType envoy_http_connection_manager.HttpConnectionManager_CodecType) CodecType {
switch envoyCodecType {
case envoy_http_connection_manager.HttpConnectionManager_HTTP1:
return CodecTypeHTTP1
case envoy_http_connection_manager.HttpConnectionManager_HTTP2:
return CodecTypeHTTP2
}
return CodecTypeAuto
}
// ToEnvoy converts the codec type to an envoy codec type.
func (codecType CodecType) ToEnvoy() envoy_http_connection_manager.HttpConnectionManager_CodecType {
switch codecType {
case CodecTypeHTTP1:
return envoy_http_connection_manager.HttpConnectionManager_HTTP1
case CodecTypeHTTP2:
return envoy_http_connection_manager.HttpConnectionManager_HTTP2
}
return envoy_http_connection_manager.HttpConnectionManager_AUTO
}
func decodeCodecTypeHookFunc() mapstructure.DecodeHookFunc {
return func(f, t reflect.Type, data interface{}) (interface{}, error) {
if t != reflect.TypeOf(CodecType("")) {
return data, nil
}
bs, err := json.Marshal(data)
if err != nil {
return nil, err
}
var raw string
err = json.Unmarshal(bs, &raw)
if err != nil {
return nil, err
}
return ParseCodecType(raw)
}
}

View file

@ -23,14 +23,10 @@ var (
errEitherToOrRedirectRequired = errors.New("policy should have either `to` or `redirect` defined") errEitherToOrRedirectRequired = errors.New("policy should have either `to` or `redirect` defined")
) )
var ( var protoPartial = protojson.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true}
protoPartial = protojson.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true}
)
var (
// ViperPolicyHooks are used to decode options and policy coming from YAML and env vars // ViperPolicyHooks are used to decode options and policy coming from YAML and env vars
ViperPolicyHooks = viper.DecodeHook(mapstructure.ComposeDecodeHookFunc( var ViperPolicyHooks = viper.DecodeHook(mapstructure.ComposeDecodeHookFunc(
mapstructure.StringToTimeDurationHookFunc(), mapstructure.StringToTimeDurationHookFunc(),
mapstructure.StringToSliceHookFunc(","), mapstructure.StringToSliceHookFunc(","),
// decode policy including all protobuf-native notations - i.e. duration as `1s` // decode policy including all protobuf-native notations - i.e. duration as `1s`
@ -39,5 +35,5 @@ var (
// parse base-64 encoded POLICY that is bound to environment variable // parse base-64 encoded POLICY that is bound to environment variable
DecodePolicyBase64Hook(), DecodePolicyBase64Hook(),
decodeJWTClaimHeadersHookFunc(), decodeJWTClaimHeadersHookFunc(),
decodeCodecTypeHookFunc(),
)) ))
)

View file

@ -453,7 +453,7 @@ func (b *Builder) buildMainHTTPConnectionManagerFilter(
return nil, err return nil, err
} }
tc := marshalAny(&envoy_http_connection_manager.HttpConnectionManager{ tc := marshalAny(&envoy_http_connection_manager.HttpConnectionManager{
CodecType: envoy_http_connection_manager.HttpConnectionManager_AUTO, CodecType: options.GetCodecType().ToEnvoy(),
StatPrefix: "ingress", StatPrefix: "ingress",
RouteSpecifier: &envoy_http_connection_manager.HttpConnectionManager_RouteConfig{ RouteSpecifier: &envoy_http_connection_manager.HttpConnectionManager_RouteConfig{
RouteConfig: rc, RouteConfig: rc,

View file

@ -290,6 +290,9 @@ type Options struct {
// ProgrammaticRedirectDomainWhitelist restricts the allowed redirect URLs when using programmatic login. // 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 ProgrammaticRedirectDomainWhitelist []string `mapstructure:"programmatic_redirect_domain_whitelist" yaml:"programmatic_redirect_domain_whitelist,omitempty" json:"programmatic_redirect_domain_whitelist,omitempty"` //nolint
// CodecType is the codec to use for downstream connections.
CodecType CodecType `mapstructure:"codec_type" yaml:"codec_type"`
AuditKey *PublicKeyEncryptionKeyOptions `mapstructure:"audit_key"` AuditKey *PublicKeyEncryptionKeyOptions `mapstructure:"audit_key"`
} }
@ -940,6 +943,14 @@ func (o *Options) GetQPS() float64 {
return o.QPS return o.QPS
} }
// GetCodecType gets a codec type.
func (o *Options) GetCodecType() CodecType {
if IsAll(o.Services) && o.CodecType == CodecTypeAuto {
return CodecTypeHTTP1
}
return o.CodecType
}
// Checksum returns the checksum of the current options struct // Checksum returns the checksum of the current options struct
func (o *Options) Checksum() uint64 { func (o *Options) Checksum() uint64 {
return hashutil.MustHash(o) return hashutil.MustHash(o)
@ -1177,6 +1188,9 @@ func (o *Options) ApplySettings(settings *config.Settings) {
Data: base64.StdEncoding.EncodeToString(settings.AuditKey.GetData()), Data: base64.StdEncoding.EncodeToString(settings.AuditKey.GetData()),
} }
} }
if settings.CodecType != nil {
o.CodecType = CodecTypeFromEnvoy(settings.GetCodecType())
}
} }
func dataDir() string { func dataDir() string {

View file

@ -926,6 +926,40 @@ Do not append proxy IP address to `x-forwarded-for` HTTP header. See [Envoy](htt
The number of trusted reverse proxies in front of pomerium. This affects `x-forwarded-proto` header and [`x-envoy-external-address` header](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#x-envoy-external-address), which reports tursted client address. [Envoy](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers.html?highlight=xff_num_trusted_hops#x-forwarded-for) docs for more detail. The number of trusted reverse proxies in front of pomerium. This affects `x-forwarded-proto` header and [`x-envoy-external-address` header](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#x-envoy-external-address), which reports tursted client address. [Envoy](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers.html?highlight=xff_num_trusted_hops#x-forwarded-for) docs for more detail.
### Codec Type
- Environment Variable: `CODEC_TYPE`
- Config File Key: `codec_type`
- Type: `string`
- Default: `auto` (`http1` in all-in-one mode)
Specifies the codec to use for downstream connections. Either `auto`, `http1` or `http2`.
When `auto` is specified the codec will be determined via TLS ALPN or protocol inference.
:::warning
With HTTP/2, browsers typically coalesce connections for the same IP address that use the same
TLS certificate. For example, you may have `authenticate.localhost.pomerium.io` and
`example.localhost.pomerium.io` using the same wildcard certificate (`*.localhost.pomerium.io`)
and both pointing to `127.0.0.1`. Your browser sees this and re-uses the initial connection
it makes to `example` for `authenticate`. But unfortunately the routes necessary to handle
`authenticate` don't exist on `example` so the proxy cannot handle the request.
If this happens Pomerium will respond with a `421 Misdirected Request` status. Most browsers will attempt to
make the request on a new HTTP/2 connection. However not all browsers implement this behavior
(notably Safari), and users may end up seeing a blank page instead.
If you see this happen, there are several ways to mitigate the problem:
1. Don't re-use TLS certificates for shared IP domains.
2. Don't re-use IP addresses for shared TLS certificates.
3. Don't use HTTP/2.
More details on this problem are available in [Github Issue #2150](https://github.com/pomerium/pomerium/issues/2150).
:::
## Data Broker Service ## Data Broker Service
The databroker service is used for storing user session data. The databroker service is used for storing user session data.

View file

@ -1046,6 +1046,40 @@ settings:
The number of trusted reverse proxies in front of pomerium. This affects `x-forwarded-proto` header and [`x-envoy-external-address` header](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#x-envoy-external-address), which reports tursted client address. [Envoy](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers.html?highlight=xff_num_trusted_hops#x-forwarded-for) docs for more detail. The number of trusted reverse proxies in front of pomerium. This affects `x-forwarded-proto` header and [`x-envoy-external-address` header](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#x-envoy-external-address), which reports tursted client address. [Envoy](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers.html?highlight=xff_num_trusted_hops#x-forwarded-for) docs for more detail.
shortdoc: | shortdoc: |
The number of trusted reverse proxies in front of pomerium. The number of trusted reverse proxies in front of pomerium.
- name: "Codec Type"
keys: ["codec_type"]
attributes: |
- Environment Variable: `CODEC_TYPE`
- Config File Key: `codec_type`
- Type: `string`
- Default: `auto` (`http1` in all-in-one mode)
doc: |
Specifies the codec to use for downstream connections. Either `auto`, `http1` or `http2`.
When `auto` is specified the codec will be determined via TLS ALPN or protocol inference.
:::warning
With HTTP/2, browsers typically coalesce connections for the same IP address that use the same
TLS certificate. For example, you may have `authenticate.localhost.pomerium.io` and
`example.localhost.pomerium.io` using the same wildcard certificate (`*.localhost.pomerium.io`)
and both pointing to `127.0.0.1`. Your browser sees this and re-uses the initial connection
it makes to `example` for `authenticate`. But unfortunately the routes necessary to handle
`authenticate` don't exist on `example` so the proxy cannot handle the request.
If this happens Pomerium will respond with a `421 Misdirected Request` status. Most browsers will attempt to
make the request on a new HTTP/2 connection. However not all browsers implement this behavior
(notably Safari), and users may end up seeing a blank page instead.
If you see this happen, there are several ways to mitigate the problem:
1. Don't re-use TLS certificates for shared IP domains.
2. Don't re-use IP addresses for shared TLS certificates.
3. Don't use HTTP/2.
More details on this problem are available in [Github Issue #2150](https://github.com/pomerium/pomerium/issues/2150).
:::
- name: "Data Broker Service" - name: "Data Broker Service"
doc: | doc: |
The databroker service is used for storing user session data. The databroker service is used for storing user session data.

File diff suppressed because it is too large Load diff

View file

@ -8,6 +8,7 @@ import "google/protobuf/duration.proto";
import "google/protobuf/struct.proto"; import "google/protobuf/struct.proto";
import "google/protobuf/timestamp.proto"; import "google/protobuf/timestamp.proto";
import "envoy/config/cluster/v3/cluster.proto"; import "envoy/config/cluster/v3/cluster.proto";
import "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto";
import "crypt/crypt.proto"; import "crypt/crypt.proto";
@ -183,6 +184,8 @@ message Settings {
optional uint32 xff_num_trusted_hops = 70; optional uint32 xff_num_trusted_hops = 70;
repeated string programmatic_redirect_domain_whitelist = 68; repeated string programmatic_redirect_domain_whitelist = 68;
optional pomerium.crypt.PublicKeyEncryptionKey audit_key = 72; optional pomerium.crypt.PublicKeyEncryptionKey audit_key = 72;
optional envoy.extensions.filters.network.http_connection_manager.v3
.HttpConnectionManager.CodecType codec_type = 73;
} }
// EnvoyConfigurationEvents is a list of envoy configuration events. // EnvoyConfigurationEvents is a list of envoy configuration events.

View file

@ -8,6 +8,7 @@ function join_by() {
_protos=( _protos=(
"envoy/annotations/deprecation.proto" "envoy/annotations/deprecation.proto"
"envoy/config/accesslog/v3/accesslog.proto"
"envoy/config/cluster/v3/circuit_breaker.proto" "envoy/config/cluster/v3/circuit_breaker.proto"
"envoy/config/cluster/v3/cluster.proto" "envoy/config/cluster/v3/cluster.proto"
"envoy/config/cluster/v3/filter.proto" "envoy/config/cluster/v3/filter.proto"
@ -24,17 +25,26 @@ _protos=(
"envoy/config/core/v3/protocol.proto" "envoy/config/core/v3/protocol.proto"
"envoy/config/core/v3/proxy_protocol.proto" "envoy/config/core/v3/proxy_protocol.proto"
"envoy/config/core/v3/socket_option.proto" "envoy/config/core/v3/socket_option.proto"
"envoy/config/core/v3/substitution_format_string.proto"
"envoy/config/endpoint/v3/endpoint_components.proto" "envoy/config/endpoint/v3/endpoint_components.proto"
"envoy/config/endpoint/v3/endpoint.proto" "envoy/config/endpoint/v3/endpoint.proto"
"envoy/config/route/v3/route_components.proto" "envoy/config/route/v3/route_components.proto"
"envoy/config/route/v3/route.proto"
"envoy/config/route/v3/scoped_route.proto"
"envoy/config/trace/v3/http_tracer.proto"
"envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto"
"envoy/service/auth/v3/attribute_context.proto" "envoy/service/auth/v3/attribute_context.proto"
"envoy/service/auth/v3/external_auth.proto" "envoy/service/auth/v3/external_auth.proto"
"envoy/type/http/v3/path_transformation.proto"
"envoy/type/matcher/v3/metadata.proto"
"envoy/type/matcher/v3/number.proto"
"envoy/type/matcher/v3/regex.proto" "envoy/type/matcher/v3/regex.proto"
"envoy/type/matcher/v3/string.proto" "envoy/type/matcher/v3/string.proto"
"envoy/type/matcher/v3/value.proto"
"envoy/type/metadata/v3/metadata.proto" "envoy/type/metadata/v3/metadata.proto"
"envoy/type/tracing/v3/custom_tag.proto" "envoy/type/tracing/v3/custom_tag.proto"
"envoy/type/v3/http.proto"
"envoy/type/v3/http_status.proto" "envoy/type/v3/http_status.proto"
"envoy/type/v3/http.proto"
"envoy/type/v3/percent.proto" "envoy/type/v3/percent.proto"
"envoy/type/v3/range.proto" "envoy/type/v3/range.proto"
"envoy/type/v3/semantic_version.proto" "envoy/type/v3/semantic_version.proto"