diff --git a/go.mod b/go.mod index 65218ed5b..08dd7dc9b 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/cenkalti/backoff/v4 v4.1.0 github.com/cespare/xxhash/v2 v2.1.1 github.com/coreos/go-oidc/v3 v3.0.0 - github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad + github.com/envoyproxy/go-control-plane v0.9.9-0.20201217023817-7fe139bd184a github.com/envoyproxy/protoc-gen-validate v0.4.1 github.com/fsnotify/fsnotify v1.4.9 github.com/go-chi/chi v1.5.4 diff --git a/go.sum b/go.sum index d29e27310..41695e3f6 100644 --- a/go.sum +++ b/go.sum @@ -170,8 +170,9 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF 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.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad h1:EmNYJhPYy0pOFjCx2PrgtaBXmee0iUX9hLlxE1xHOJE= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201217023817-7fe139bd184a h1:ADsmVb13JBufLsSrAAKQAJ+J7LunEE1GkVnsUl79Hhs= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201217023817-7fe139bd184a/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.4.1 h1:7dLaJvASGRD7X49jSCSXXHwKPm0ZN9r9kJD+p+vS7dM= github.com/envoyproxy/protoc-gen-validate v0.4.1/go.mod h1:E+IEazqdaWv3FrnGtZIu3b9fPFMK8AzeTTrk9SfVwWs= diff --git a/internal/controlplane/xds_cluster_test.go b/internal/controlplane/xds_cluster_test.go index b26cc27d1..490459584 100644 --- a/internal/controlplane/xds_cluster_test.go +++ b/internal/controlplane/xds_cluster_test.go @@ -42,7 +42,7 @@ func Test_buildPolicyTransportSocket(t *testing.T) { "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", "commonTlsContext": { - "alpnProtocols": ["http/1.1"], + "alpnProtocols": ["h2", "http/1.1"], "tlsParams": { "ecdhCurves": [ "X25519", @@ -77,7 +77,7 @@ func Test_buildPolicyTransportSocket(t *testing.T) { "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", "commonTlsContext": { - "alpnProtocols": ["http/1.1"], + "alpnProtocols": ["h2", "http/1.1"], "tlsParams": { "ecdhCurves": [ "X25519", @@ -112,7 +112,7 @@ func Test_buildPolicyTransportSocket(t *testing.T) { "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", "commonTlsContext": { - "alpnProtocols": ["http/1.1"], + "alpnProtocols": ["h2", "http/1.1"], "tlsParams": { "ecdhCurves": [ "X25519", @@ -148,7 +148,7 @@ func Test_buildPolicyTransportSocket(t *testing.T) { "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", "commonTlsContext": { - "alpnProtocols": ["http/1.1"], + "alpnProtocols": ["h2", "http/1.1"], "tlsParams": { "ecdhCurves": [ "X25519", @@ -184,7 +184,7 @@ func Test_buildPolicyTransportSocket(t *testing.T) { "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", "commonTlsContext": { - "alpnProtocols": ["http/1.1"], + "alpnProtocols": ["h2", "http/1.1"], "tlsParams": { "ecdhCurves": [ "X25519", @@ -236,10 +236,17 @@ func Test_buildCluster(t *testing.T) { "type": "STRICT_DNS", "connectTimeout": "10s", "respectDnsTtl": true, - "http2ProtocolOptions": { - "allowConnect": true - }, "dnsLookupFamily": "V4_ONLY", + "typedExtensionProtocolOptions": { + "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", + "explicitHttpConfig": { + "http2ProtocolOptions": { + "allowConnect": true + } + } + } + }, "loadAssignment": { "clusterName": "example", "endpoints": [{ @@ -296,15 +303,15 @@ func Test_buildCluster(t *testing.T) { "typedConfig": { "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", "commonTlsContext": { - "alpnProtocols": ["http/1.1"], - "tlsParams": { - "ecdhCurves": [ - "X25519", - "P-256", - "P-384", - "P-521" - ] - }, + "alpnProtocols": ["h2", "http/1.1"], + "tlsParams": { + "ecdhCurves": [ + "X25519", + "P-256", + "P-384", + "P-521" + ] + }, "validationContext": { "matchSubjectAltNames": [{ "exact": "example.com" @@ -318,8 +325,41 @@ func Test_buildCluster(t *testing.T) { } } }], - "http2ProtocolOptions": { - "allowConnect": true + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "commonTlsContext": { + "alpnProtocols": ["h2", "http/1.1"], + "tlsParams": { + "ecdhCurves": [ + "X25519", + "P-256", + "P-384", + "P-521" + ] + }, + "validationContext": { + "matchSubjectAltNames": [{ + "exact": "example.com" + }], + "trustedCa": { + "filename": "`+rootCA+`" + } + } + }, + "sni": "example.com" + } + }, + "typedExtensionProtocolOptions": { + "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", + "explicitHttpConfig": { + "http2ProtocolOptions": { + "allowConnect": true + } + } + } }, "loadAssignment": { "clusterName": "example", @@ -378,8 +418,15 @@ func Test_buildCluster(t *testing.T) { "type": "STATIC", "connectTimeout": "10s", "respectDnsTtl": true, - "http2ProtocolOptions": { - "allowConnect": true + "typedExtensionProtocolOptions": { + "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", + "explicitHttpConfig": { + "http2ProtocolOptions": { + "allowConnect": true + } + } + } }, "loadAssignment": { "clusterName": "example", @@ -424,8 +471,15 @@ func Test_buildCluster(t *testing.T) { "type": "STATIC", "connectTimeout": "10s", "respectDnsTtl": true, - "http2ProtocolOptions": { - "allowConnect": true + "typedExtensionProtocolOptions": { + "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", + "explicitHttpConfig": { + "http2ProtocolOptions": { + "allowConnect": true + } + } + } }, "loadAssignment": { "clusterName": "example", @@ -472,8 +526,15 @@ func Test_buildCluster(t *testing.T) { "type": "STATIC", "connectTimeout": "10s", "respectDnsTtl": true, - "http2ProtocolOptions": { - "allowConnect": true + "typedExtensionProtocolOptions": { + "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", + "explicitHttpConfig": { + "http2ProtocolOptions": { + "allowConnect": true + } + } + } }, "loadAssignment": { "clusterName": "example", @@ -513,8 +574,15 @@ func Test_buildCluster(t *testing.T) { "type": "STRICT_DNS", "connectTimeout": "10s", "respectDnsTtl": true, - "http2ProtocolOptions": { - "allowConnect": true + "typedExtensionProtocolOptions": { + "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": { + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions", + "explicitHttpConfig": { + "http2ProtocolOptions": { + "allowConnect": true + } + } + } }, "dnsLookupFamily": "V4_ONLY", "outlierDetection": { diff --git a/internal/controlplane/xds_clusters.go b/internal/controlplane/xds_clusters.go index 005dc40d1..62c3cb912 100644 --- a/internal/controlplane/xds_clusters.go +++ b/internal/controlplane/xds_clusters.go @@ -15,6 +15,7 @@ import ( "github.com/golang/protobuf/proto" "github.com/golang/protobuf/ptypes/wrappers" "github.com/martinlindhe/base36" + "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/structpb" "github.com/pomerium/pomerium/config" @@ -233,7 +234,7 @@ func (srv *Server) buildPolicyTransportSocket(policy *config.Policy, dst url.URL "P-521", }, }, - AlpnProtocols: []string{"http/1.1"}, + AlpnProtocols: []string{"h2", "http/1.1"}, ValidationContextType: &envoy_extensions_transport_sockets_tls_v3.CommonTlsContext_ValidationContext{ ValidationContext: vc, }, @@ -321,11 +322,14 @@ func (srv *Server) buildCluster( if err != nil { return err } + // Set the default transport socket to the first socket match. This is necessary so that ALPN + // auto configuration works. + if len(cluster.TransportSocketMatches) > 0 { + cluster.TransportSocket = cluster.TransportSocketMatches[0].TransportSocket + } - if forceHTTP2 { - cluster.Http2ProtocolOptions = &envoy_config_core_v3.Http2ProtocolOptions{ - AllowConnect: true, - } + cluster.TypedExtensionProtocolOptions = map[string]*anypb.Any{ + "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": marshalAny(buildUpstreamProtocolOptions(endpoints, forceHTTP2)), } // for IPs we use a static discovery type, otherwise we use DNS diff --git a/internal/controlplane/xds_protocols.go b/internal/controlplane/xds_protocols.go new file mode 100644 index 000000000..bf5a40fc2 --- /dev/null +++ b/internal/controlplane/xds_protocols.go @@ -0,0 +1,60 @@ +package controlplane + +import ( + "encoding/json" + + envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + envoy_extensions_upstreams_http_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3" + + "github.com/pomerium/pomerium/internal/log" +) + +func buildUpstreamProtocolOptions(endpoints []Endpoint, forceHTTP2 bool) *envoy_extensions_upstreams_http_v3.HttpProtocolOptions { + // if forcing http/2, use that explicitly + if forceHTTP2 { + return &envoy_extensions_upstreams_http_v3.HttpProtocolOptions{ + UpstreamProtocolOptions: &envoy_extensions_upstreams_http_v3.HttpProtocolOptions_ExplicitHttpConfig_{ + ExplicitHttpConfig: &envoy_extensions_upstreams_http_v3.HttpProtocolOptions_ExplicitHttpConfig{ + ProtocolConfig: &envoy_extensions_upstreams_http_v3.HttpProtocolOptions_ExplicitHttpConfig_Http2ProtocolOptions{ + Http2ProtocolOptions: &envoy_config_core_v3.Http2ProtocolOptions{ + AllowConnect: true, + }, + }, + }, + }, + } + } + + // when using TLS use ALPN auto config + tlsCount := 0 + for _, e := range endpoints { + if e.transportSocket != nil { + tlsCount++ + } + } + if tlsCount > 0 && tlsCount == len(endpoints) { + for _, e := range endpoints { + bs, _ := json.Marshal(e.transportSocket) + log.Info(). + Str("url", e.url.String()). + Str("endpoints", string(bs)). + Msg("<<>>") + } + return &envoy_extensions_upstreams_http_v3.HttpProtocolOptions{ + UpstreamProtocolOptions: &envoy_extensions_upstreams_http_v3.HttpProtocolOptions_AutoConfig{ + AutoConfig: &envoy_extensions_upstreams_http_v3.HttpProtocolOptions_AutoHttpConfig{}, + }, + } + } + + // otherwise only use http/1.1 + return &envoy_extensions_upstreams_http_v3.HttpProtocolOptions{ + UpstreamProtocolOptions: &envoy_extensions_upstreams_http_v3.HttpProtocolOptions_ExplicitHttpConfig_{ + ExplicitHttpConfig: &envoy_extensions_upstreams_http_v3.HttpProtocolOptions_ExplicitHttpConfig{ + ProtocolConfig: &envoy_extensions_upstreams_http_v3.HttpProtocolOptions_ExplicitHttpConfig_HttpProtocolOptions{ + HttpProtocolOptions: &envoy_config_core_v3.Http1ProtocolOptions{}, + }, + }, + }, + } +}