Standardize support for Tagged and BindAddresses in Ingress Gateways (#7924)

* Standardize support for Tagged and BindAddresses in Ingress Gateways

This updates the TaggedAddresses and BindAddresses behavior for Ingress
to match Mesh/Terminating gateways. The `consul connect envoy` command
now also allows passing an address without a port for tagged/bind
addresses.

* Update command/connect/envoy/envoy.go

Co-authored-by: Freddy <freddygv@users.noreply.github.com>

* PR comments

* Check to see if address is an actual IP address

* Update agent/xds/listeners.go

Co-authored-by: Freddy <freddygv@users.noreply.github.com>

* fix whitespace

Co-authored-by: Chris Piraino <cpiraino@hashicorp.com>
Co-authored-by: Freddy <freddygv@users.noreply.github.com>
This commit is contained in:
Kyle Havlovitz 2020-05-21 07:08:12 -07:00 committed by GitHub
parent aedabfbf57
commit b14696e32a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 351 additions and 123 deletions

View File

@ -1350,7 +1350,7 @@ func (s *state) handleUpdateIngressGateway(u cache.UpdateEvent, snap *ConfigSnap
watchedSvcs := make(map[string]struct{}) watchedSvcs := make(map[string]struct{})
upstreamsMap := make(map[IngressListenerKey]structs.Upstreams) upstreamsMap := make(map[IngressListenerKey]structs.Upstreams)
for _, service := range services.Services { for _, service := range services.Services {
u := makeUpstream(service, s.address) u := makeUpstream(service)
err := s.watchIngressDiscoveryChain(snap, u) err := s.watchIngressDiscoveryChain(snap, u)
if err != nil { if err != nil {
@ -1386,7 +1386,7 @@ func (s *state) handleUpdateIngressGateway(u cache.UpdateEvent, snap *ConfigSnap
return nil return nil
} }
func makeUpstream(g *structs.GatewayService, bindAddr string) structs.Upstream { func makeUpstream(g *structs.GatewayService) structs.Upstream {
upstream := structs.Upstream{ upstream := structs.Upstream{
DestinationName: g.Service.ID, DestinationName: g.Service.ID,
DestinationNamespace: g.Service.NamespaceOrDefault(), DestinationNamespace: g.Service.NamespaceOrDefault(),
@ -1398,10 +1398,6 @@ func makeUpstream(g *structs.GatewayService, bindAddr string) structs.Upstream {
"protocol": g.Protocol, "protocol": g.Protocol,
}, },
} }
upstream.LocalBindAddress = bindAddr
if bindAddr == "" {
upstream.LocalBindAddress = "0.0.0.0"
}
return upstream return upstream
} }

View File

@ -782,7 +782,6 @@ func TestState_WatchesAndUpdates(t *testing.T) {
{ {
DestinationNamespace: "default", DestinationNamespace: "default",
DestinationName: "api", DestinationName: "api",
LocalBindAddress: "10.0.1.1",
LocalBindPort: 9999, LocalBindPort: 9999,
Config: map[string]interface{}{ Config: map[string]interface{}{
"protocol": "http", "protocol": "http",

View File

@ -11,6 +11,7 @@ import (
"strings" "strings"
"github.com/hashicorp/consul/logging" "github.com/hashicorp/consul/logging"
"github.com/hashicorp/go-hclog"
envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2" envoy "github.com/envoyproxy/go-control-plane/envoy/api/v2"
envoyauth "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth" envoyauth "github.com/envoyproxy/go-control-plane/envoy/api/v2/auth"
@ -45,7 +46,7 @@ func (s *Server) listenersFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot, token s
case structs.ServiceKindMeshGateway: case structs.ServiceKindMeshGateway:
return s.listenersFromSnapshotGateway(cfgSnap, token) return s.listenersFromSnapshotGateway(cfgSnap, token)
case structs.ServiceKindIngressGateway: case structs.ServiceKindIngressGateway:
return s.listenersFromSnapshotIngressGateway(cfgSnap) return s.listenersFromSnapshotGateway(cfgSnap, token)
default: default:
return nil, fmt.Errorf("Invalid service kind: %v", cfgSnap.Kind) return nil, fmt.Errorf("Invalid service kind: %v", cfgSnap.Kind)
} }
@ -71,11 +72,13 @@ func (s *Server) listenersFromSnapshotConnectProxy(cfgSnap *proxycfg.ConfigSnaps
} }
var upstreamListener proto.Message var upstreamListener proto.Message
if chain == nil || chain.IsDefault() { upstreamListener, err = s.makeUpstreamListenerForDiscoveryChain(
upstreamListener, err = s.makeUpstreamListenerIgnoreDiscoveryChain(&u, chain, cfgSnap, nil) &u,
} else { u.LocalBindAddress,
upstreamListener, err = s.makeUpstreamListenerForDiscoveryChain(&u, chain, cfgSnap, nil) chain,
} cfgSnap,
nil,
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -254,6 +257,12 @@ func (s *Server) listenersFromSnapshotGateway(cfgSnap *proxycfg.ConfigSnapshot,
if err != nil { if err != nil {
return nil, err return nil, err
} }
case structs.ServiceKindIngressGateway:
listeners, err := s.makeIngressGatewayListeners(a.Address, cfgSnap)
if err != nil {
return nil, err
}
resources = append(resources, listeners...)
case structs.ServiceKindMeshGateway: case structs.ServiceKindMeshGateway:
l, err = s.makeMeshGatewayListener(a.name, a.Address, a.Port, cfgSnap) l, err = s.makeMeshGatewayListener(a.name, a.Address, a.Port, cfgSnap)
if err != nil { if err != nil {
@ -267,10 +276,9 @@ func (s *Server) listenersFromSnapshotGateway(cfgSnap *proxycfg.ConfigSnapshot,
return resources, err return resources, err
} }
// TODO(ingress): Support configured bind addresses from similar to mesh gateways func (s *Server) makeIngressGatewayListeners(address string, cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) {
// See: https://www.consul.io/docs/connect/proxies/envoy.html#mesh-gateway-options
func (s *Server) listenersFromSnapshotIngressGateway(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 *envoyauth.DownstreamTlsContext var tlsContext *envoyauth.DownstreamTlsContext
if cfgSnap.IngressGateway.TLSEnabled { if cfgSnap.IngressGateway.TLSEnabled {
@ -290,24 +298,20 @@ func (s *Server) listenersFromSnapshotIngressGateway(cfgSnap *proxycfg.ConfigSna
chain := cfgSnap.IngressGateway.DiscoveryChain[id] chain := cfgSnap.IngressGateway.DiscoveryChain[id]
var upstreamListener proto.Message var upstreamListener proto.Message
var err error upstreamListener, err := s.makeUpstreamListenerForDiscoveryChain(
if chain == nil || chain.IsDefault() { &u,
upstreamListener, err = s.makeUpstreamListenerIgnoreDiscoveryChain(&u, chain, cfgSnap, tlsContext) address,
} else { chain,
upstreamListener, err = s.makeUpstreamListenerForDiscoveryChain(&u, chain, cfgSnap, tlsContext) cfgSnap,
} tlsContext,
)
if err != nil { if err != nil {
return nil, err return nil, err
} }
resources = append(resources, upstreamListener) resources = append(resources, upstreamListener)
} else { } else {
// If multiple upstreams share this port, make a special listener for the protocol. // If multiple upstreams share this port, make a special listener for the protocol.
addr := cfgSnap.Address listener := makeListener(listenerKey.Protocol, address, listenerKey.Port)
if addr == "" {
addr = "0.0.0.0"
}
listener := makeListener(listenerKey.Protocol, addr, listenerKey.Port)
filter, err := makeListenerFilter( filter, err := makeListenerFilter(
true, listenerKey.Protocol, listenerKey.RouteName(), "", "ingress_upstream_", "", false) true, listenerKey.Protocol, listenerKey.RouteName(), "", "ingress_upstream_", "", false)
if err != nil { if err != nil {
@ -541,56 +545,6 @@ func (s *Server) makeExposedCheckListener(cfgSnap *proxycfg.ConfigSnapshot, clus
return l, err return l, err
} }
// makeUpstreamListenerIgnoreDiscoveryChain counterintuitively takes an (optional) chain
func (s *Server) makeUpstreamListenerIgnoreDiscoveryChain(
u *structs.Upstream,
chain *structs.CompiledDiscoveryChain,
cfgSnap *proxycfg.ConfigSnapshot,
tlsContext *envoyauth.DownstreamTlsContext,
) (proto.Message, error) {
cfg, err := ParseUpstreamConfig(u.Config)
if err != nil {
// Don't hard fail on a config typo, just warn. The parse func returns
// default config if there is an error so it's safe to continue.
s.Logger.Warn("failed to parse", "upstream", u.Identifier(), "error", err)
}
if cfg.ListenerJSON != "" {
return makeListenerFromUserConfig(cfg.ListenerJSON)
}
addr := u.LocalBindAddress
if addr == "" {
addr = "127.0.0.1"
}
upstreamID := u.Identifier()
dc := u.Datacenter
if dc == "" {
dc = cfgSnap.Datacenter
}
sni := connect.UpstreamSNI(u, "", dc, cfgSnap.Roots.TrustDomain)
clusterName := CustomizeClusterName(sni, chain)
l := makeListener(upstreamID, addr, u.LocalBindPort)
filter, err := makeListenerFilter(
false, cfg.Protocol, upstreamID, clusterName, "upstream_", "", false)
if err != nil {
return nil, err
}
l.FilterChains = []envoylistener.FilterChain{
{
Filters: []envoylistener.Filter{
filter,
},
TlsContext: tlsContext,
},
}
return l, nil
}
func (s *Server) makeTerminatingGatewayListener(name, addr string, port int, cfgSnap *proxycfg.ConfigSnapshot, token string) (*envoy.Listener, error) { func (s *Server) makeTerminatingGatewayListener(name, addr string, port int, cfgSnap *proxycfg.ConfigSnapshot, token string) (*envoy.Listener, error) {
l := makeListener(name, addr, port) l := makeListener(name, addr, port)
@ -790,56 +744,50 @@ func (s *Server) makeMeshGatewayListener(name, addr string, port int, cfgSnap *p
func (s *Server) makeUpstreamListenerForDiscoveryChain( func (s *Server) makeUpstreamListenerForDiscoveryChain(
u *structs.Upstream, u *structs.Upstream,
address string,
chain *structs.CompiledDiscoveryChain, chain *structs.CompiledDiscoveryChain,
cfgSnap *proxycfg.ConfigSnapshot, cfgSnap *proxycfg.ConfigSnapshot,
tlsContext *envoyauth.DownstreamTlsContext, tlsContext *envoyauth.DownstreamTlsContext,
) (proto.Message, error) { ) (proto.Message, error) {
cfg, err := ParseUpstreamConfigNoDefaults(u.Config) if address == "" {
if err != nil { address = "127.0.0.1"
// Don't hard fail on a config typo, just warn. The parse func returns
// default config if there is an error so it's safe to continue.
s.Logger.Warn("failed to parse", "upstream", u.Identifier(), "error", err)
} }
if cfg.ListenerJSON != "" {
s.Logger.Warn("ignoring escape hatch setting because already configured for",
"discovery chain", chain.ServiceName, "upstream", u.Identifier(), "config", "envoy_listener_json")
}
addr := u.LocalBindAddress
if addr == "" {
addr = "127.0.0.1"
}
upstreamID := u.Identifier() upstreamID := u.Identifier()
l := makeListener(upstreamID, address, u.LocalBindPort)
l := makeListener(upstreamID, addr, u.LocalBindPort) cfg := getAndModifyUpstreamConfigForListener(s.Logger, u, chain)
if cfg.ListenerJSON != "" {
proto := cfg.Protocol return makeListenerFromUserConfig(cfg.ListenerJSON)
if proto == "" {
proto = chain.Protocol
}
if proto == "" {
proto = "tcp"
} }
useRDS := true useRDS := true
clusterName := "" clusterName := ""
if proto == "tcp" { if chain == nil || chain.IsDefault() {
dc := u.Datacenter
if dc == "" {
dc = cfgSnap.Datacenter
}
sni := connect.UpstreamSNI(u, "", dc, cfgSnap.Roots.TrustDomain)
useRDS = false
clusterName = CustomizeClusterName(sni, chain)
} else if cfg.Protocol == "tcp" {
startNode := chain.Nodes[chain.StartNode] startNode := chain.Nodes[chain.StartNode]
if startNode == nil { if startNode == nil {
panic("missing first node in compiled discovery chain for: " + chain.ServiceName) panic("missing first node in compiled discovery chain for: " + chain.ServiceName)
} else if startNode.Type != structs.DiscoveryGraphNodeTypeResolver { } else if startNode.Type != structs.DiscoveryGraphNodeTypeResolver {
panic(fmt.Sprintf("unexpected first node in discovery chain using protocol=%q: %s", proto, startNode.Type)) panic(fmt.Sprintf("unexpected first node in discovery chain using protocol=%q: %s", cfg.Protocol, startNode.Type))
} }
targetID := startNode.Resolver.Target targetID := startNode.Resolver.Target
target := chain.Targets[targetID] target := chain.Targets[targetID]
clusterName = CustomizeClusterName(target.Name, chain)
useRDS = false useRDS = false
clusterName = CustomizeClusterName(target.Name, chain)
} }
filter, err := makeListenerFilter( filter, err := makeListenerFilter(
useRDS, proto, upstreamID, clusterName, "upstream_", "", false) useRDS, cfg.Protocol, upstreamID, clusterName, "upstream_", "", false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -855,6 +803,53 @@ func (s *Server) makeUpstreamListenerForDiscoveryChain(
return l, nil return l, nil
} }
func getAndModifyUpstreamConfigForListener(logger hclog.Logger, u *structs.Upstream, chain *structs.CompiledDiscoveryChain) UpstreamConfig {
var (
cfg UpstreamConfig
err error
)
if chain == nil || chain.IsDefault() {
cfg, err = ParseUpstreamConfig(u.Config)
if err != nil {
// Don't hard fail on a config typo, just warn. The parse func returns
// default config if there is an error so it's safe to continue.
logger.Warn("failed to parse", "upstream", u.Identifier(), "error", err)
}
} else {
// Use NoDefaults here so that we can set the protocol to the chain
// protocol if necessary
cfg, err = ParseUpstreamConfigNoDefaults(u.Config)
if err != nil {
// Don't hard fail on a config typo, just warn. The parse func returns
// default config if there is an error so it's safe to continue.
logger.Warn("failed to parse", "upstream", u.Identifier(), "error", err)
}
if cfg.ListenerJSON != "" {
logger.Warn("ignoring escape hatch setting because already configured for",
"discovery chain", chain.ServiceName, "upstream", u.Identifier(), "config", "envoy_listener_json")
// Remove from config struct so we don't use it later on
cfg.ListenerJSON = ""
}
proto := cfg.Protocol
if proto == "" {
proto = chain.Protocol
}
if proto == "" {
proto = "tcp"
}
// set back on the config so that we can use it from return value
cfg.Protocol = proto
}
return cfg
}
func makeListenerFilter( func makeListenerFilter(
useRDS bool, useRDS bool,
protocol, filterName, cluster, statPrefix, routePath string, ingress bool) (envoylistener.Filter, error) { protocol, filterName, cluster, statPrefix, routePath string, ingress bool) (envoylistener.Filter, error) {

View File

@ -128,6 +128,17 @@ func TestListenersFromSnapshot(t *testing.T) {
}) })
}, },
}, },
{
name: "custom-upstream-typed-ignored-with-disco-chain",
create: proxycfg.TestConfigSnapshotDiscoveryChainWithFailover,
setup: func(snap *proxycfg.ConfigSnapshot) {
snap.Proxy.Upstreams[0].Config["envoy_listener_json"] =
customListenerJSON(t, customListenerJSONOptions{
Name: "custom-upstream",
IncludeType: true,
})
},
},
{ {
name: "splitter-with-resolver-redirect", name: "splitter-with-resolver-redirect",
create: proxycfg.TestConfigSnapshotDiscoveryChain_SplitterWithResolverRedirectMultiDC, create: proxycfg.TestConfigSnapshotDiscoveryChain_SplitterWithResolverRedirectMultiDC,
@ -268,6 +279,23 @@ func TestListenersFromSnapshot(t *testing.T) {
create: proxycfg.TestConfigSnapshotIngressGateway, create: proxycfg.TestConfigSnapshotIngressGateway,
setup: nil, setup: nil,
}, },
{
name: "ingress-gateway-bind-addrs",
create: proxycfg.TestConfigSnapshotIngressGateway,
setup: func(snap *proxycfg.ConfigSnapshot) {
snap.TaggedAddresses = map[string]structs.ServiceAddress{
"lan": structs.ServiceAddress{Address: "10.0.0.1"},
"wan": structs.ServiceAddress{Address: "172.16.0.1"},
}
snap.Proxy.Config = map[string]interface{}{
"envoy_gateway_no_default_bind": true,
"envoy_gateway_bind_tagged_addresses": true,
"envoy_gateway_bind_addresses": map[string]structs.ServiceAddress{
"foo": structs.ServiceAddress{Address: "8.8.8.8"},
},
}
},
},
{ {
name: "ingress-gateway-no-services", name: "ingress-gateway-no-services",
create: proxycfg.TestConfigSnapshotIngressGatewayNoServices, create: proxycfg.TestConfigSnapshotIngressGatewayNoServices,

View File

@ -0,0 +1,116 @@
{
"versionInfo": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.api.v2.Listener",
"name": "db:127.0.0.1:9191",
"address": {
"socketAddress": {
"address": "127.0.0.1",
"portValue": 9191
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.tcp_proxy",
"config": {
"cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"stat_prefix": "upstream_db_tcp"
}
}
]
}
]
},
{
"@type": "type.googleapis.com/envoy.api.v2.Listener",
"name": "prepared_query:geo-cache:127.10.10.10:8181",
"address": {
"socketAddress": {
"address": "127.10.10.10",
"portValue": 8181
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.tcp_proxy",
"config": {
"cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul",
"stat_prefix": "upstream_prepared_query_geo-cache_tcp"
}
}
]
}
]
},
{
"@type": "type.googleapis.com/envoy.api.v2.Listener",
"name": "public_listener:0.0.0.0:9999",
"address": {
"socketAddress": {
"address": "0.0.0.0",
"portValue": 9999
}
},
"filterChains": [
{
"tlsContext": {
"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": true
},
"filters": [
{
"name": "envoy.ext_authz",
"config": {
"grpc_service": {
"envoy_grpc": {
"cluster_name": "local_agent"
},
"initial_metadata": [
{
"key": "x-consul-token",
"value": "my-token"
}
]
},
"stat_prefix": "connect_authz"
}
},
{
"name": "envoy.tcp_proxy",
"config": {
"cluster": "local_app",
"stat_prefix": "public_listener_tcp"
}
}
]
}
]
}
],
"typeUrl": "type.googleapis.com/envoy.api.v2.Listener",
"nonce": "00000001"
}

View File

@ -0,0 +1,76 @@
{
"versionInfo": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.api.v2.Listener",
"name": "db:10.0.0.1:9191",
"address": {
"socketAddress": {
"address": "10.0.0.1",
"portValue": 9191
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.tcp_proxy",
"config": {
"cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"stat_prefix": "upstream_db_tcp"
}
}
]
}
]
},
{
"@type": "type.googleapis.com/envoy.api.v2.Listener",
"name": "db:172.16.0.1:9191",
"address": {
"socketAddress": {
"address": "172.16.0.1",
"portValue": 9191
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.tcp_proxy",
"config": {
"cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"stat_prefix": "upstream_db_tcp"
}
}
]
}
]
},
{
"@type": "type.googleapis.com/envoy.api.v2.Listener",
"name": "db:8.8.8.8:9191",
"address": {
"socketAddress": {
"address": "8.8.8.8",
"portValue": 9191
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.tcp_proxy",
"config": {
"cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"stat_prefix": "upstream_db_tcp"
}
}
]
}
]
}
],
"typeUrl": "type.googleapis.com/envoy.api.v2.Listener",
"nonce": "00000001"
}

View File

@ -3,10 +3,10 @@
"resources": [ "resources": [
{ {
"@type": "type.googleapis.com/envoy.api.v2.Listener", "@type": "type.googleapis.com/envoy.api.v2.Listener",
"name": "db:2.3.4.5:9191", "name": "db:1.2.3.4:9191",
"address": { "address": {
"socketAddress": { "socketAddress": {
"address": "2.3.4.5", "address": "1.2.3.4",
"portValue": 9191 "portValue": 9191
} }
}, },

View File

@ -3,10 +3,10 @@
"resources": [ "resources": [
{ {
"@type": "type.googleapis.com/envoy.api.v2.Listener", "@type": "type.googleapis.com/envoy.api.v2.Listener",
"name": "db:2.3.4.5:9191", "name": "db:1.2.3.4:9191",
"address": { "address": {
"socketAddress": { "socketAddress": {
"address": "2.3.4.5", "address": "1.2.3.4",
"portValue": 9191 "portValue": 9191
} }
}, },

View File

@ -3,10 +3,10 @@
"resources": [ "resources": [
{ {
"@type": "type.googleapis.com/envoy.api.v2.Listener", "@type": "type.googleapis.com/envoy.api.v2.Listener",
"name": "db:2.3.4.5:9191", "name": "db:1.2.3.4:9191",
"address": { "address": {
"socketAddress": { "socketAddress": {
"address": "2.3.4.5", "address": "1.2.3.4",
"portValue": 9191 "portValue": 9191
} }
}, },

View File

@ -3,10 +3,10 @@
"resources": [ "resources": [
{ {
"@type": "type.googleapis.com/envoy.api.v2.Listener", "@type": "type.googleapis.com/envoy.api.v2.Listener",
"name": "db:2.3.4.5:9191", "name": "db:1.2.3.4:9191",
"address": { "address": {
"socketAddress": { "socketAddress": {
"address": "2.3.4.5", "address": "1.2.3.4",
"portValue": 9191 "portValue": 9191
} }
}, },

View File

@ -3,10 +3,10 @@
"resources": [ "resources": [
{ {
"@type": "type.googleapis.com/envoy.api.v2.Listener", "@type": "type.googleapis.com/envoy.api.v2.Listener",
"name": "db:2.3.4.5:9191", "name": "db:1.2.3.4:9191",
"address": { "address": {
"socketAddress": { "socketAddress": {
"address": "2.3.4.5", "address": "1.2.3.4",
"portValue": 9191 "portValue": 9191
} }
}, },

View File

@ -3,10 +3,10 @@
"resources": [ "resources": [
{ {
"@type": "type.googleapis.com/envoy.api.v2.Listener", "@type": "type.googleapis.com/envoy.api.v2.Listener",
"name": "db:2.3.4.5:9191", "name": "db:1.2.3.4:9191",
"address": { "address": {
"socketAddress": { "socketAddress": {
"address": "2.3.4.5", "address": "1.2.3.4",
"portValue": 9191 "portValue": 9191
} }
}, },

View File

@ -136,7 +136,8 @@ func (c *cmd) init() {
"LAN address to advertise in the gateway service registration") "LAN address to advertise in the gateway service registration")
c.flags.Var(&c.wanAddress, "wan-address", c.flags.Var(&c.wanAddress, "wan-address",
"WAN address to advertise in the gateway service registration") "WAN address to advertise in the gateway service registration. For ingress gateways, "+
"only an IP address (without a port) is required.")
c.flags.Var(&c.bindAddresses, "bind-address", "Bind "+ c.flags.Var(&c.bindAddresses, "bind-address", "Bind "+
"address to use instead of the default binding rules given as `<name>=<ip>:<port>` "+ "address to use instead of the default binding rules given as `<name>=<ip>:<port>` "+

View File

@ -47,8 +47,17 @@ func parseAddress(raw string) (api.ServiceAddress, error) {
} }
addr, portStr, err := net.SplitHostPort(x) addr, portStr, err := net.SplitHostPort(x)
// Error message from Go's net/ipsock.go
if err != nil { if err != nil {
return result, fmt.Errorf("Error parsing address %q: %v", x, err) if !strings.Contains(err.Error(), "missing port in address") {
return result, fmt.Errorf("Error parsing address %q: %v", x, err)
}
// Use the whole input as the address if there wasn't a port.
if ip := net.ParseIP(x); ip == nil {
return result, fmt.Errorf("Error parsing address %q: not an IP address", x)
}
addr = x
} }
port := defaultGatewayPort port := defaultGatewayPort

View File

@ -67,9 +67,17 @@ func TestServiceAddressValue_Set(t *testing.T) {
expectedValue: api.ServiceAddress{Address: "8.8.8.8", Port: 1234}, expectedValue: api.ServiceAddress{Address: "8.8.8.8", Port: 1234},
}, },
{ {
name: "invalid address", name: "address with no port",
input: "not-an-address", input: "8.8.8.8",
expectedErr: "missing port in address", expectedValue: api.ServiceAddress{
Address: "8.8.8.8",
Port: defaultGatewayPort,
},
},
{
name: "invalid addres",
input: "not-an-ip-address",
expectedErr: "not an IP address",
}, },
{ {
name: "invalid port", name: "invalid port",