ingress: allow setting TLS min version and cipher suites in ingress gateway config entries (#11576)

* xds: refactor ingress listener SDS configuration

* xds: update resolveListenerSDS call args in listeners_test

* ingress: add TLS min, max and cipher suites to GatewayTLSConfig

* xds: implement envoyTLSVersions and envoyTLSCipherSuites

* xds: merge TLS config

* xds: configure TLS parameters with ingress TLS context from leaf

* xds: nil check in resolveListenerTLSConfig validation

* xds: nil check in makeTLSParameters* functions

* changelog: add entry for TLS params on ingress config entries

* xds: remove indirection for TLS params in TLSConfig structs

* xds: return tlsContext, nil instead of ambiguous err

Co-authored-by: Chris S. Kim <ckim@hashicorp.com>

* xds: switch zero checks to types.TLSVersionUnspecified

* ingress: add validation for ingress config entry TLS params

* ingress: validate listener TLS config

* xds: add basic ingress with TLS params tests

* xds: add ingress listeners mixed TLS min version defaults precedence test

* xds: add more explicit tests for ingress listeners inheriting gateway defaults

* xds: add test for single TLS listener on gateway without TLS defaults

* xds: regen golden files for TLSVersionInvalid zero value, add TLSVersionAuto listener test

* types/tls: change TLSVersion to string

* types/tls: update TLSCipherSuite to string type

* types/tls: implement validation functions for TLSVersion and TLSCipherSuites, make some maps private

* api: add TLS params to GatewayTLSConfig, add tests

* api: add TLSMinVersion to ingress gateway config entry test JSON

* xds: switch to Envoy TLS cipher suite encoding from types package

* xds: fixup validation for TLSv1_3 min version with cipher suites

* add some kitchen sink tests and add a missing struct tag

* xds: check if mergedCfg.TLSVersion is in TLSVersionsWithConfigurableCipherSuites

* xds: update connectTLSEnabled comment

* xds: remove unsued resolveGatewayServiceTLSConfig function

 * xds: add makeCommonTLSContextFromLeafWithoutParams

* types/tls: add LessThan comparator function for concrete values

* types/tls: change tlsVersions validation map from string to TLSVersion keys

* types/tls: remove unused envoyTLSCipherSuites

* types/tls: enable chacha20 cipher suites for Consul agent

* types/tls: remove insecure cipher suites from allowed config

TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 and TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 are both explicitly listed as insecure and disabled in the Go source.

Refs https://cs.opensource.google/go/go/+/refs/tags/go1.17.3:src/crypto/tls/cipher_suites.go;l=329-330

* types/tls: add ValidateConsulAgentCipherSuites function, make direct lookup map private

* types/tls: return all unmatched cipher suites in validation errors

* xds: check that Envoy API value matching TLS version is found when building TlsParameters

* types/tls: check that value is found in map before appending to slice in MarshalEnvoyTLSCipherSuiteStrings

* types/tls: cast to string rather than fmt.Printf in TLSCihperSuite.String()

* xds: add TLSVersionUnspecified to list of configurable cipher suites

* structs: update note about config entry warning

* xds: remove TLS min version cipher suite unconfigurable test placeholder

* types/tls: update tests to remove assumption about private map values

Co-authored-by: R.B. Boyer <rb@hashicorp.com>
This commit is contained in:
Mike Morris 2022-01-11 11:46:42 -05:00 committed by GitHub
parent 889aa2dd1a
commit 277c41d336
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1578 additions and 187 deletions

3
.changelog/11576.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:feature
ingress: allow setting TLS min version and cipher suites in ingress gateway config entries
```

View File

@ -9,6 +9,7 @@ import (
"github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/lib/stringslice" "github.com/hashicorp/consul/lib/stringslice"
"github.com/hashicorp/consul/types"
) )
// IngressGatewayConfigEntry manages the configuration for an ingress service // IngressGatewayConfigEntry manages the configuration for an ingress service
@ -99,6 +100,13 @@ type GatewayTLSConfig struct {
// SDS allows configuring TLS certificate from an SDS service. // SDS allows configuring TLS certificate from an SDS service.
SDS *GatewayTLSSDSConfig `json:",omitempty"` SDS *GatewayTLSSDSConfig `json:",omitempty"`
TLSMinVersion types.TLSVersion `json:",omitempty" alias:"tls_min_version"`
TLSMaxVersion types.TLSVersion `json:",omitempty" alias:"tls_max_version"`
// Define a subset of cipher suites to restrict
// Only applicable to connections negotiated via TLS 1.2 or earlier
CipherSuites []types.TLSCipherSuite `json:",omitempty" alias:"cipher_suites"`
} }
type GatewayServiceTLSConfig struct { type GatewayServiceTLSConfig struct {
@ -231,11 +239,49 @@ func (e *IngressGatewayConfigEntry) validateServiceSDS(lis IngressListener, svc
return nil return nil
} }
func validateGatewayTLSConfig(tlsCfg GatewayTLSConfig) error {
if tlsCfg.TLSMinVersion != types.TLSVersionUnspecified {
if err := types.ValidateTLSVersion(tlsCfg.TLSMinVersion); err != nil {
return err
}
}
if tlsCfg.TLSMaxVersion != types.TLSVersionUnspecified {
if err := types.ValidateTLSVersion(tlsCfg.TLSMaxVersion); err != nil {
return err
}
if tlsCfg.TLSMinVersion != types.TLSVersionUnspecified {
if err, maxLessThanMin := tlsCfg.TLSMaxVersion.LessThan(tlsCfg.TLSMinVersion); err == nil && maxLessThanMin {
return fmt.Errorf("configuring max version %s less than the configured min version %s is invalid", tlsCfg.TLSMaxVersion, tlsCfg.TLSMinVersion)
}
}
}
if len(tlsCfg.CipherSuites) != 0 {
if _, ok := types.TLSVersionsWithConfigurableCipherSuites[tlsCfg.TLSMinVersion]; !ok {
return fmt.Errorf("configuring CipherSuites is only applicable to conncetions negotiated with TLS 1.2 or earlier, TLSMinVersion is set to %s", tlsCfg.TLSMinVersion)
}
// NOTE: it would be nice to emit a warning but not return an error from
// here if TLSMaxVersion is unspecified, TLS_AUTO or TLSv1_3
if err := types.ValidateEnvoyCipherSuites(tlsCfg.CipherSuites); err != nil {
return err
}
}
return nil
}
func (e *IngressGatewayConfigEntry) Validate() error { func (e *IngressGatewayConfigEntry) Validate() error {
if err := validateConfigEntryMeta(e.Meta); err != nil { if err := validateConfigEntryMeta(e.Meta); err != nil {
return err return err
} }
if err := validateGatewayTLSConfig(e.TLS); err != nil {
return err
}
validProtocols := map[string]bool{ validProtocols := map[string]bool{
"tcp": true, "tcp": true,
"http": true, "http": true,
@ -264,6 +310,12 @@ func (e *IngressGatewayConfigEntry) Validate() error {
listener.Port) listener.Port)
} }
if listener.TLS != nil {
if err := validateGatewayTLSConfig(*listener.TLS); err != nil {
return err
}
}
declaredHosts := make(map[string]bool) declaredHosts := make(map[string]bool)
serviceNames := make(map[ServiceID]struct{}) serviceNames := make(map[ServiceID]struct{})
for _, s := range listener.Services { for _, s := range listener.Services {

View File

@ -16,6 +16,7 @@ import (
"github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/agent/cache" "github.com/hashicorp/consul/agent/cache"
"github.com/hashicorp/consul/sdk/testutil" "github.com/hashicorp/consul/sdk/testutil"
"github.com/hashicorp/consul/types"
) )
func TestConfigEntries_ACLs(t *testing.T) { func TestConfigEntries_ACLs(t *testing.T) {
@ -1107,6 +1108,7 @@ func TestDecodeConfigEntry(t *testing.T) {
}, },
}, },
{ {
// TODO(rb): test SDS stuff here in both places (global/service)
name: "ingress-gateway: kitchen sink", name: "ingress-gateway: kitchen sink",
snake: ` snake: `
kind = "ingress-gateway" kind = "ingress-gateway"
@ -1118,6 +1120,12 @@ func TestDecodeConfigEntry(t *testing.T) {
tls { tls {
enabled = true enabled = true
tls_min_version = "TLSv1_1"
tls_max_version = "TLSv1_2"
cipher_suites = [
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
]
} }
listeners = [ listeners = [
@ -1181,6 +1189,12 @@ func TestDecodeConfigEntry(t *testing.T) {
} }
TLS { TLS {
Enabled = true Enabled = true
TLSMinVersion = "TLSv1_1"
TLSMaxVersion = "TLSv1_2"
CipherSuites = [
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
]
} }
Listeners = [ Listeners = [
{ {
@ -1242,7 +1256,13 @@ func TestDecodeConfigEntry(t *testing.T) {
"gir": "zim", "gir": "zim",
}, },
TLS: GatewayTLSConfig{ TLS: GatewayTLSConfig{
Enabled: true, Enabled: true,
TLSMinVersion: types.TLSv1_1,
TLSMaxVersion: types.TLSv1_2,
CipherSuites: []types.TLSCipherSuite{
types.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
types.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
},
}, },
Listeners: []IngressListener{ Listeners: []IngressListener{
{ {

View File

@ -186,7 +186,7 @@ func makePassthroughClusters(cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message,
ConnectTimeout: ptypes.DurationProto(5 * time.Second), ConnectTimeout: ptypes.DurationProto(5 * time.Second),
} }
commonTLSContext := makeCommonTLSContextFromLeaf(cfgSnap, cfgSnap.Leaf()) commonTLSContext := makeCommonTLSContextFromLeafWithoutParams(cfgSnap, cfgSnap.Leaf())
err := injectSANMatcher(commonTLSContext, passthrough.SpiffeID) err := injectSANMatcher(commonTLSContext, passthrough.SpiffeID)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to inject SAN matcher rules for cluster %q: %v", passthrough.SNI, err) return nil, fmt.Errorf("failed to inject SAN matcher rules for cluster %q: %v", passthrough.SNI, err)
@ -550,7 +550,7 @@ func (s *ResourceGenerator) makeUpstreamClusterForPreparedQuery(upstream structs
} }
// Enable TLS upstream with the configured client certificate. // Enable TLS upstream with the configured client certificate.
commonTLSContext := makeCommonTLSContextFromLeaf(cfgSnap, cfgSnap.Leaf()) commonTLSContext := makeCommonTLSContextFromLeafWithoutParams(cfgSnap, cfgSnap.Leaf())
err = injectSANMatcher(commonTLSContext, spiffeIDs...) err = injectSANMatcher(commonTLSContext, spiffeIDs...)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to inject SAN matcher rules for cluster %q: %v", sni, err) return nil, fmt.Errorf("failed to inject SAN matcher rules for cluster %q: %v", sni, err)
@ -728,7 +728,7 @@ func (s *ResourceGenerator) makeUpstreamClustersForDiscoveryChain(
c.Http2ProtocolOptions = &envoy_core_v3.Http2ProtocolOptions{} c.Http2ProtocolOptions = &envoy_core_v3.Http2ProtocolOptions{}
} }
commonTLSContext := makeCommonTLSContextFromLeaf(cfgSnap, cfgSnap.Leaf()) commonTLSContext := makeCommonTLSContextFromLeafWithoutParams(cfgSnap, cfgSnap.Leaf())
err = injectSANMatcher(commonTLSContext, spiffeIDs...) err = injectSANMatcher(commonTLSContext, spiffeIDs...)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to inject SAN matcher rules for cluster %q: %v", sni, err) return nil, fmt.Errorf("failed to inject SAN matcher rules for cluster %q: %v", sni, err)

View File

@ -740,7 +740,7 @@ func injectHTTPFilterOnFilterChains(
func (s *ResourceGenerator) injectConnectTLSOnFilterChains(cfgSnap *proxycfg.ConfigSnapshot, listener *envoy_listener_v3.Listener) error { func (s *ResourceGenerator) injectConnectTLSOnFilterChains(cfgSnap *proxycfg.ConfigSnapshot, listener *envoy_listener_v3.Listener) error {
for idx := range listener.FilterChains { for idx := range listener.FilterChains {
tlsContext := &envoy_tls_v3.DownstreamTlsContext{ tlsContext := &envoy_tls_v3.DownstreamTlsContext{
CommonTlsContext: makeCommonTLSContextFromLeaf(cfgSnap, cfgSnap.Leaf()), CommonTlsContext: makeCommonTLSContextFromLeafWithoutParams(cfgSnap, cfgSnap.Leaf()),
RequireClientCertificate: &wrappers.BoolValue{Value: true}, RequireClientCertificate: &wrappers.BoolValue{Value: true},
} }
transportSocket, err := makeDownstreamTLSTransportSocket(tlsContext) transportSocket, err := makeDownstreamTLSTransportSocket(tlsContext)
@ -1062,7 +1062,7 @@ func (s *ResourceGenerator) makeFilterChainTerminatingGateway(
protocol string, protocol string,
) (*envoy_listener_v3.FilterChain, error) { ) (*envoy_listener_v3.FilterChain, error) {
tlsContext := &envoy_tls_v3.DownstreamTlsContext{ tlsContext := &envoy_tls_v3.DownstreamTlsContext{
CommonTlsContext: makeCommonTLSContextFromLeaf(cfgSnap, cfgSnap.TerminatingGateway.ServiceLeaves[service]), CommonTlsContext: makeCommonTLSContextFromLeafWithoutParams(cfgSnap, cfgSnap.TerminatingGateway.ServiceLeaves[service]),
RequireClientCertificate: &wrappers.BoolValue{Value: true}, RequireClientCertificate: &wrappers.BoolValue{Value: true},
} }
transportSocket, err := makeDownstreamTLSTransportSocket(tlsContext) transportSocket, err := makeDownstreamTLSTransportSocket(tlsContext)
@ -1536,7 +1536,11 @@ func makeEnvoyHTTPFilter(name string, cfg proto.Message) (*envoy_http_v3.HttpFil
}, nil }, nil
} }
func makeCommonTLSContextFromLeaf(cfgSnap *proxycfg.ConfigSnapshot, leaf *structs.IssuedCert) *envoy_tls_v3.CommonTlsContext { func makeCommonTLSContextFromLeafWithoutParams(cfgSnap *proxycfg.ConfigSnapshot, leaf *structs.IssuedCert) *envoy_tls_v3.CommonTlsContext {
return makeCommonTLSContextFromLeaf(cfgSnap, leaf, nil)
}
func makeCommonTLSContextFromLeaf(cfgSnap *proxycfg.ConfigSnapshot, leaf *structs.IssuedCert, tlsParams *envoy_tls_v3.TlsParameters) *envoy_tls_v3.CommonTlsContext {
// Concatenate all the root PEMs into one. // Concatenate all the root PEMs into one.
if cfgSnap.Roots == nil { if cfgSnap.Roots == nil {
return nil return nil
@ -1547,8 +1551,12 @@ func makeCommonTLSContextFromLeaf(cfgSnap *proxycfg.ConfigSnapshot, leaf *struct
rootPEMS += ca.EnsureTrailingNewline(root.RootCert) rootPEMS += ca.EnsureTrailingNewline(root.RootCert)
} }
if tlsParams == nil {
tlsParams = &envoy_tls_v3.TlsParameters{}
}
return &envoy_tls_v3.CommonTlsContext{ return &envoy_tls_v3.CommonTlsContext{
TlsParams: &envoy_tls_v3.TlsParameters{}, TlsParams: tlsParams,
TlsCertificates: []*envoy_tls_v3.TlsCertificate{ TlsCertificates: []*envoy_tls_v3.TlsCertificate{
{ {
CertificateChain: &envoy_core_v3.DataSource{ CertificateChain: &envoy_core_v3.DataSource{

View File

@ -8,43 +8,26 @@ import (
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes/duration" "github.com/golang/protobuf/ptypes/duration"
"github.com/golang/protobuf/ptypes/wrappers" "github.com/golang/protobuf/ptypes/wrappers"
"github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/proxycfg"
"github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/types"
) )
func (s *ResourceGenerator) makeIngressGatewayListeners(address string, cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) { func (s *ResourceGenerator) makeIngressGatewayListeners(address string, cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) {
var resources []proto.Message var resources []proto.Message
for listenerKey, upstreams := range cfgSnap.IngressGateway.Upstreams { for listenerKey, upstreams := range cfgSnap.IngressGateway.Upstreams {
var tlsContext *envoy_tls_v3.DownstreamTlsContext
listenerCfg, ok := cfgSnap.IngressGateway.Listeners[listenerKey] listenerCfg, ok := cfgSnap.IngressGateway.Listeners[listenerKey]
if !ok { if !ok {
return nil, fmt.Errorf("no listener config found for listener on port %d", listenerKey.Port) return nil, fmt.Errorf("no listener config found for listener on port %d", listenerKey.Port)
} }
// Enable connect TLS if it is enabled at the Gateway or specific listener
// level.
connectTLSEnabled := cfgSnap.IngressGateway.TLSConfig.Enabled ||
(listenerCfg.TLS != nil && listenerCfg.TLS.Enabled)
sdsCfg, err := resolveListenerSDSConfig(cfgSnap, listenerCfg) tlsContext, err := makeDownstreamTLSContextFromSnapshotListenerConfig(cfgSnap, listenerCfg)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if sdsCfg != nil {
// Set up listener TLS from SDS
tlsContext = &envoy_tls_v3.DownstreamTlsContext{
CommonTlsContext: makeCommonTLSContextFromSDS(*sdsCfg),
RequireClientCertificate: &wrappers.BoolValue{Value: false},
}
} else if connectTLSEnabled {
tlsContext = &envoy_tls_v3.DownstreamTlsContext{
CommonTlsContext: makeCommonTLSContextFromLeaf(cfgSnap, cfgSnap.Leaf()),
RequireClientCertificate: &wrappers.BoolValue{Value: false},
}
}
if listenerKey.Protocol == "tcp" { if listenerKey.Protocol == "tcp" {
// We rely on the invariant of upstreams slice always having at least 1 // We rely on the invariant of upstreams slice always having at least 1
// member, because this key/value pair is created only when a // member, because this key/value pair is created only when a
@ -154,21 +137,116 @@ func (s *ResourceGenerator) makeIngressGatewayListeners(address string, cfgSnap
return resources, nil return resources, nil
} }
func resolveListenerSDSConfig(cfgSnap *proxycfg.ConfigSnapshot, listenerCfg structs.IngressListener) (*structs.GatewayTLSSDSConfig, error) { func makeDownstreamTLSContextFromSnapshotListenerConfig(cfgSnap *proxycfg.ConfigSnapshot, listenerCfg structs.IngressListener) (*envoy_tls_v3.DownstreamTlsContext, error) {
var mergedCfg structs.GatewayTLSSDSConfig var downstreamContext *envoy_tls_v3.DownstreamTlsContext
gwSDS := cfgSnap.IngressGateway.TLSConfig.SDS tlsContext, err := makeCommonTLSContextFromSnapshotListenerConfig(cfgSnap, listenerCfg)
if gwSDS != nil { if err != nil {
mergedCfg.ClusterName = gwSDS.ClusterName return nil, err
mergedCfg.CertResource = gwSDS.CertResource
} }
if listenerCfg.TLS != nil && listenerCfg.TLS.SDS != nil { if tlsContext != nil {
if listenerCfg.TLS.SDS.ClusterName != "" { downstreamContext = &envoy_tls_v3.DownstreamTlsContext{
mergedCfg.ClusterName = listenerCfg.TLS.SDS.ClusterName CommonTlsContext: tlsContext,
RequireClientCertificate: &wrappers.BoolValue{Value: false},
} }
if listenerCfg.TLS.SDS.CertResource != "" { }
mergedCfg.CertResource = listenerCfg.TLS.SDS.CertResource
return downstreamContext, nil
}
func makeCommonTLSContextFromSnapshotListenerConfig(cfgSnap *proxycfg.ConfigSnapshot, listenerCfg structs.IngressListener) (*envoy_tls_v3.CommonTlsContext, error) {
var tlsContext *envoy_tls_v3.CommonTlsContext
// Enable connect TLS if it is enabled at the Gateway or specific listener
// level.
gatewayTLSCfg := cfgSnap.IngressGateway.TLSConfig
// It is not possible to explicitly _disable_ TLS on a listener if it's
// enabled on the gateway, because false is the zero-value of the struct field
// and therefore indistinguishable from it being unspecified.
connectTLSEnabled := gatewayTLSCfg.Enabled ||
(listenerCfg.TLS != nil && listenerCfg.TLS.Enabled)
tlsCfg, err := resolveListenerTLSConfig(&gatewayTLSCfg, listenerCfg)
if err != nil {
return nil, err
}
if tlsCfg.SDS != nil {
// Set up listener TLS from SDS
tlsContext = makeCommonTLSContextFromGatewayTLSConfig(*tlsCfg)
} else if connectTLSEnabled {
tlsContext = makeCommonTLSContextFromLeaf(cfgSnap, cfgSnap.Leaf(), makeTLSParametersFromGatewayTLSConfig(*tlsCfg))
}
return tlsContext, nil
}
func resolveListenerTLSConfig(gatewayTLSCfg *structs.GatewayTLSConfig, listenerCfg structs.IngressListener) (*structs.GatewayTLSConfig, error) {
var mergedCfg structs.GatewayTLSConfig
resolvedSDSCfg, err := resolveListenerSDSConfig(gatewayTLSCfg.SDS, listenerCfg.TLS, listenerCfg.Port)
if err != nil {
return nil, err
}
mergedCfg.SDS = resolvedSDSCfg
if gatewayTLSCfg != nil {
mergedCfg.TLSMinVersion = gatewayTLSCfg.TLSMinVersion
mergedCfg.TLSMaxVersion = gatewayTLSCfg.TLSMaxVersion
mergedCfg.CipherSuites = gatewayTLSCfg.CipherSuites
}
if listenerCfg.TLS != nil {
if listenerCfg.TLS.TLSMinVersion != types.TLSVersionUnspecified {
mergedCfg.TLSMinVersion = listenerCfg.TLS.TLSMinVersion
}
if listenerCfg.TLS.TLSMaxVersion != types.TLSVersionUnspecified {
mergedCfg.TLSMaxVersion = listenerCfg.TLS.TLSMaxVersion
}
if len(listenerCfg.TLS.CipherSuites) != 0 {
mergedCfg.CipherSuites = listenerCfg.TLS.CipherSuites
}
}
var TLSVersionsWithConfigurableCipherSuites = map[types.TLSVersion]struct{}{
// Remove these two if Envoy ever sets TLS 1.3 as default minimum
types.TLSVersionUnspecified: {},
types.TLSVersionAuto: {},
types.TLSv1_0: {},
types.TLSv1_1: {},
types.TLSv1_2: {},
}
// Validate. Configuring cipher suites is only applicable to connections negotiated
// via TLS 1.2 or earlier. Other cases shouldn't be possible as we validate them at
// input but be resilient to bugs later.
if len(mergedCfg.CipherSuites) != 0 {
if _, ok := TLSVersionsWithConfigurableCipherSuites[mergedCfg.TLSMinVersion]; !ok {
return nil, fmt.Errorf("configuring CipherSuites is only applicable to connections negotiated with TLS 1.2 or earlier, TLSMinVersion is set to %s in listener or gateway config", mergedCfg.TLSMinVersion)
}
}
return &mergedCfg, nil
}
func resolveListenerSDSConfig(gatewaySDSCfg *structs.GatewayTLSSDSConfig, listenerTLSCfg *structs.GatewayTLSConfig, listenerPort int) (*structs.GatewayTLSSDSConfig, error) {
var mergedCfg structs.GatewayTLSSDSConfig
if gatewaySDSCfg != nil {
mergedCfg.ClusterName = gatewaySDSCfg.ClusterName
mergedCfg.CertResource = gatewaySDSCfg.CertResource
}
if listenerTLSCfg != nil && listenerTLSCfg.SDS != nil {
if listenerTLSCfg.SDS.ClusterName != "" {
mergedCfg.ClusterName = listenerTLSCfg.SDS.ClusterName
}
if listenerTLSCfg.SDS.CertResource != "" {
mergedCfg.CertResource = listenerTLSCfg.SDS.CertResource
} }
} }
@ -183,10 +261,10 @@ func resolveListenerSDSConfig(cfgSnap *proxycfg.ConfigSnapshot, listenerCfg stru
return &mergedCfg, nil return &mergedCfg, nil
case mergedCfg.ClusterName == "" && mergedCfg.CertResource != "": case mergedCfg.ClusterName == "" && mergedCfg.CertResource != "":
return nil, fmt.Errorf("missing SDS cluster name for listener on port %d", listenerCfg.Port) return nil, fmt.Errorf("missing SDS cluster name for listener on port %d", listenerPort)
case mergedCfg.ClusterName != "" && mergedCfg.CertResource == "": case mergedCfg.ClusterName != "" && mergedCfg.CertResource == "":
return nil, fmt.Errorf("missing SDS cert resource for listener on port %d", listenerCfg.Port) return nil, fmt.Errorf("missing SDS cert resource for listener on port %d", listenerPort)
} }
return &mergedCfg, nil return &mergedCfg, nil
@ -260,7 +338,7 @@ func makeSDSOverrideFilterChains(cfgSnap *proxycfg.ConfigSnapshot,
} }
tlsContext := &envoy_tls_v3.DownstreamTlsContext{ tlsContext := &envoy_tls_v3.DownstreamTlsContext{
CommonTlsContext: makeCommonTLSContextFromSDS(*svc.TLS.SDS), CommonTlsContext: makeCommonTLSContextFromGatewayServiceTLSConfig(*svc.TLS),
RequireClientCertificate: &wrappers.BoolValue{Value: false}, RequireClientCertificate: &wrappers.BoolValue{Value: false},
} }
@ -284,33 +362,71 @@ func makeSDSOverrideFilterChains(cfgSnap *proxycfg.ConfigSnapshot,
return chains, nil return chains, nil
} }
func makeCommonTLSContextFromSDS(sdsCfg structs.GatewayTLSSDSConfig) *envoy_tls_v3.CommonTlsContext { var envoyTLSVersions = map[types.TLSVersion]envoy_tls_v3.TlsParameters_TlsProtocol{
types.TLSVersionAuto: envoy_tls_v3.TlsParameters_TLS_AUTO,
types.TLSv1_0: envoy_tls_v3.TlsParameters_TLSv1_0,
types.TLSv1_1: envoy_tls_v3.TlsParameters_TLSv1_1,
types.TLSv1_2: envoy_tls_v3.TlsParameters_TLSv1_2,
types.TLSv1_3: envoy_tls_v3.TlsParameters_TLSv1_3,
}
func makeTLSParametersFromGatewayTLSConfig(tlsCfg structs.GatewayTLSConfig) *envoy_tls_v3.TlsParameters {
tlsParams := envoy_tls_v3.TlsParameters{}
if tlsCfg.TLSMinVersion != types.TLSVersionUnspecified {
if minVersion, ok := envoyTLSVersions[tlsCfg.TLSMinVersion]; ok {
tlsParams.TlsMinimumProtocolVersion = minVersion
}
}
if tlsCfg.TLSMaxVersion != types.TLSVersionUnspecified {
if maxVersion, ok := envoyTLSVersions[tlsCfg.TLSMaxVersion]; ok {
tlsParams.TlsMaximumProtocolVersion = maxVersion
}
}
if len(tlsCfg.CipherSuites) != 0 {
tlsParams.CipherSuites = types.MarshalEnvoyTLSCipherSuiteStrings(tlsCfg.CipherSuites)
}
return &tlsParams
}
func makeCommonTLSContextFromGatewayTLSConfig(tlsCfg structs.GatewayTLSConfig) *envoy_tls_v3.CommonTlsContext {
return &envoy_tls_v3.CommonTlsContext{ return &envoy_tls_v3.CommonTlsContext{
TlsParams: &envoy_tls_v3.TlsParameters{}, TlsParams: makeTLSParametersFromGatewayTLSConfig(tlsCfg),
TlsCertificateSdsSecretConfigs: []*envoy_tls_v3.SdsSecretConfig{ TlsCertificateSdsSecretConfigs: makeTLSCertificateSdsSecretConfigsFromSDS(*tlsCfg.SDS),
{ }
Name: sdsCfg.CertResource, }
SdsConfig: &envoy_core_v3.ConfigSource{
ConfigSourceSpecifier: &envoy_core_v3.ConfigSource_ApiConfigSource{ func makeCommonTLSContextFromGatewayServiceTLSConfig(tlsCfg structs.GatewayServiceTLSConfig) *envoy_tls_v3.CommonTlsContext {
ApiConfigSource: &envoy_core_v3.ApiConfigSource{ return &envoy_tls_v3.CommonTlsContext{
ApiType: envoy_core_v3.ApiConfigSource_GRPC, TlsParams: &envoy_tls_v3.TlsParameters{},
TransportApiVersion: envoy_core_v3.ApiVersion_V3, TlsCertificateSdsSecretConfigs: makeTLSCertificateSdsSecretConfigsFromSDS(*tlsCfg.SDS),
// Note ClusterNames can't be set here - that's only for REST type }
// we need a full GRPC config instead. }
GrpcServices: []*envoy_core_v3.GrpcService{ func makeTLSCertificateSdsSecretConfigsFromSDS(sdsCfg structs.GatewayTLSSDSConfig) []*envoy_tls_v3.SdsSecretConfig {
{ return []*envoy_tls_v3.SdsSecretConfig{
TargetSpecifier: &envoy_core_v3.GrpcService_EnvoyGrpc_{ {
EnvoyGrpc: &envoy_core_v3.GrpcService_EnvoyGrpc{ Name: sdsCfg.CertResource,
ClusterName: sdsCfg.ClusterName, SdsConfig: &envoy_core_v3.ConfigSource{
}, ConfigSourceSpecifier: &envoy_core_v3.ConfigSource_ApiConfigSource{
ApiConfigSource: &envoy_core_v3.ApiConfigSource{
ApiType: envoy_core_v3.ApiConfigSource_GRPC,
TransportApiVersion: envoy_core_v3.ApiVersion_V3,
// Note ClusterNames can't be set here - that's only for REST type
// we need a full GRPC config instead.
GrpcServices: []*envoy_core_v3.GrpcService{
{
TargetSpecifier: &envoy_core_v3.GrpcService_EnvoyGrpc_{
EnvoyGrpc: &envoy_core_v3.GrpcService_EnvoyGrpc{
ClusterName: sdsCfg.ClusterName,
}, },
Timeout: &duration.Duration{Seconds: 5},
}, },
Timeout: &duration.Duration{Seconds: 5},
}, },
}, },
}, },
ResourceApiVersion: envoy_core_v3.ApiVersion_V3,
}, },
ResourceApiVersion: envoy_core_v3.ApiVersion_V3,
}, },
}, },
} }

View File

@ -519,6 +519,30 @@ func TestListenersFromSnapshot(t *testing.T) {
create: proxycfg.TestConfigSnapshotIngressWithTLSListener, create: proxycfg.TestConfigSnapshotIngressWithTLSListener,
setup: nil, setup: nil,
}, },
{
name: "ingress-with-tls-listener-min-version",
create: proxycfg.TestConfigSnapshotIngressWithTLSListener,
setup: func(snap *proxycfg.ConfigSnapshot) {
snap.IngressGateway.TLSConfig.TLSMinVersion = types.TLSv1_3
},
},
{
name: "ingress-with-tls-listener-max-version",
create: proxycfg.TestConfigSnapshotIngressWithTLSListener,
setup: func(snap *proxycfg.ConfigSnapshot) {
snap.IngressGateway.TLSConfig.TLSMaxVersion = types.TLSv1_2
},
},
{
name: "ingress-with-tls-listener-cipher-suites",
create: proxycfg.TestConfigSnapshotIngressWithTLSListener,
setup: func(snap *proxycfg.ConfigSnapshot) {
snap.IngressGateway.TLSConfig.CipherSuites = []types.TLSCipherSuite{
types.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
types.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
}
},
},
{ {
name: "ingress-with-tls-mixed-listeners", name: "ingress-with-tls-mixed-listeners",
// Use SDS helper even though we aren't testing SDS since it already sets // Use SDS helper even though we aren't testing SDS since it already sets
@ -572,6 +596,215 @@ func TestListenersFromSnapshot(t *testing.T) {
} }
}, },
}, },
{
name: "ingress-with-tls-min-version-listeners-gateway-defaults",
create: proxycfg.TestConfigSnapshotIngressWithTLSListener,
setup: func(snap *proxycfg.ConfigSnapshot) {
snap.IngressGateway.TLSConfig.TLSMinVersion = types.TLSv1_2
// One listener disables TLS, one inherits TLS minimum version from the gateway
// config, two others set different versions
snap.IngressGateway.Upstreams = map[proxycfg.IngressListenerKey]structs.Upstreams{
{Protocol: "http", Port: 8080}: {
{
DestinationName: "s1",
LocalBindPort: 8080,
},
},
{Protocol: "http", Port: 8081}: {
{
DestinationName: "s2",
LocalBindPort: 8081,
},
},
{Protocol: "http", Port: 8082}: {
{
DestinationName: "s3",
LocalBindPort: 8082,
},
},
{Protocol: "http", Port: 8083}: {
{
DestinationName: "s4",
LocalBindPort: 8083,
},
},
{Protocol: "http", Port: 8084}: {
{
DestinationName: "s4",
LocalBindPort: 8084,
},
},
}
snap.IngressGateway.Listeners = map[proxycfg.IngressListenerKey]structs.IngressListener{
// Omits listener TLS config, should default to gateway TLS config
{Protocol: "http", Port: 8080}: {
Port: 8080,
Services: []structs.IngressService{
{
Name: "s1",
},
},
},
// Explicitly sets listener TLS config to nil, should default to gateway TLS config
{Protocol: "http", Port: 8081}: {
Port: 8081,
Services: []structs.IngressService{
{
Name: "s2",
},
},
TLS: nil,
},
// Explicitly enables TLS config, but with no listener default TLS params,
// should default to gateway TLS config
{Protocol: "http", Port: 8082}: {
Port: 8082,
Services: []structs.IngressService{
{
Name: "s3",
},
},
TLS: &structs.GatewayTLSConfig{
Enabled: true,
},
},
// Explicitly unset gateway default TLS min version in favor of proxy default
{Protocol: "http", Port: 8083}: {
Port: 8083,
Services: []structs.IngressService{
{
Name: "s3",
},
},
TLS: &structs.GatewayTLSConfig{
Enabled: true,
TLSMinVersion: types.TLSVersionAuto,
},
},
// Disables listener TLS
{Protocol: "http", Port: 8084}: {
Port: 8084,
Services: []structs.IngressService{
{
Name: "s4",
},
},
TLS: &structs.GatewayTLSConfig{
Enabled: false,
},
},
}
},
},
{
name: "ingress-with-single-tls-listener",
create: proxycfg.TestConfigSnapshotIngress,
setup: func(snap *proxycfg.ConfigSnapshot) {
// One listener should inherit non-TLS gateway config, another
// listener configures TLS with an explicit minimum version
snap.IngressGateway.Upstreams = map[proxycfg.IngressListenerKey]structs.Upstreams{
{Protocol: "http", Port: 8080}: {
{
DestinationName: "s1",
LocalBindPort: 8080,
},
},
{Protocol: "http", Port: 8081}: {
{
DestinationName: "s2",
LocalBindPort: 8081,
},
},
}
snap.IngressGateway.Listeners = map[proxycfg.IngressListenerKey]structs.IngressListener{
{Protocol: "http", Port: 8080}: {
Port: 8080,
Services: []structs.IngressService{
{
Name: "s1",
},
},
},
{Protocol: "http", Port: 8081}: {
Port: 8081,
Services: []structs.IngressService{
{
Name: "s2",
},
},
TLS: &structs.GatewayTLSConfig{
Enabled: true,
TLSMinVersion: types.TLSv1_2,
},
},
}
},
},
{
name: "ingress-with-tls-mixed-min-version-listeners",
create: proxycfg.TestConfigSnapshotIngressWithTLSListener,
setup: func(snap *proxycfg.ConfigSnapshot) {
snap.IngressGateway.TLSConfig.TLSMinVersion = types.TLSv1_2
// One listener should inherit TLS minimum version from the gateway config,
// two others each set explicit TLS minimum versions
snap.IngressGateway.Upstreams = map[proxycfg.IngressListenerKey]structs.Upstreams{
{Protocol: "http", Port: 8080}: {
{
DestinationName: "s1",
LocalBindPort: 8080,
},
},
{Protocol: "http", Port: 8081}: {
{
DestinationName: "s2",
LocalBindPort: 8081,
},
},
{Protocol: "http", Port: 8082}: {
{
DestinationName: "s3",
LocalBindPort: 8082,
},
},
}
snap.IngressGateway.Listeners = map[proxycfg.IngressListenerKey]structs.IngressListener{
{Protocol: "http", Port: 8080}: {
Port: 8080,
Services: []structs.IngressService{
{
Name: "s1",
},
},
},
{Protocol: "http", Port: 8081}: {
Port: 8081,
Services: []structs.IngressService{
{
Name: "s2",
},
},
TLS: &structs.GatewayTLSConfig{
Enabled: true,
TLSMinVersion: types.TLSv1_0,
},
},
{Protocol: "http", Port: 8082}: {
Port: 8082,
Services: []structs.IngressService{
{
Name: "s3",
},
},
TLS: &structs.GatewayTLSConfig{
Enabled: true,
TLSMinVersion: types.TLSv1_3,
},
},
}
},
},
{ {
name: "ingress-with-sds-listener-gw-level", name: "ingress-with-sds-listener-gw-level",
create: proxycfg.TestConfigSnapshotIngressWithGatewaySDS, create: proxycfg.TestConfigSnapshotIngressWithGatewaySDS,
@ -1208,7 +1441,7 @@ func TestResolveListenerSDSConfig(t *testing.T) {
listenerCfg = lisCfg listenerCfg = lisCfg
} }
got, err := resolveListenerSDSConfig(snap, listenerCfg) got, err := resolveListenerSDSConfig(snap.IngressGateway.TLSConfig.SDS, listenerCfg.TLS, listenerCfg.Port)
if tc.wantErr != "" { if tc.wantErr != "" {
require.Error(t, err) require.Error(t, err)
require.Contains(t, err.Error(), tc.wantErr) require.Contains(t, err.Error(), tc.wantErr)

View File

@ -0,0 +1,120 @@
{
"versionInfo": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "http:1.2.3.4:8080",
"address": {
"socketAddress": {
"address": "1.2.3.4",
"portValue": 8080
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.http_connection_manager",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"statPrefix": "ingress_upstream_8080",
"rds": {
"configSource": {
"ads": {
},
"resourceApiVersion": "V3"
},
"routeConfigName": "8080"
},
"httpFilters": [
{
"name": "envoy.filters.http.router"
}
],
"tracing": {
"randomSampling": {
}
}
}
}
]
}
],
"trafficDirection": "OUTBOUND"
},
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "http:1.2.3.4:8081",
"address": {
"socketAddress": {
"address": "1.2.3.4",
"portValue": 8081
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.http_connection_manager",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"statPrefix": "ingress_upstream_8081",
"rds": {
"configSource": {
"ads": {
},
"resourceApiVersion": "V3"
},
"routeConfigName": "8081"
},
"httpFilters": [
{
"name": "envoy.filters.http.router"
}
],
"tracing": {
"randomSampling": {
}
}
}
}
],
"transportSocket": {
"name": "tls",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
"commonTlsContext": {
"tlsParams": {
"tlsMinimumProtocolVersion": "TLSv1_2"
},
"tlsCertificates": [
{
"certificateChain": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
},
"privateKey": {
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
}
}
],
"validationContext": {
"trustedCa": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
}
}
},
"requireClientCertificate": false
}
}
}
],
"trafficDirection": "OUTBOUND"
}
],
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
"nonce": "00000001"
}

View File

@ -0,0 +1,62 @@
{
"versionInfo": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "db:1.2.3.4:9191",
"address": {
"socketAddress": {
"address": "1.2.3.4",
"portValue": 9191
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.tcp_proxy",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
"statPrefix": "upstream.db.default.default.dc1",
"cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
],
"transportSocket": {
"name": "tls",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
"commonTlsContext": {
"tlsParams": {
"cipherSuites": [
"ECDHE-ECDSA-AES128-GCM-SHA256",
"ECDHE-ECDSA-CHACHA20-POLY1305"
]
},
"tlsCertificates": [
{
"certificateChain": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
},
"privateKey": {
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
}
}
],
"validationContext": {
"trustedCa": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
}
}
},
"requireClientCertificate": false
}
}
}
],
"trafficDirection": "OUTBOUND"
}
],
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
"nonce": "00000001"
}

View File

@ -0,0 +1,59 @@
{
"versionInfo": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "db:1.2.3.4:9191",
"address": {
"socketAddress": {
"address": "1.2.3.4",
"portValue": 9191
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.tcp_proxy",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
"statPrefix": "upstream.db.default.default.dc1",
"cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
],
"transportSocket": {
"name": "tls",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
"commonTlsContext": {
"tlsParams": {
"tlsMaximumProtocolVersion": "TLSv1_2"
},
"tlsCertificates": [
{
"certificateChain": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
},
"privateKey": {
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
}
}
],
"validationContext": {
"trustedCa": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
}
}
},
"requireClientCertificate": false
}
}
}
],
"trafficDirection": "OUTBOUND"
}
],
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
"nonce": "00000001"
}

View File

@ -0,0 +1,59 @@
{
"versionInfo": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "db:1.2.3.4:9191",
"address": {
"socketAddress": {
"address": "1.2.3.4",
"portValue": 9191
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.tcp_proxy",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
"statPrefix": "upstream.db.default.default.dc1",
"cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
],
"transportSocket": {
"name": "tls",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
"commonTlsContext": {
"tlsParams": {
"tlsMinimumProtocolVersion": "TLSv1_3"
},
"tlsCertificates": [
{
"certificateChain": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
},
"privateKey": {
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
}
}
],
"validationContext": {
"trustedCa": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
}
}
},
"requireClientCertificate": false
}
}
}
],
"trafficDirection": "OUTBOUND"
}
],
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
"nonce": "00000001"
}

View File

@ -0,0 +1,357 @@
{
"versionInfo": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "http:1.2.3.4:8080",
"address": {
"socketAddress": {
"address": "1.2.3.4",
"portValue": 8080
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.http_connection_manager",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"statPrefix": "ingress_upstream_8080",
"rds": {
"configSource": {
"ads": {
},
"resourceApiVersion": "V3"
},
"routeConfigName": "8080"
},
"httpFilters": [
{
"name": "envoy.filters.http.router"
}
],
"tracing": {
"randomSampling": {
}
}
}
}
],
"transportSocket": {
"name": "tls",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
"commonTlsContext": {
"tlsParams": {
"tlsMinimumProtocolVersion": "TLSv1_2"
},
"tlsCertificates": [
{
"certificateChain": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
},
"privateKey": {
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
}
}
],
"validationContext": {
"trustedCa": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
}
}
},
"requireClientCertificate": false
}
}
}
],
"trafficDirection": "OUTBOUND"
},
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "http:1.2.3.4:8081",
"address": {
"socketAddress": {
"address": "1.2.3.4",
"portValue": 8081
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.http_connection_manager",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"statPrefix": "ingress_upstream_8081",
"rds": {
"configSource": {
"ads": {
},
"resourceApiVersion": "V3"
},
"routeConfigName": "8081"
},
"httpFilters": [
{
"name": "envoy.filters.http.router"
}
],
"tracing": {
"randomSampling": {
}
}
}
}
],
"transportSocket": {
"name": "tls",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
"commonTlsContext": {
"tlsParams": {
"tlsMinimumProtocolVersion": "TLSv1_2"
},
"tlsCertificates": [
{
"certificateChain": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
},
"privateKey": {
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
}
}
],
"validationContext": {
"trustedCa": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
}
}
},
"requireClientCertificate": false
}
}
}
],
"trafficDirection": "OUTBOUND"
},
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "http:1.2.3.4:8082",
"address": {
"socketAddress": {
"address": "1.2.3.4",
"portValue": 8082
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.http_connection_manager",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"statPrefix": "ingress_upstream_8082",
"rds": {
"configSource": {
"ads": {
},
"resourceApiVersion": "V3"
},
"routeConfigName": "8082"
},
"httpFilters": [
{
"name": "envoy.filters.http.router"
}
],
"tracing": {
"randomSampling": {
}
}
}
}
],
"transportSocket": {
"name": "tls",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
"commonTlsContext": {
"tlsParams": {
"tlsMinimumProtocolVersion": "TLSv1_2"
},
"tlsCertificates": [
{
"certificateChain": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
},
"privateKey": {
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
}
}
],
"validationContext": {
"trustedCa": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
}
}
},
"requireClientCertificate": false
}
}
}
],
"trafficDirection": "OUTBOUND"
},
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "http:1.2.3.4:8083",
"address": {
"socketAddress": {
"address": "1.2.3.4",
"portValue": 8083
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.http_connection_manager",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"statPrefix": "ingress_upstream_8083",
"rds": {
"configSource": {
"ads": {
},
"resourceApiVersion": "V3"
},
"routeConfigName": "8083"
},
"httpFilters": [
{
"name": "envoy.filters.http.router"
}
],
"tracing": {
"randomSampling": {
}
}
}
}
],
"transportSocket": {
"name": "tls",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
"commonTlsContext": {
"tlsParams": {
},
"tlsCertificates": [
{
"certificateChain": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
},
"privateKey": {
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
}
}
],
"validationContext": {
"trustedCa": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
}
}
},
"requireClientCertificate": false
}
}
}
],
"trafficDirection": "OUTBOUND"
},
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "http:1.2.3.4:8084",
"address": {
"socketAddress": {
"address": "1.2.3.4",
"portValue": 8084
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.http_connection_manager",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"statPrefix": "ingress_upstream_8084",
"rds": {
"configSource": {
"ads": {
},
"resourceApiVersion": "V3"
},
"routeConfigName": "8084"
},
"httpFilters": [
{
"name": "envoy.filters.http.router"
}
],
"tracing": {
"randomSampling": {
}
}
}
}
],
"transportSocket": {
"name": "tls",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
"commonTlsContext": {
"tlsParams": {
"tlsMinimumProtocolVersion": "TLSv1_2"
},
"tlsCertificates": [
{
"certificateChain": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
},
"privateKey": {
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
}
}
],
"validationContext": {
"trustedCa": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
}
}
},
"requireClientCertificate": false
}
}
}
],
"trafficDirection": "OUTBOUND"
}
],
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
"nonce": "00000001"
}

View File

@ -0,0 +1,217 @@
{
"versionInfo": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "http:1.2.3.4:8080",
"address": {
"socketAddress": {
"address": "1.2.3.4",
"portValue": 8080
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.http_connection_manager",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"statPrefix": "ingress_upstream_8080",
"rds": {
"configSource": {
"ads": {
},
"resourceApiVersion": "V3"
},
"routeConfigName": "8080"
},
"httpFilters": [
{
"name": "envoy.filters.http.router"
}
],
"tracing": {
"randomSampling": {
}
}
}
}
],
"transportSocket": {
"name": "tls",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
"commonTlsContext": {
"tlsParams": {
"tlsMinimumProtocolVersion": "TLSv1_2"
},
"tlsCertificates": [
{
"certificateChain": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
},
"privateKey": {
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
}
}
],
"validationContext": {
"trustedCa": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
}
}
},
"requireClientCertificate": false
}
}
}
],
"trafficDirection": "OUTBOUND"
},
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "http:1.2.3.4:8081",
"address": {
"socketAddress": {
"address": "1.2.3.4",
"portValue": 8081
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.http_connection_manager",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"statPrefix": "ingress_upstream_8081",
"rds": {
"configSource": {
"ads": {
},
"resourceApiVersion": "V3"
},
"routeConfigName": "8081"
},
"httpFilters": [
{
"name": "envoy.filters.http.router"
}
],
"tracing": {
"randomSampling": {
}
}
}
}
],
"transportSocket": {
"name": "tls",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
"commonTlsContext": {
"tlsParams": {
"tlsMinimumProtocolVersion": "TLSv1_0"
},
"tlsCertificates": [
{
"certificateChain": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
},
"privateKey": {
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
}
}
],
"validationContext": {
"trustedCa": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
}
}
},
"requireClientCertificate": false
}
}
}
],
"trafficDirection": "OUTBOUND"
},
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "http:1.2.3.4:8082",
"address": {
"socketAddress": {
"address": "1.2.3.4",
"portValue": 8082
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.http_connection_manager",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"statPrefix": "ingress_upstream_8082",
"rds": {
"configSource": {
"ads": {
},
"resourceApiVersion": "V3"
},
"routeConfigName": "8082"
},
"httpFilters": [
{
"name": "envoy.filters.http.router"
}
],
"tracing": {
"randomSampling": {
}
}
}
}
],
"transportSocket": {
"name": "tls",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
"commonTlsContext": {
"tlsParams": {
"tlsMinimumProtocolVersion": "TLSv1_3"
},
"tlsCertificates": [
{
"certificateChain": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
},
"privateKey": {
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
}
}
],
"validationContext": {
"trustedCa": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
}
}
},
"requireClientCertificate": false
}
}
}
],
"trafficDirection": "OUTBOUND"
}
],
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
"nonce": "00000001"
}

View File

@ -43,6 +43,13 @@ type GatewayTLSConfig struct {
// SDS allows configuring TLS certificate from an SDS service. // SDS allows configuring TLS certificate from an SDS service.
SDS *GatewayTLSSDSConfig `json:",omitempty"` SDS *GatewayTLSSDSConfig `json:",omitempty"`
TLSMinVersion string `json:",omitempty" alias:"tls_min_version"`
TLSMaxVersion string `json:",omitempty" alias:"tls_max_version"`
// Define a subset of cipher suites to restrict
// Only applicable to connections negotiated via TLS 1.2 or earlier
CipherSuites []string `json:",omitempty" alias:"cipher_suites"`
} }
type GatewayServiceTLSConfig struct { type GatewayServiceTLSConfig struct {

View File

@ -26,7 +26,8 @@ func TestAPI_ConfigEntries_IngressGateway(t *testing.T) {
Kind: IngressGateway, Kind: IngressGateway,
Name: "bar", Name: "bar",
TLS: GatewayTLSConfig{ TLS: GatewayTLSConfig{
Enabled: true, Enabled: true,
TLSMinVersion: "TLSv1_2",
}, },
} }

View File

@ -945,6 +945,7 @@ func TestDecodeConfigEntry(t *testing.T) {
}, },
}, },
{ {
// TODO(rb): test SDS stuff here in both places (global/service)
name: "ingress-gateway", name: "ingress-gateway",
body: ` body: `
{ {
@ -955,7 +956,13 @@ func TestDecodeConfigEntry(t *testing.T) {
"gir": "zim" "gir": "zim"
}, },
"Tls": { "Tls": {
"Enabled": true "Enabled": true,
"TLSMinVersion": "TLSv1_1",
"TLSMaxVersion": "TLSv1_2",
"CipherSuites": [
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
]
}, },
"Listeners": [ "Listeners": [
{ {
@ -992,7 +999,13 @@ func TestDecodeConfigEntry(t *testing.T) {
"gir": "zim", "gir": "zim",
}, },
TLS: GatewayTLSConfig{ TLS: GatewayTLSConfig{
Enabled: true, Enabled: true,
TLSMinVersion: "TLSv1_1",
TLSMaxVersion: "TLSv1_2",
CipherSuites: []string{
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
},
}, },
Listeners: []IngressListener{ Listeners: []IngressListener{
{ {

View File

@ -2096,6 +2096,7 @@ func TestParseConfigEntry(t *testing.T) {
}, },
}, },
{ {
// TODO(rb): test SDS stuff here in both places (global/service)
name: "ingress-gateway: kitchen sink", name: "ingress-gateway: kitchen sink",
snake: ` snake: `
kind = "ingress-gateway" kind = "ingress-gateway"
@ -2106,6 +2107,12 @@ func TestParseConfigEntry(t *testing.T) {
} }
tls { tls {
enabled = true enabled = true
tls_min_version = "TLSv1_1"
tls_max_version = "TLSv1_2"
cipher_suites = [
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
]
} }
listeners = [ listeners = [
{ {
@ -2133,6 +2140,12 @@ func TestParseConfigEntry(t *testing.T) {
} }
Tls { Tls {
Enabled = true Enabled = true
TLSMinVersion = "TLSv1_1"
TLSMaxVersion = "TLSv1_2"
CipherSuites = [
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
]
} }
Listeners = [ Listeners = [
{ {
@ -2160,7 +2173,13 @@ func TestParseConfigEntry(t *testing.T) {
"gir": "zim" "gir": "zim"
}, },
"tls": { "tls": {
"enabled": true "enabled": true,
"tls_min_version": "TLSv1_1",
"tls_max_version": "TLSv1_2",
"cipher_suites": [
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
]
}, },
"listeners": [ "listeners": [
{ {
@ -2188,8 +2207,14 @@ func TestParseConfigEntry(t *testing.T) {
"foo": "bar", "foo": "bar",
"gir": "zim" "gir": "zim"
}, },
"Tls": { "TLS": {
"Enabled": true "Enabled": true,
"TLSMinVersion": "TLSv1_1",
"TLSMaxVersion": "TLSv1_2",
"CipherSuites": [
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
]
}, },
"Listeners": [ "Listeners": [
{ {
@ -2217,7 +2242,13 @@ func TestParseConfigEntry(t *testing.T) {
"gir": "zim", "gir": "zim",
}, },
TLS: api.GatewayTLSConfig{ TLS: api.GatewayTLSConfig{
Enabled: true, Enabled: true,
TLSMinVersion: "TLSv1_1",
TLSMaxVersion: "TLSv1_2",
CipherSuites: []string{
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
},
}, },
Listeners: []api.IngressListener{ Listeners: []api.IngressListener{
{ {

View File

@ -1,43 +1,42 @@
package types package types
import ( import (
"encoding/json"
"fmt" "fmt"
"strings"
) )
// TLSVersion is a strongly-typed int used for relative comparison // TLSVersion is a strongly-typed string for TLS versions
// (minimum, maximum, greater than, less than) of TLS versions type TLSVersion string
type TLSVersion int
const ( const (
// Error value, excluded from lookup maps // Error value, excluded from lookup maps
TLSVersionInvalid TLSVersion = iota - 1 TLSVersionInvalid TLSVersion = "TLS_INVALID"
// Explicit unspecified zero-value to avoid overwriting parent defaults // Explicit unspecified zero-value to avoid overwriting parent defaults
TLSVersionUnspecified TLSVersionUnspecified TLSVersion = ""
// Explictly allow implementation to select TLS version // Explictly allow implementation to select TLS version
// May be useful to supercede defaults specified at a higher layer // May be useful to supercede defaults specified at a higher layer
TLSVersionAuto TLSVersionAuto TLSVersion = "TLS_AUTO"
_ // Placeholder for SSLv3, hopefully we won't have to add this _ // Placeholder for SSLv3, hopefully we won't have to add this
// TLS versions // TLS versions
TLSv1_0 TLSv1_0 TLSVersion = "TLSv1_0"
TLSv1_1 TLSv1_1 TLSVersion = "TLSv1_1"
TLSv1_2 TLSv1_2 TLSVersion = "TLSv1_2"
TLSv1_3 TLSv1_3 TLSVersion = "TLSv1_3"
) )
var ( var (
TLSVersions = map[string]TLSVersion{ tlsVersions = map[TLSVersion]struct{}{
"TLS_AUTO": TLSVersionAuto, TLSVersionAuto: {},
"TLSv1_0": TLSv1_0, TLSv1_0: {},
"TLSv1_1": TLSv1_1, TLSv1_1: {},
"TLSv1_2": TLSv1_2, TLSv1_2: {},
"TLSv1_3": TLSv1_3, TLSv1_3: {},
} }
// NOTE: This interface is deprecated in favor of TLSVersions // NOTE: This interface is deprecated in favor of tlsVersions
// and should be eventually removed in a future release. // and should be eventually removed in a future release.
DeprecatedConsulAgentTLSVersions = map[string]TLSVersion{ DeprecatedConsulAgentTLSVersions = map[string]TLSVersion{
"": TLSVersionAuto, "": TLSVersionAuto,
@ -46,24 +45,10 @@ var (
"tls12": TLSv1_2, "tls12": TLSv1_2,
"tls13": TLSv1_3, "tls13": TLSv1_3,
} }
HumanTLSVersionStrings = map[TLSVersion]string{
TLSVersionAuto: "Allow implementation to select TLS version",
TLSv1_0: "TLS 1.0",
TLSv1_1: "TLS 1.1",
TLSv1_2: "TLS 1.2",
TLSv1_3: "TLS 1.3",
}
ConsulConfigTLSVersionStrings = func() map[TLSVersion]string {
inverted := make(map[TLSVersion]string, len(TLSVersions))
for k, v := range TLSVersions {
inverted[v] = k
}
return inverted
}()
// NOTE: these currently map to the deprecated config strings to support the // NOTE: these currently map to the deprecated config strings to support the
// deployment pattern of upgrading servers first. This map should eventually // deployment pattern of upgrading servers first. This map should eventually
// be removed and any lookups updated to use ConsulConfigTLSVersionStrings // be removed and any lookups updated to instead use the TLSVersion string
// with newer config strings instead in a future release. // values directly in a future release.
ConsulAutoConfigTLSVersionStrings = map[TLSVersion]string{ ConsulAutoConfigTLSVersionStrings = map[TLSVersion]string{
TLSVersionAuto: "", TLSVersionAuto: "",
TLSv1_0: "tls10", TLSv1_0: "tls10",
@ -71,33 +56,51 @@ var (
TLSv1_2: "tls12", TLSv1_2: "tls12",
TLSv1_3: "tls13", TLSv1_3: "tls13",
} }
TLSVersionsWithConfigurableCipherSuites = map[TLSVersion]struct{}{
// NOTE: these two are implementation-dependent, but it is not expected that
// either Go or Envoy would default to TLS 1.3 as a minimum version in the
// near future
TLSVersionUnspecified: {},
TLSVersionAuto: {},
TLSv1_0: {},
TLSv1_1: {},
TLSv1_2: {},
}
) )
func (v TLSVersion) String() string { func (v *TLSVersion) String() string {
return ConsulConfigTLSVersionStrings[v] return string(*v)
} }
func (v TLSVersion) MarshalJSON() ([]byte, error) { var tlsVersionComparison = map[TLSVersion]uint{
return json.Marshal(v.String()) TLSv1_0: 1,
TLSv1_1: 2,
TLSv1_2: 3,
TLSv1_3: 4,
} }
func (v *TLSVersion) UnmarshalJSON(bytes []byte) error { // Will only return true for concrete versions and won't catch
versionStr := string(bytes) // implementation-dependent conflicts with TLSVersionAuto or unspecified values
func (a TLSVersion) LessThan(b TLSVersion) (error, bool) {
if n := len(versionStr); n > 1 && versionStr[0] == '"' && versionStr[n-1] == '"' { for _, v := range []TLSVersion{a, b} {
versionStr = versionStr[1 : n-1] // trim surrounding quotes if _, ok := tlsVersionComparison[v]; !ok {
return fmt.Errorf("can't compare implementation-dependent values"), false
}
} }
if version, ok := TLSVersions[versionStr]; ok { return nil, tlsVersionComparison[a] < tlsVersionComparison[b]
*v = version
return nil
}
*v = TLSVersionInvalid
return fmt.Errorf("no matching TLS Version found for %s", versionStr)
} }
// IANA cipher suite constants and values as defined at func ValidateTLSVersion(v TLSVersion) error {
if _, ok := tlsVersions[v]; !ok {
return fmt.Errorf("no matching TLS version found for %s", v.String())
}
return nil
}
// IANA cipher suite string constants as defined at
// https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml // https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml
// This is the total list of TLS 1.2-style cipher suites // This is the total list of TLS 1.2-style cipher suites
// which are currently supported by either Envoy 1.21 or the Consul agent // which are currently supported by either Envoy 1.21 or the Consul agent
@ -106,67 +109,49 @@ func (v *TLSVersion) UnmarshalJSON(bytes []byte) error {
// and as supported cipher suites in the Go runtime change. // and as supported cipher suites in the Go runtime change.
// //
// The naming convention for cipher suites changed in TLS 1.3 // The naming convention for cipher suites changed in TLS 1.3
// but constant values should still be globally unqiue // but constant values should still be globally unqiue.
// Handling validation on a subset of TLSCipherSuite constants //
// would be a future exercise if cipher suites for TLS 1.3 ever // Handling validation on distinct sets of TLS 1.3 and TLS 1.2 TLSCipherSuite
// become configurable in BoringSSL, Envoy, or other implementation // constants would be a future exercise if cipher suites for TLS 1.3 ever
type TLSCipherSuite uint16 // become configurable in BoringSSL, Envoy, or other implementation.
type TLSCipherSuite string
const ( const (
// Envoy cipher suites also used by Consul agent // Cipher suites used by both Envoy and Consul agent
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLSCipherSuite = 0xc02b TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xcca9 // Not used by Consul agent yet TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xc02f TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xcca8 // Not used by Consul agent yet TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xc009 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xc013 TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xc02c TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xc030 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xc00a TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xc014 TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"
// Older cipher suites not supported for Consul agent TLS, will eventually be removed from Envoy defaults // Older cipher suites not supported for Consul agent TLS,
TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009c // will eventually be removed from Envoy defaults
TLS_RSA_WITH_AES_128_CBC_SHA = 0x002f TLS_RSA_WITH_AES_128_GCM_SHA256 = "TLS_RSA_WITH_AES_128_GCM_SHA256"
TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009d TLS_RSA_WITH_AES_128_CBC_SHA = "TLS_RSA_WITH_AES_128_CBC_SHA"
TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035 TLS_RSA_WITH_AES_256_GCM_SHA384 = "TLS_RSA_WITH_AES_256_GCM_SHA384"
TLS_RSA_WITH_AES_256_CBC_SHA = "TLS_RSA_WITH_AES_256_CBC_SHA"
// Additional cipher suites used by Consul agent but not Envoy
// TODO: these are both explicitly listed as insecure and disabled in the Go source, should they be removed?
// https://cs.opensource.google/go/go/+/refs/tags/go1.17.3:src/crypto/tls/cipher_suites.go;l=329-330
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0x0023
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xc027
) )
var ( var (
TLSCipherSuites = map[string]TLSCipherSuite{ consulAgentTLSCipherSuites = map[TLSCipherSuite]struct{}{
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256": TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: {},
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: {},
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: {},
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: {},
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: {},
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256": TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
"TLS_RSA_WITH_AES_128_GCM_SHA256": TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: {},
"TLS_RSA_WITH_AES_128_CBC_SHA": TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: {},
"TLS_RSA_WITH_AES_256_GCM_SHA384": TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: {},
"TLS_RSA_WITH_AES_256_CBC_SHA": TLS_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: {},
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: {},
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
} }
HumanTLSCipherSuiteStrings = func() map[TLSCipherSuite]string { envoyTLSCipherSuiteStrings = map[TLSCipherSuite]string{
inverted := make(map[TLSCipherSuite]string, len(TLSCipherSuites))
for k, v := range TLSCipherSuites {
inverted[v] = k
}
return inverted
}()
EnvoyTLSCipherSuiteStrings = map[TLSCipherSuite]string{
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "ECDHE-ECDSA-AES128-GCM-SHA256", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "ECDHE-ECDSA-AES128-GCM-SHA256",
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: "ECDHE-ECDSA-CHACHA20-POLY1305", TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: "ECDHE-ECDSA-CHACHA20-POLY1305",
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: "ECDHE-RSA-AES128-GCM-SHA256", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: "ECDHE-RSA-AES128-GCM-SHA256",
@ -183,3 +168,50 @@ var (
TLS_RSA_WITH_AES_256_CBC_SHA: "AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA: "AES256-SHA",
} }
) )
func (c *TLSCipherSuite) String() string {
return string(*c)
}
func ValidateConsulAgentCipherSuites(cipherSuites []TLSCipherSuite) error {
var unmatched []string
for _, c := range cipherSuites {
if _, ok := consulAgentTLSCipherSuites[c]; !ok {
unmatched = append(unmatched, c.String())
}
}
if len(unmatched) > 0 {
return fmt.Errorf("no matching Consul Agent TLS cipher suite found for %s", strings.Join(unmatched, ","))
}
return nil
}
func ValidateEnvoyCipherSuites(cipherSuites []TLSCipherSuite) error {
var unmatched []string
for _, c := range cipherSuites {
if _, ok := envoyTLSCipherSuiteStrings[c]; !ok {
unmatched = append(unmatched, c.String())
}
}
if len(unmatched) > 0 {
return fmt.Errorf("no matching Envoy TLS cipher suite found for %s", strings.Join(unmatched, ","))
}
return nil
}
func MarshalEnvoyTLSCipherSuiteStrings(cipherSuites []TLSCipherSuite) []string {
cipherSuiteStrings := []string{}
for _, c := range cipherSuites {
if s, ok := envoyTLSCipherSuiteStrings[c]; ok {
cipherSuiteStrings = append(cipherSuiteStrings, s)
}
}
return cipherSuiteStrings
}

View File

@ -7,14 +7,12 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestTLSVersion_PartialEq(t *testing.T) { func TestTLSVersion_Valid(t *testing.T) {
require.Greater(t, TLSv1_3, TLSv1_2) require.NoError(t, ValidateTLSVersion("TLS_AUTO"))
require.Greater(t, TLSv1_2, TLSv1_1) require.NoError(t, ValidateTLSVersion("TLSv1_0"))
require.Greater(t, TLSv1_1, TLSv1_0) require.NoError(t, ValidateTLSVersion("TLSv1_1"))
require.NoError(t, ValidateTLSVersion("TLSv1_2"))
require.Less(t, TLSv1_2, TLSv1_3) require.NoError(t, ValidateTLSVersion("TLSv1_3"))
require.Less(t, TLSv1_1, TLSv1_2)
require.Less(t, TLSv1_0, TLSv1_1)
} }
func TestTLSVersion_Invalid(t *testing.T) { func TestTLSVersion_Invalid(t *testing.T) {
@ -33,16 +31,19 @@ func TestTLSVersion_Zero(t *testing.T) {
func TestTLSVersion_ToJSON(t *testing.T) { func TestTLSVersion_ToJSON(t *testing.T) {
var tlsVersion TLSVersion var tlsVersion TLSVersion
err := tlsVersion.UnmarshalJSON([]byte(`"foo"`))
require.Error(t, err)
require.Equal(t, tlsVersion, TLSVersionInvalid)
for str, version := range TLSVersions { // Unmarshalling won't catch invalid version strings,
// must be checked in config or config entry validation
err := json.Unmarshal([]byte(`"foo"`), &tlsVersion)
require.NoError(t, err)
for version := range tlsVersions {
str := version.String()
versionJSON, err := json.Marshal(version) versionJSON, err := json.Marshal(version)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, versionJSON, []byte(`"`+str+`"`)) require.Equal(t, versionJSON, []byte(`"`+str+`"`))
err = tlsVersion.UnmarshalJSON([]byte(`"` + str + `"`)) err = json.Unmarshal([]byte(`"`+str+`"`), &tlsVersion)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, tlsVersion, version) require.Equal(t, tlsVersion, version)
} }