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,21 +23,17 @@ var (
errEitherToOrRedirectRequired = errors.New("policy should have either `to` or `redirect` defined")
)
var (
protoPartial = protojson.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true}
)
var protoPartial = protojson.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true}
var (
// ViperPolicyHooks are used to decode options and policy coming from YAML and env vars
ViperPolicyHooks = viper.DecodeHook(mapstructure.ComposeDecodeHookFunc(
mapstructure.StringToTimeDurationHookFunc(),
mapstructure.StringToSliceHookFunc(","),
// decode policy including all protobuf-native notations - i.e. duration as `1s`
// https://developers.google.com/protocol-buffers/docs/proto3#json
DecodePolicyHookFunc(),
// parse base-64 encoded POLICY that is bound to environment variable
DecodePolicyBase64Hook(),
decodeJWTClaimHeadersHookFunc(),
))
)
// ViperPolicyHooks are used to decode options and policy coming from YAML and env vars
var ViperPolicyHooks = viper.DecodeHook(mapstructure.ComposeDecodeHookFunc(
mapstructure.StringToTimeDurationHookFunc(),
mapstructure.StringToSliceHookFunc(","),
// decode policy including all protobuf-native notations - i.e. duration as `1s`
// https://developers.google.com/protocol-buffers/docs/proto3#json
DecodePolicyHookFunc(),
// parse base-64 encoded POLICY that is bound to environment variable
DecodePolicyBase64Hook(),
decodeJWTClaimHeadersHookFunc(),
decodeCodecTypeHookFunc(),
))

View file

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

View file

@ -290,6 +290,9 @@ type Options struct {
// 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
// CodecType is the codec to use for downstream connections.
CodecType CodecType `mapstructure:"codec_type" yaml:"codec_type"`
AuditKey *PublicKeyEncryptionKeyOptions `mapstructure:"audit_key"`
}
@ -940,6 +943,14 @@ func (o *Options) GetQPS() float64 {
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
func (o *Options) Checksum() uint64 {
return hashutil.MustHash(o)
@ -1177,6 +1188,9 @@ func (o *Options) ApplySettings(settings *config.Settings) {
Data: base64.StdEncoding.EncodeToString(settings.AuditKey.GetData()),
}
}
if settings.CodecType != nil {
o.CodecType = CodecTypeFromEnvoy(settings.GetCodecType())
}
}
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.
### 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
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.
shortdoc: |
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"
doc: |
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/timestamp.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";
@ -183,6 +184,8 @@ message Settings {
optional uint32 xff_num_trusted_hops = 70;
repeated string programmatic_redirect_domain_whitelist = 68;
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.

View file

@ -8,6 +8,7 @@ function join_by() {
_protos=(
"envoy/annotations/deprecation.proto"
"envoy/config/accesslog/v3/accesslog.proto"
"envoy/config/cluster/v3/circuit_breaker.proto"
"envoy/config/cluster/v3/cluster.proto"
"envoy/config/cluster/v3/filter.proto"
@ -24,17 +25,26 @@ _protos=(
"envoy/config/core/v3/protocol.proto"
"envoy/config/core/v3/proxy_protocol.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.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/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/string.proto"
"envoy/type/matcher/v3/value.proto"
"envoy/type/metadata/v3/metadata.proto"
"envoy/type/tracing/v3/custom_tag.proto"
"envoy/type/v3/http.proto"
"envoy/type/v3/http_status.proto"
"envoy/type/v3/http.proto"
"envoy/type/v3/percent.proto"
"envoy/type/v3/range.proto"
"envoy/type/v3/semantic_version.proto"