diff --git a/.changelog/9981.txt b/.changelog/9981.txt new file mode 100644 index 000000000..aeafabb70 --- /dev/null +++ b/.changelog/9981.txt @@ -0,0 +1,3 @@ +```release-note:feature +connect: add support for unix domain sockets addresses for service upstreams and downstreams +``` diff --git a/agent/agent_endpoint_test.go b/agent/agent_endpoint_test.go index 5aba74bf1..f88cfa45b 100644 --- a/agent/agent_endpoint_test.go +++ b/agent/agent_endpoint_test.go @@ -399,7 +399,7 @@ func TestAgent_Service(t *testing.T) { Service: "web-sidecar-proxy", Port: 8000, Proxy: expectProxy.ToAPI(), - ContentHash: "eb557bc310d4f8a0", + ContentHash: "35ad6dd5b1ff8d18", Weights: api.AgentWeights{ Passing: 1, Warning: 1, @@ -413,7 +413,7 @@ func TestAgent_Service(t *testing.T) { // Copy and modify updatedResponse := *expectedResponse updatedResponse.Port = 9999 - updatedResponse.ContentHash = "d61c11f438c7eb02" + updatedResponse.ContentHash = "8e407e299ec9eba" // Simple response for non-proxy service registered in TestAgent config expectWebResponse := &api.AgentService{ diff --git a/agent/config/builder.go b/agent/config/builder.go index dd9347834..f4e0c33b8 100644 --- a/agent/config/builder.go +++ b/agent/config/builder.go @@ -14,6 +14,7 @@ import ( "reflect" "regexp" "sort" + "strconv" "strings" "time" @@ -1640,6 +1641,14 @@ func (b *builder) serviceVal(v *ServiceDefinition) *structs.ServiceDefinition { if err := structs.ValidateWeights(serviceWeights); err != nil { b.err = multierror.Append(fmt.Errorf("Invalid weight definition for service %s: %s", stringVal(v.Name), err)) } + + if (v.Port != nil || v.Address != nil) && (v.SocketPath != nil) { + b.err = multierror.Append( + fmt.Errorf("service %s cannot have both socket path %s and address/port", + stringVal(v.Name), stringVal(v.SocketPath)), b.err) + + } + return &structs.ServiceDefinition{ Kind: kind, ID: stringVal(v.ID), @@ -1649,6 +1658,7 @@ func (b *builder) serviceVal(v *ServiceDefinition) *structs.ServiceDefinition { TaggedAddresses: b.svcTaggedAddresses(v.TaggedAddresses), Meta: meta, Port: intVal(v.Port), + SocketPath: stringVal(v.SocketPath), Token: stringVal(v.Token), EnableTagOverride: boolVal(v.EnableTagOverride), Weights: serviceWeights, @@ -1687,6 +1697,7 @@ func (b *builder) serviceProxyVal(v *ServiceProxy) *structs.ConnectProxyConfig { DestinationServiceID: stringVal(v.DestinationServiceID), LocalServiceAddress: stringVal(v.LocalServiceAddress), LocalServicePort: intVal(v.LocalServicePort), + LocalServiceSocketPath: stringVal(&v.LocalServiceSocketPath), Config: v.Config, Upstreams: b.upstreamsVal(v.Upstreams), MeshGateway: b.meshGatewayConfVal(v.MeshGateway), @@ -1706,6 +1717,8 @@ func (b *builder) upstreamsVal(v []Upstream) structs.Upstreams { Datacenter: stringVal(u.Datacenter), LocalBindAddress: stringVal(u.LocalBindAddress), LocalBindPort: intVal(u.LocalBindPort), + LocalBindSocketPath: stringVal(u.LocalBindSocketPath), + LocalBindSocketMode: b.unixPermissionsVal("local_bind_socket_mode", u.LocalBindSocketMode), Config: u.Config, MeshGateway: b.meshGatewayConfVal(u.MeshGateway), } @@ -1892,6 +1905,18 @@ func uint64Val(v *uint64) uint64 { return *v } +// Expect an octal permissions string, e.g. 0644 +func (b *builder) unixPermissionsVal(name string, v *string) string { + if v == nil { + return "" + } + if _, err := strconv.ParseUint(*v, 8, 32); err == nil { + return *v + } + b.err = multierror.Append(b.err, fmt.Errorf("%s: invalid mode: %s", name, *v)) + return "0" +} + func (b *builder) portVal(name string, v *int) int { if v == nil || *v <= 0 { return -1 diff --git a/agent/config/builder_test.go b/agent/config/builder_test.go index 332f27f18..2354f4211 100644 --- a/agent/config/builder_test.go +++ b/agent/config/builder_test.go @@ -175,6 +175,30 @@ func TestBuilder_BuildAndValidate_NodeName(t *testing.T) { } } +func TestBuilder_unixPermissionsVal(t *testing.T) { + + b, _ := newBuilder(LoadOpts{ + FlagValues: Config{ + NodeName: pString("foo"), + DataDir: pString("dir"), + }, + }) + + goodmode := "666" + badmode := "9666" + + patchLoadOptsShims(&b.opts) + require.NoError(t, b.err) + _ = b.unixPermissionsVal("local_bind_socket_mode", &goodmode) + require.NoError(t, b.err) + require.Len(t, b.Warnings, 0) + + _ = b.unixPermissionsVal("local_bind_socket_mode", &badmode) + require.NotNil(t, b.err) + require.Contains(t, b.err.Error(), "local_bind_socket_mode: invalid mode") + require.Len(t, b.Warnings, 0) +} + func patchLoadOptsShims(opts *LoadOpts) { if opts.hostname == nil { opts.hostname = func() (string, error) { diff --git a/agent/config/config.go b/agent/config/config.go index b65cb5280..149d7f16f 100644 --- a/agent/config/config.go +++ b/agent/config/config.go @@ -375,6 +375,7 @@ type ServiceDefinition struct { TaggedAddresses map[string]ServiceAddress `mapstructure:"tagged_addresses"` Meta map[string]string `mapstructure:"meta"` Port *int `mapstructure:"port"` + SocketPath *string `mapstructure:"socket_path"` Check *CheckDefinition `mapstructure:"check"` Checks []CheckDefinition `mapstructure:"checks"` Token *string `mapstructure:"token"` @@ -461,6 +462,10 @@ type ServiceProxy struct { // (DestinationServiceID is set) but otherwise will be ignored. LocalServicePort *int `mapstructure:"local_service_port"` + // LocalServiceSocketPath is the socket of the local service instance. It is optional + // and should only be specified for "side-car" style proxies. + LocalServiceSocketPath string `mapstructure:"local_service_socket_path"` + // TransparentProxy configuration. TransparentProxy *TransparentProxyConfig `mapstructure:"transparent_proxy"` @@ -503,14 +508,21 @@ type Upstream struct { // datacenter. Datacenter *string `mapstructure:"datacenter"` + // It would be worth thinking about a separate structure for these four items, + // unifying under address as something like "unix:/tmp/foo", "tcp:localhost:80" could make sense // LocalBindAddress is the ip address a side-car proxy should listen on for - // traffic destined for this upstream service. Default if empty is 127.0.0.1. + // traffic destined for this upstream service. Default if empty and local bind socket + // is not present is 127.0.0.1. LocalBindAddress *string `mapstructure:"local_bind_address"` // LocalBindPort is the ip address a side-car proxy should listen on for traffic // destined for this upstream service. Required. LocalBindPort *int `mapstructure:"local_bind_port"` + // These are exclusive with LocalBindAddress/LocalBindPort. These are created under our control. + LocalBindSocketPath *string `mapstructure:"local_bind_socket_path"` + LocalBindSocketMode *string `mapstructure:"local_bind_socket_mode"` + // Config is an opaque config that is specific to the proxy process being run. // It can be used to pass arbitrary configuration for this specific upstream // to the proxy. diff --git a/agent/config/runtime_test.go b/agent/config/runtime_test.go index dffcc4be9..bd6f2bdf1 100644 --- a/agent/config/runtime_test.go +++ b/agent/config/runtime_test.go @@ -2590,6 +2590,11 @@ func TestLoad_IntegrationWithFlags(t *testing.T) { { "destination_name": "db", "local_bind_port": 7000 + }, + { + "destination_name": "db2", + "local_bind_socket_path": "/tmp/socketpath", + "local_bind_socket_mode": "0644" } ] } @@ -2631,6 +2636,11 @@ func TestLoad_IntegrationWithFlags(t *testing.T) { destination_name = "db" local_bind_port = 7000 }, + { + destination_name = "db2", + local_bind_socket_path = "/tmp/socketpath", + local_bind_socket_mode = "0644" + } ] } } @@ -2675,6 +2685,12 @@ func TestLoad_IntegrationWithFlags(t *testing.T) { DestinationName: "db", LocalBindPort: 7000, }, + structs.Upstream{ + DestinationType: "service", + DestinationName: "db2", + LocalBindSocketPath: "/tmp/socketpath", + LocalBindSocketMode: "0644", + }, }, }, Weights: &structs.Weights{ diff --git a/agent/config/testdata/full-config.hcl b/agent/config/testdata/full-config.hcl index c1323ef22..f0c6f083f 100644 --- a/agent/config/testdata/full-config.hcl +++ b/agent/config/testdata/full-config.hcl @@ -362,6 +362,7 @@ service = { address = "cOlSOhbp" token = "msy7iWER" port = 24237 + socket_path = "/tmp/rc78ap" weights = { passing = 100, warning = 1 @@ -455,6 +456,7 @@ services = [ address = "9RhqPSPB" token = "myjKJkWH" port = 72219 + socket_path = "/foo/bar/sock_7IszXMQ1" enable_tag_override = true check = { id = "qmfeO5if" @@ -561,6 +563,7 @@ services = [ destination_service_id = "6L6BVfgH-id" local_service_address = "127.0.0.2" local_service_port = 23759 + local_service_socket_path = "/foo/bar/local" config { cedGGtZf = "pWrUNiWw" } @@ -578,6 +581,8 @@ services = [ destination_name = "KSd8HsRl" local_bind_port = 11884 local_bind_address = "127.24.88.0" + local_bind_socket_path = "/foo/bar/upstream" + local_bind_socket_mode = "0600" }, ] expose { diff --git a/agent/config/testdata/full-config.json b/agent/config/testdata/full-config.json index 2c8b26df2..6dd73a01c 100644 --- a/agent/config/testdata/full-config.json +++ b/agent/config/testdata/full-config.json @@ -358,6 +358,7 @@ "address": "cOlSOhbp", "token": "msy7iWER", "port": 24237, + "socket_path": "/tmp/rc78ap", "weights": { "passing": 100, "warning": 1 @@ -452,6 +453,7 @@ "address": "9RhqPSPB", "token": "myjKJkWH", "port": 72219, + "socket_path":"/foo/bar/sock_7IszXMQ1", "enable_tag_override": true, "check": { "id": "qmfeO5if", @@ -561,6 +563,7 @@ "destination_service_name": "6L6BVfgH", "local_service_address": "127.0.0.2", "local_service_port": 23759, + "local_service_socket_path": "/foo/bar/local", "expose": { "checks": true, "paths": [ @@ -589,7 +592,9 @@ "destination_namespace": "9nakw0td", "destination_type": "prepared_query", "local_bind_address": "127.24.88.0", - "local_bind_port": 11884 + "local_bind_port": 11884, + "local_bind_socket_path": "/foo/bar/upstream", + "local_bind_socket_mode": "0600" } ] } diff --git a/agent/proxycfg/manager_test.go b/agent/proxycfg/manager_test.go index 1c9067f50..88f15a06d 100644 --- a/agent/proxycfg/manager_test.go +++ b/agent/proxycfg/manager_test.go @@ -235,6 +235,7 @@ func TestManager_BasicLifecycle(t *testing.T) { UpstreamConfig: map[string]*structs.Upstream{ upstreams[0].Identifier(): &upstreams[0], upstreams[1].Identifier(): &upstreams[1], + upstreams[2].Identifier(): &upstreams[2], }, }, PreparedQueryEndpoints: map[string]structs.CheckServiceNodes{}, @@ -290,6 +291,7 @@ func TestManager_BasicLifecycle(t *testing.T) { UpstreamConfig: map[string]*structs.Upstream{ upstreams[0].Identifier(): &upstreams[0], upstreams[1].Identifier(): &upstreams[1], + upstreams[2].Identifier(): &upstreams[2], }, }, PreparedQueryEndpoints: map[string]structs.CheckServiceNodes{}, diff --git a/agent/proxycfg/state.go b/agent/proxycfg/state.go index a7c389074..f53f46086 100644 --- a/agent/proxycfg/state.go +++ b/agent/proxycfg/state.go @@ -1709,6 +1709,8 @@ func (s *state) handleUpdateIngressGateway(u cache.UpdateEvent, snap *ConfigSnap return nil } +// Note: Ingress gateways are always bound to ports and never unix sockets. +// This means LocalBindPort is the only possibility func makeUpstream(g *structs.GatewayService) structs.Upstream { upstream := structs.Upstream{ DestinationName: g.Service.Name, diff --git a/agent/proxycfg/testing.go b/agent/proxycfg/testing.go index 911561710..7292cb44f 100644 --- a/agent/proxycfg/testing.go +++ b/agent/proxycfg/testing.go @@ -1639,6 +1639,7 @@ func testConfigSnapshotIngressGateway( additionalEntries ...structs.ConfigEntry, ) *ConfigSnapshot { roots, leaf := TestCerts(t) + snap := &ConfigSnapshot{ Kind: structs.ServiceKindIngressGateway, Service: "ingress-gateway", diff --git a/agent/sidecar_service.go b/agent/sidecar_service.go index ac3e02c66..7486a53c6 100644 --- a/agent/sidecar_service.go +++ b/agent/sidecar_service.go @@ -98,11 +98,20 @@ func (a *Agent) sidecarServiceFromNodeService(ns *structs.NodeService, token str if sidecar.Proxy.DestinationServiceID == "" { sidecar.Proxy.DestinationServiceID = ns.ID } - if sidecar.Proxy.LocalServiceAddress == "" { - sidecar.Proxy.LocalServiceAddress = "127.0.0.1" - } - if sidecar.Proxy.LocalServicePort < 1 { - sidecar.Proxy.LocalServicePort = ns.Port + + // Fill defaults from NodeService if none of the address components are present. + // This really argues for a refactoring to a more generalized 'address' concept. + if sidecar.Proxy.LocalServiceSocketPath == "" && (sidecar.Proxy.LocalServiceAddress == "" || sidecar.Proxy.LocalServicePort < 1) { + if ns.SocketPath != "" { + sidecar.Proxy.LocalServiceSocketPath = ns.SocketPath + } else { + if sidecar.Proxy.LocalServiceAddress == "" { + sidecar.Proxy.LocalServiceAddress = "127.0.0.1" + } + if sidecar.Proxy.LocalServicePort < 1 { + sidecar.Proxy.LocalServicePort = ns.Port + } + } } // Allocate port if needed (min and max inclusive). diff --git a/agent/structs/connect_proxy_config.go b/agent/structs/connect_proxy_config.go index eff98a0d7..d55dacec1 100644 --- a/agent/structs/connect_proxy_config.go +++ b/agent/structs/connect_proxy_config.go @@ -3,6 +3,7 @@ package structs import ( "encoding/json" "fmt" + "net" "github.com/hashicorp/consul/api" "github.com/hashicorp/consul/lib" @@ -160,6 +161,10 @@ type ConnectProxyConfig struct { // (DestinationServiceID is set) but otherwise will be ignored. LocalServicePort int `json:",omitempty" alias:"local_service_port"` + // LocalServiceSocketPath is the socket of the local service instance. It is optional + // and should only be specified for "side-car" style proxies. + LocalServiceSocketPath string `json:",omitempty" alias:"local_service_socket_path"` + // Mode represents how the proxy's inbound and upstream listeners are dialed. Mode ProxyMode @@ -189,9 +194,9 @@ func (t *ConnectProxyConfig) UnmarshalJSON(data []byte) (err error) { DestinationServiceIDSnake string `json:"destination_service_id"` LocalServiceAddressSnake string `json:"local_service_address"` LocalServicePortSnake int `json:"local_service_port"` + LocalServiceSocketPathSnake string `json:"local_service_socket_path"` MeshGatewaySnake MeshGatewayConfig `json:"mesh_gateway"` TransparentProxySnake TransparentProxyConfig `json:"transparent_proxy"` - *Alias }{ Alias: (*Alias)(t), @@ -211,6 +216,9 @@ func (t *ConnectProxyConfig) UnmarshalJSON(data []byte) (err error) { if t.LocalServicePort == 0 { t.LocalServicePort = aux.LocalServicePortSnake } + if t.LocalServiceSocketPath == "" { + t.LocalServiceSocketPath = aux.LocalServiceSocketPathSnake + } if t.MeshGateway.Mode == "" { t.MeshGateway.Mode = aux.MeshGatewaySnake.Mode } @@ -245,6 +253,7 @@ func (c *ConnectProxyConfig) ToAPI() *api.AgentServiceConnectProxyConfig { DestinationServiceID: c.DestinationServiceID, LocalServiceAddress: c.LocalServiceAddress, LocalServicePort: c.LocalServicePort, + LocalServiceSocketPath: c.LocalServiceSocketPath, Mode: api.ProxyMode(c.Mode), TransparentProxy: c.TransparentProxy.ToAPI(), Config: c.Config, @@ -319,7 +328,12 @@ type Upstream struct { // LocalBindPort is the ip address a side-car proxy should listen on for traffic // destined for this upstream service. Required. - LocalBindPort int `alias:"local_bind_port"` + LocalBindPort int `json:",omitempty" alias:"local_bind_port"` + + // These are exclusive with LocalBindAddress/LocalBindPort + LocalBindSocketPath string `json:",omitempty" alias:"local_bind_socket_path"` + // This might be represented as an int, but because it's octal outputs can be a bit strange. + LocalBindSocketMode string `json:",omitempty" alias:"local_bind_socket_mode"` // Config is an opaque config that is specific to the proxy process being run. // It can be used to pass arbitrary configuration for this specific upstream @@ -349,6 +363,9 @@ func (t *Upstream) UnmarshalJSON(data []byte) (err error) { LocalBindAddressSnake string `json:"local_bind_address"` LocalBindPortSnake int `json:"local_bind_port"` + LocalBindSocketPathSnake string `json:"local_bind_socket_path"` + LocalBindSocketModeSnake string `json:"local_bind_socket_mode"` + MeshGatewaySnake MeshGatewayConfig `json:"mesh_gateway"` *Alias @@ -373,6 +390,12 @@ func (t *Upstream) UnmarshalJSON(data []byte) (err error) { if t.LocalBindPort == 0 { t.LocalBindPort = aux.LocalBindPortSnake } + if t.LocalBindSocketPath == "" { + t.LocalBindSocketPath = aux.LocalBindSocketPathSnake + } + if t.LocalBindSocketMode == "" { + t.LocalBindSocketMode = aux.LocalBindSocketModeSnake + } if t.MeshGateway.Mode == "" { t.MeshGateway.Mode = aux.MeshGatewaySnake.Mode } @@ -396,8 +419,11 @@ func (u *Upstream) Validate() error { return fmt.Errorf("upstream destination name cannot be a wildcard") } - if u.LocalBindPort == 0 && !u.CentrallyConfigured { - return fmt.Errorf("upstream local bind port cannot be zero") + if u.LocalBindPort == 0 && u.LocalBindSocketPath == "" && !u.CentrallyConfigured { + return fmt.Errorf("upstream local bind port or local socket path must be defined and nonzero") + } + if u.LocalBindPort != 0 && u.LocalBindSocketPath != "" && !u.CentrallyConfigured { + return fmt.Errorf("only one of upstream local bind port or local socket path can be defined and nonzero") } return nil @@ -415,6 +441,8 @@ func (u *Upstream) ToAPI() api.Upstream { Datacenter: u.Datacenter, LocalBindAddress: u.LocalBindAddress, LocalBindPort: u.LocalBindPort, + LocalBindSocketPath: u.LocalBindSocketPath, + LocalBindSocketMode: u.LocalBindSocketMode, Config: u.Config, MeshGateway: u.MeshGateway.ToAPI(), } @@ -435,6 +463,26 @@ func (u *Upstream) ToKey() UpstreamKey { } } +func (u Upstream) HasLocalPortOrSocket() bool { + return (u.LocalBindPort != 0 || u.LocalBindSocketPath != "") +} + +func (u Upstream) UpstreamIsUnixSocket() bool { + return (u.LocalBindPort == 0 && u.LocalBindAddress == "" && u.LocalBindSocketPath != "") +} + +func (u Upstream) UpstreamAddressToString() string { + if u.UpstreamIsUnixSocket() { + return u.LocalBindSocketPath + } + + addr := u.LocalBindAddress + if addr == "" { + addr = "127.0.0.1" + } + return net.JoinHostPort(addr, fmt.Sprintf("%d", u.LocalBindPort)) +} + type UpstreamKey struct { DestinationType string DestinationName string @@ -466,6 +514,8 @@ func UpstreamFromAPI(u api.Upstream) Upstream { Datacenter: u.Datacenter, LocalBindAddress: u.LocalBindAddress, LocalBindPort: u.LocalBindPort, + LocalBindSocketPath: u.LocalBindSocketPath, + LocalBindSocketMode: u.LocalBindSocketMode, Config: u.Config, } } diff --git a/agent/structs/service_definition.go b/agent/structs/service_definition.go index bc77ec595..96c19112c 100644 --- a/agent/structs/service_definition.go +++ b/agent/structs/service_definition.go @@ -16,6 +16,7 @@ type ServiceDefinition struct { TaggedAddresses map[string]ServiceAddress Meta map[string]string Port int + SocketPath string Check CheckType Checks CheckTypes Weights *Weights @@ -67,6 +68,7 @@ func (s *ServiceDefinition) NodeService() *NodeService { Address: s.Address, Meta: s.Meta, Port: s.Port, + SocketPath: s.SocketPath, Weights: s.Weights, EnableTagOverride: s.EnableTagOverride, EnterpriseMeta: s.EnterpriseMeta, diff --git a/agent/structs/structs.go b/agent/structs/structs.go index 7be5e5c1a..07520652f 100644 --- a/agent/structs/structs.go +++ b/agent/structs/structs.go @@ -6,7 +6,6 @@ import ( "encoding/json" "fmt" "math/rand" - "net" "reflect" "regexp" "sort" @@ -808,6 +807,8 @@ type Services map[string][]string // in the state store and are filled in on the way out by parseServiceNodes(). // This is also why PartialClone() skips them, because we know they are blank // already so it would be a waste of time to copy them. +// This is somewhat complicated when the address is really a unix domain socket; technically that +// will override the address field, but in practice the two use cases should not overlap. type ServiceNode struct { ID types.NodeID Node string @@ -824,6 +825,7 @@ type ServiceNode struct { ServiceWeights Weights ServiceMeta map[string]string ServicePort int + ServiceSocketPath string ServiceEnableTagOverride bool ServiceProxy ConnectProxyConfig ServiceConnect ServiceConnect @@ -865,6 +867,7 @@ func (s *ServiceNode) PartialClone() *ServiceNode { ServiceName: s.ServiceName, ServiceTags: tags, ServiceAddress: s.ServiceAddress, + ServiceSocketPath: s.ServiceSocketPath, ServiceTaggedAddresses: svcTaggedAddrs, ServicePort: s.ServicePort, ServiceMeta: nsmeta, @@ -890,6 +893,7 @@ func (s *ServiceNode) ToNodeService() *NodeService { Address: s.ServiceAddress, TaggedAddresses: s.ServiceTaggedAddresses, Port: s.ServicePort, + SocketPath: s.ServiceSocketPath, Meta: s.ServiceMeta, Weights: &s.ServiceWeights, EnableTagOverride: s.ServiceEnableTagOverride, @@ -996,7 +1000,8 @@ type NodeService struct { Address string TaggedAddresses map[string]ServiceAddress `json:",omitempty"` Meta map[string]string - Port int + Port int `json:",omitempty"` + SocketPath string `json:",omitempty"` // TODO This might be integrated into Address somehow, but not sure about the ergonomics. Only one of (address,port) or socketpath can be defined. Weights *Weights EnableTagOverride bool @@ -1159,9 +1164,9 @@ func (s *NodeService) Validate() error { "services")) } - if s.Port == 0 { + if s.Port == 0 && s.SocketPath == "" { result = multierror.Append(result, fmt.Errorf( - "Port must be set for a Connect proxy")) + "Port or SocketPath must be set for a Connect proxy")) } if s.Connect.Native { @@ -1188,17 +1193,13 @@ func (s *NodeService) Validate() error { } upstreamKeys[uk] = struct{}{} - addr := u.LocalBindAddress - if addr == "" { - addr = "127.0.0.1" - } - addr = net.JoinHostPort(addr, fmt.Sprintf("%d", u.LocalBindPort)) + addr := u.UpstreamAddressToString() // Centrally configured upstreams will fail this check if there are multiple because they do not have an address/port. // Only consider non-centrally configured upstreams in this check since those are the ones we create listeners for. if _, ok := bindAddrs[addr]; ok && !u.CentrallyConfigured { result = multierror.Append(result, fmt.Errorf( - "upstreams cannot contain duplicates by local bind address and port; %q is specified twice", addr)) + "upstreams cannot contain duplicates by local bind address and port or unix path; %q is specified twice", addr)) continue } bindAddrs[addr] = struct{}{} @@ -1266,6 +1267,10 @@ func (s *NodeService) Validate() error { result = multierror.Append(result, fmt.Errorf("The Proxy.LocalServicePort configuration is invalid for a %s", s.Kind)) } + if s.Proxy.LocalServiceSocketPath != "" { + result = multierror.Append(result, fmt.Errorf("The Proxy.LocalServiceSocketPath configuration is invalid for a %s", s.Kind)) + } + if len(s.Proxy.Upstreams) != 0 { result = multierror.Append(result, fmt.Errorf("The Proxy.Upstreams configuration is invalid for a %s", s.Kind)) } @@ -1299,6 +1304,7 @@ func (s *NodeService) IsSame(other *NodeService) bool { !reflect.DeepEqual(s.Tags, other.Tags) || s.Address != other.Address || s.Port != other.Port || + s.SocketPath != other.SocketPath || !reflect.DeepEqual(s.TaggedAddresses, other.TaggedAddresses) || !reflect.DeepEqual(s.Weights, other.Weights) || !reflect.DeepEqual(s.Meta, other.Meta) || @@ -1370,6 +1376,7 @@ func (s *NodeService) ToServiceNode(node string) *ServiceNode { ServiceAddress: s.Address, ServiceTaggedAddresses: s.TaggedAddresses, ServicePort: s.Port, + ServiceSocketPath: s.SocketPath, ServiceMeta: s.Meta, ServiceWeights: theWeights, ServiceEnableTagOverride: s.EnableTagOverride, diff --git a/agent/structs/structs_filtering_test.go b/agent/structs/structs_filtering_test.go index 863d362eb..e8ddd9e56 100644 --- a/agent/structs/structs_filtering_test.go +++ b/agent/structs/structs_filtering_test.go @@ -176,6 +176,16 @@ var expectedFieldConfigUpstreams bexpr.FieldConfigurations = bexpr.FieldConfigur CoerceFn: bexpr.CoerceInt, SupportedOperations: []bexpr.MatchOperator{bexpr.MatchEqual, bexpr.MatchNotEqual}, }, + "LocalBindSocketPath": &bexpr.FieldConfiguration{ + StructFieldName: "LocalBindSocketPath", + CoerceFn: bexpr.CoerceString, + SupportedOperations: []bexpr.MatchOperator{bexpr.MatchEqual, bexpr.MatchNotEqual, bexpr.MatchIn, bexpr.MatchNotIn, bexpr.MatchMatches, bexpr.MatchNotMatches}, + }, + "LocalBindSocketMode": &bexpr.FieldConfiguration{ + StructFieldName: "LocalBindSocketMode", + CoerceFn: bexpr.CoerceInt, + SupportedOperations: []bexpr.MatchOperator{bexpr.MatchEqual, bexpr.MatchNotEqual}, + }, "MeshGateway": &bexpr.FieldConfiguration{ StructFieldName: "MeshGateway", SubFields: expectedFieldConfigMeshGatewayConfig, diff --git a/agent/structs/structs_test.go b/agent/structs/structs_test.go index e340db2c7..8ee3d2a4b 100644 --- a/agent/structs/structs_test.go +++ b/agent/structs/structs_test.go @@ -734,7 +734,19 @@ func TestStructs_NodeService_ValidateConnectProxy(t *testing.T) { LocalBindPort: 0, }} }, - "upstream local bind port cannot be zero", + "upstream local bind port or local socket path must be defined and nonzero", + }, + { + "connect-proxy: upstream bind port and path defined", + func(x *NodeService) { + x.Proxy.Upstreams = Upstreams{{ + DestinationType: UpstreamDestTypeService, + DestinationName: "foo", + LocalBindPort: 1, + LocalBindSocketPath: "/tmp/socket", + }} + }, + "only one of upstream local bind port or local socket path can be defined and nonzero", }, { "connect-proxy: Upstreams almost-but-not-quite-duplicated in various ways", diff --git a/agent/structs/testing_connect_proxy_config.go b/agent/structs/testing_connect_proxy_config.go index 700154b56..694c5e663 100644 --- a/agent/structs/testing_connect_proxy_config.go +++ b/agent/structs/testing_connect_proxy_config.go @@ -32,6 +32,11 @@ func TestUpstreams(t testing.T) Upstreams { LocalBindPort: 8181, LocalBindAddress: "127.10.10.10", }, + { + DestinationName: "upstream_socket", + LocalBindSocketPath: "/tmp/upstream.sock", + LocalBindSocketMode: "0700", + }, } } diff --git a/agent/xds/clusters.go b/agent/xds/clusters.go index fd438714b..8ebdcc434 100644 --- a/agent/xds/clusters.go +++ b/agent/xds/clusters.go @@ -400,10 +400,17 @@ func (s *ResourceGenerator) makeAppCluster(cfgSnap *proxycfg.ConfigSnapshot, nam return makeClusterFromUserConfig(cfg.LocalClusterJSON) } - addr := cfgSnap.Proxy.LocalServiceAddress - if addr == "" { - addr = "127.0.0.1" + var endpoint *envoy_endpoint_v3.LbEndpoint + if cfgSnap.Proxy.LocalServiceSocketPath != "" { + endpoint = makePipeEndpoint(cfgSnap.Proxy.LocalServiceSocketPath) + } else { + addr := cfgSnap.Proxy.LocalServiceAddress + if addr == "" { + addr = "127.0.0.1" + } + endpoint = makeEndpoint(addr, port) } + c = &envoy_cluster_v3.Cluster{ Name: name, ConnectTimeout: ptypes.DurationProto(time.Duration(cfg.LocalConnectTimeoutMs) * time.Millisecond), @@ -413,7 +420,7 @@ func (s *ResourceGenerator) makeAppCluster(cfgSnap *proxycfg.ConfigSnapshot, nam Endpoints: []*envoy_endpoint_v3.LocalityLbEndpoints{ { LbEndpoints: []*envoy_endpoint_v3.LbEndpoint{ - makeEndpoint(addr, port), + endpoint, }, }, }, diff --git a/agent/xds/clusters_test.go b/agent/xds/clusters_test.go index 1e7f4ddaa..c9e7ee2a5 100644 --- a/agent/xds/clusters_test.go +++ b/agent/xds/clusters_test.go @@ -227,6 +227,17 @@ func TestClustersFromSnapshot(t *testing.T) { name: "expose-paths-local-app-paths", create: proxycfg.TestConfigSnapshotExposeConfig, }, + { + name: "downstream-service-with-unix-sockets", + create: proxycfg.TestConfigSnapshot, + setup: func(snap *proxycfg.ConfigSnapshot) { + snap.Address = "" + snap.Port = 0 + snap.Proxy.LocalServiceAddress = "" + snap.Proxy.LocalServicePort = 0 + snap.Proxy.LocalServiceSocketPath = "/tmp/downstream_proxy.sock" + }, + }, { name: "expose-paths-new-cluster-http2", create: proxycfg.TestConfigSnapshotExposeConfig, diff --git a/agent/xds/endpoints.go b/agent/xds/endpoints.go index bf9d125ac..4487cdf5b 100644 --- a/agent/xds/endpoints.go +++ b/agent/xds/endpoints.go @@ -287,6 +287,7 @@ func (s *ResourceGenerator) endpointsFromSnapshotIngressGateway(cfgSnap *proxycf return resources, nil } +// used in clusters.go func makeEndpoint(host string, port int) *envoy_endpoint_v3.LbEndpoint { return &envoy_endpoint_v3.LbEndpoint{ HostIdentifier: &envoy_endpoint_v3.LbEndpoint_Endpoint{ @@ -297,6 +298,16 @@ func makeEndpoint(host string, port int) *envoy_endpoint_v3.LbEndpoint { } } +func makePipeEndpoint(path string) *envoy_endpoint_v3.LbEndpoint { + return &envoy_endpoint_v3.LbEndpoint{ + HostIdentifier: &envoy_endpoint_v3.LbEndpoint_Endpoint{ + Endpoint: &envoy_endpoint_v3.Endpoint{ + Address: makePipeAddress(path, 0), + }, + }, + } +} + func (s *ResourceGenerator) endpointsFromDiscoveryChain( id string, chain *structs.CompiledDiscoveryChain, diff --git a/agent/xds/listeners.go b/agent/xds/listeners.go index 1a2a76ee8..17dc11a4d 100644 --- a/agent/xds/listeners.go +++ b/agent/xds/listeners.go @@ -57,7 +57,6 @@ func (s *ResourceGenerator) listenersFromSnapshot(cfgSnap *proxycfg.ConfigSnapsh // listenersFromSnapshotConnectProxy returns the "listeners" for a connect proxy service func (s *ResourceGenerator) listenersFromSnapshotConnectProxy(cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) { resources := make([]proto.Message, 1) - var err error // Configure inbound listener. @@ -77,7 +76,7 @@ func (s *ResourceGenerator) listenersFromSnapshotConnectProxy(cfgSnap *proxycfg. port = cfgSnap.Proxy.TransparentProxy.OutboundListenerPort } - outboundListener = makeListener(OutboundListenerName, "127.0.0.1", port, envoy_core_v3.TrafficDirection_OUTBOUND) + outboundListener = makePortListener(OutboundListenerName, "127.0.0.1", port, envoy_core_v3.TrafficDirection_OUTBOUND) outboundListener.FilterChains = make([]*envoy_listener_v3.FilterChain, 0) outboundListener.ListenerFilters = []*envoy_listener_v3.ListenerFilter{ { @@ -103,13 +102,8 @@ func (s *ResourceGenerator) listenersFromSnapshotConnectProxy(cfgSnap *proxycfg. continue } - // Generate the upstream listeners for when they are explicitly set with a local bind port - if outboundListener == nil || (upstreamCfg != nil && upstreamCfg.LocalBindPort != 0) { - address := "127.0.0.1" - if upstreamCfg.LocalBindAddress != "" { - address = upstreamCfg.LocalBindAddress - } - + // Generate the upstream listeners for when they are explicitly set with a local bind port or socket path + if outboundListener == nil || (upstreamCfg != nil && upstreamCfg.HasLocalPortOrSocket()) { filterChain, err := s.makeUpstreamFilterChainForDiscoveryChain( id, "", @@ -123,7 +117,7 @@ func (s *ResourceGenerator) listenersFromSnapshotConnectProxy(cfgSnap *proxycfg. return nil, err } - upstreamListener := makeListener(id, address, upstreamCfg.LocalBindPort, envoy_core_v3.TrafficDirection_OUTBOUND) + upstreamListener := makeListener(id, upstreamCfg, envoy_core_v3.TrafficDirection_OUTBOUND) upstreamListener.FilterChains = []*envoy_listener_v3.FilterChain{ filterChain, } @@ -243,12 +237,7 @@ func (s *ResourceGenerator) listenersFromSnapshotConnectProxy(cfgSnap *proxycfg. // default config if there is an error so it's safe to continue. s.Logger.Warn("failed to parse", "upstream", u.Identifier(), "error", err) } - address := "127.0.0.1" - if u.LocalBindAddress != "" { - address = u.LocalBindAddress - } - - upstreamListener := makeListener(id, address, u.LocalBindPort, envoy_core_v3.TrafficDirection_OUTBOUND) + upstreamListener := makeListener(id, u, envoy_core_v3.TrafficDirection_OUTBOUND) filterChain, err := s.makeUpstreamFilterChainForDiscoveryChain( id, @@ -499,7 +488,7 @@ func (s *ResourceGenerator) makeIngressGatewayListeners(address string, cfgSnap resources = append(resources, upstreamListener) } else { // If multiple upstreams share this port, make a special listener for the protocol. - listener := makeListener(listenerKey.Protocol, address, listenerKey.Port, envoy_core_v3.TrafficDirection_OUTBOUND) + listener := makePortListener(listenerKey.Protocol, address, listenerKey.Port, envoy_core_v3.TrafficDirection_OUTBOUND) opts := listenerFilterOpts{ useRDS: true, protocol: listenerKey.Protocol, @@ -546,7 +535,15 @@ func (s *ResourceGenerator) makeIngressGatewayListeners(address string, cfgSnap // changes them, we actually create a whole new listener on the new address and // port. Envoy should take care of closing the old one once it sees it's no // longer in the config. -func makeListener(name, addr string, port int, trafficDirection envoy_core_v3.TrafficDirection) *envoy_listener_v3.Listener { +func makeListener(name string, upstream *structs.Upstream, trafficDirection envoy_core_v3.TrafficDirection) *envoy_listener_v3.Listener { + if upstream.LocalBindPort == 0 && upstream.LocalBindSocketPath != "" { + return makePipeListener(name, upstream.LocalBindSocketPath, upstream.LocalBindSocketMode, trafficDirection) + } + + return makePortListenerWithDefault(name, upstream.LocalBindAddress, upstream.LocalBindPort, trafficDirection) +} + +func makePortListener(name, addr string, port int, trafficDirection envoy_core_v3.TrafficDirection) *envoy_listener_v3.Listener { return &envoy_listener_v3.Listener{ Name: fmt.Sprintf("%s:%s:%d", name, addr, port), Address: makeAddress(addr, port), @@ -554,6 +551,26 @@ func makeListener(name, addr string, port int, trafficDirection envoy_core_v3.Tr } } +func makePortListenerWithDefault(name, addr string, port int, trafficDirection envoy_core_v3.TrafficDirection) *envoy_listener_v3.Listener { + if addr == "" { + addr = "127.0.0.1" + } + return makePortListener(name, addr, port, trafficDirection) +} + +func makePipeListener(name, path string, mode_str string, trafficDirection envoy_core_v3.TrafficDirection) *envoy_listener_v3.Listener { + // We've already validated this, so it should not fail. + mode, err := strconv.ParseUint(mode_str, 0, 32) + if err != nil { + mode = 0 + } + return &envoy_listener_v3.Listener{ + Name: fmt.Sprintf("%s:%s", name, path), + Address: makePipeAddress(path, uint32(mode)), + TrafficDirection: trafficDirection, + } +} + // makeListenerFromUserConfig returns the listener config decoded from an // arbitrary proto3 json format string or an error if it's invalid. // @@ -755,7 +772,7 @@ func (s *ResourceGenerator) makeInboundListener(cfgSnap *proxycfg.ConfigSnapshot port = cfg.BindPort } - l = makeListener(name, addr, port, envoy_core_v3.TrafficDirection_INBOUND) + l = makePortListener(name, addr, port, envoy_core_v3.TrafficDirection_INBOUND) filterOpts := listenerFilterOpts{ protocol: cfg.Protocol, @@ -833,7 +850,7 @@ func (s *ResourceGenerator) makeExposedCheckListener(cfgSnap *proxycfg.ConfigSna strippedPath := r.ReplaceAllString(path.Path, "") listenerName := fmt.Sprintf("exposed_path_%s", strippedPath) - l := makeListener(listenerName, addr, path.ListenerPort, envoy_core_v3.TrafficDirection_INBOUND) + l := makePortListener(listenerName, addr, path.ListenerPort, envoy_core_v3.TrafficDirection_INBOUND) filterName := fmt.Sprintf("exposed_path_filter_%s_%d", strippedPath, path.ListenerPort) @@ -898,7 +915,7 @@ func (s *ResourceGenerator) makeTerminatingGatewayListener( name, addr string, port int, ) (*envoy_listener_v3.Listener, error) { - l := makeListener(name, addr, port, envoy_core_v3.TrafficDirection_INBOUND) + l := makePortListener(name, addr, port, envoy_core_v3.TrafficDirection_INBOUND) tlsInspector, err := makeTLSInspectorListenerFilter() if err != nil { @@ -1088,7 +1105,7 @@ func (s *ResourceGenerator) makeMeshGatewayListener(name, addr string, port int, }, } - l := makeListener(name, addr, port, envoy_core_v3.TrafficDirection_UNSPECIFIED) + l := makePortListener(name, addr, port, envoy_core_v3.TrafficDirection_UNSPECIFIED) l.ListenerFilters = []*envoy_listener_v3.ListenerFilter{tlsInspector} // TODO (mesh-gateway) - Do we need to create clusters for all the old trust domains as well? @@ -1275,12 +1292,15 @@ func (s *ResourceGenerator) makeUpstreamListenerForDiscoveryChain( cfgSnap *proxycfg.ConfigSnapshot, tlsContext *envoy_tls_v3.DownstreamTlsContext, ) (proto.Message, error) { - if address == "" { - address = "127.0.0.1" - } - upstreamID := u.Identifier() - l := makeListener(upstreamID, address, u.LocalBindPort, envoy_core_v3.TrafficDirection_OUTBOUND) + // Best understanding is this only makes sense for port listeners.... + if u.LocalBindSocketPath != "" { + return nil, fmt.Errorf("makeUpstreamListenerForDiscoveryChain not supported for unix domain sockets %s %+v", + address, u) + } + + upstreamID := u.Identifier() + l := makePortListenerWithDefault(upstreamID, address, u.LocalBindPort, envoy_core_v3.TrafficDirection_OUTBOUND) cfg := s.getAndModifyUpstreamConfigForListener(upstreamID, u, chain) if cfg.EnvoyListenerJSON != "" { return makeListenerFromUserConfig(cfg.EnvoyListenerJSON) diff --git a/agent/xds/listeners_test.go b/agent/xds/listeners_test.go index 2cf9a6783..c3404a59c 100644 --- a/agent/xds/listeners_test.go +++ b/agent/xds/listeners_test.go @@ -65,6 +65,16 @@ func TestListenersFromSnapshot(t *testing.T) { snap.Proxy.Config["bind_port"] = 8888 }, }, + { + name: "listener-unix-domain-socket", + create: proxycfg.TestConfigSnapshot, + setup: func(snap *proxycfg.ConfigSnapshot) { + snap.Proxy.Upstreams[0].LocalBindAddress = "" + snap.Proxy.Upstreams[0].LocalBindPort = 0 + snap.Proxy.Upstreams[0].LocalBindSocketPath = "/tmp/service-mesh/client-1/grpc-employee-server" + snap.Proxy.Upstreams[0].LocalBindSocketMode = "0640" + }, + }, { name: "http-public-listener", create: proxycfg.TestConfigSnapshot, diff --git a/agent/xds/response.go b/agent/xds/response.go index 1c9f7fba8..716666d19 100644 --- a/agent/xds/response.go +++ b/agent/xds/response.go @@ -38,6 +38,17 @@ func createResponse(typeURL string, version, nonce string, resources []proto.Mes return resp, nil } +func makePipeAddress(path string, mode uint32) *envoy_core_v3.Address { + return &envoy_core_v3.Address{ + Address: &envoy_core_v3.Address_Pipe{ + Pipe: &envoy_core_v3.Pipe{ + Path: path, + Mode: mode, + }, + }, + } +} + func makeAddress(ip string, port int) *envoy_core_v3.Address { return &envoy_core_v3.Address{ Address: &envoy_core_v3.Address_SocketAddress{ diff --git a/agent/xds/testdata/clusters/downstream-service-with-unix-sockets.envoy-1-18-x.golden b/agent/xds/testdata/clusters/downstream-service-with-unix-sockets.envoy-1-18-x.golden new file mode 100644 index 000000000..0440b9f07 --- /dev/null +++ b/agent/xds/testdata/clusters/downstream-service-with-unix-sockets.envoy-1-18-x.golden @@ -0,0 +1,131 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "altStatName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": { + + }, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": { + + }, + "outlierDetection": { + + }, + "commonLbConfig": { + "healthyPanicThreshold": { + + } + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "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" + } + } + }, + "sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": { + + }, + "resourceApiVersion": "V3" + } + }, + "connectTimeout": "5s", + "circuitBreakers": { + + }, + "outlierDetection": { + + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext", + "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" + } + } + }, + "sni": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + }, + { + "@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "name": "local_app", + "type": "STATIC", + "connectTimeout": "5s", + "loadAssignment": { + "clusterName": "local_app", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "pipe": { + "path": "/tmp/downstream_proxy.sock" + } + } + } + } + ] + } + ] + } + } + ], + "typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/clusters/downstream-service-with-unix-sockets.v2compat.envoy-1-16-x.golden b/agent/xds/testdata/clusters/downstream-service-with-unix-sockets.v2compat.envoy-1-16-x.golden new file mode 100644 index 000000000..a8466b90c --- /dev/null +++ b/agent/xds/testdata/clusters/downstream-service-with-unix-sockets.v2compat.envoy-1-16-x.golden @@ -0,0 +1,131 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.api.v2.Cluster", + "name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "altStatName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": { + + }, + "resourceApiVersion": "V2" + } + }, + "connectTimeout": "5s", + "circuitBreakers": { + + }, + "outlierDetection": { + + }, + "commonLbConfig": { + "healthyPanicThreshold": { + + } + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.api.v2.auth.UpstreamTlsContext", + "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" + } + } + }, + "sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Cluster", + "name": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "type": "EDS", + "edsClusterConfig": { + "edsConfig": { + "ads": { + + }, + "resourceApiVersion": "V2" + } + }, + "connectTimeout": "5s", + "circuitBreakers": { + + }, + "outlierDetection": { + + }, + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.api.v2.auth.UpstreamTlsContext", + "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" + } + } + }, + "sni": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + }, + { + "@type": "type.googleapis.com/envoy.api.v2.Cluster", + "name": "local_app", + "type": "STATIC", + "connectTimeout": "5s", + "loadAssignment": { + "clusterName": "local_app", + "endpoints": [ + { + "lbEndpoints": [ + { + "endpoint": { + "address": { + "pipe": { + "path": "/tmp/downstream_proxy.sock" + } + } + } + } + ] + } + ] + } + } + ], + "typeUrl": "type.googleapis.com/envoy.api.v2.Cluster", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/listeners/listener-unix-domain-socket.envoy-1-18-x.golden b/agent/xds/testdata/listeners/listener-unix-domain-socket.envoy-1-18-x.golden new file mode 100644 index 000000000..60a237e1a --- /dev/null +++ b/agent/xds/testdata/listeners/listener-unix-domain-socket.envoy-1-18-x.golden @@ -0,0 +1,119 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "db:/tmp/service-mesh/client-1/grpc-employee-server", + "address": { + "pipe": { + "path": "/tmp/service-mesh/client-1/grpc-employee-server", + "mode": 416 + } + }, + "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.dc1", + "cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "prepared_query:geo-cache:127.10.10.10:8181", + "address": { + "socketAddress": { + "address": "127.10.10.10", + "portValue": 8181 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.prepared_query_geo-cache", + "cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "public_listener:0.0.0.0:9999", + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 9999 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.rbac", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC", + "rules": { + + }, + "statPrefix": "connect_authz" + } + }, + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "public_listener", + "cluster": "local_app" + } + } + ], + "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": true + } + } + } + ], + "trafficDirection": "INBOUND" + } + ], + "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/listeners/listener-unix-domain-socket.v2compat.envoy-1-16-x.golden b/agent/xds/testdata/listeners/listener-unix-domain-socket.v2compat.envoy-1-16-x.golden new file mode 100644 index 000000000..c64b9d280 --- /dev/null +++ b/agent/xds/testdata/listeners/listener-unix-domain-socket.v2compat.envoy-1-16-x.golden @@ -0,0 +1,119 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.api.v2.Listener", + "name": "db:/tmp/service-mesh/client-1/grpc-employee-server", + "address": { + "pipe": { + "path": "/tmp/service-mesh/client-1/grpc-employee-server", + "mode": 416 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy", + "statPrefix": "upstream.db.default.dc1", + "cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@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.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy", + "statPrefix": "upstream.prepared_query_geo-cache", + "cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul" + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@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": [ + { + "filters": [ + { + "name": "envoy.filters.network.rbac", + "typedConfig": { + "@type": "type.googleapis.com/envoy.config.filter.network.rbac.v2.RBAC", + "rules": { + + }, + "statPrefix": "connect_authz" + } + }, + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy", + "statPrefix": "public_listener", + "cluster": "local_app" + } + } + ], + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.api.v2.auth.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": true + } + } + } + ], + "trafficDirection": "INBOUND" + } + ], + "typeUrl": "type.googleapis.com/envoy.api.v2.Listener", + "nonce": "00000001" +} \ No newline at end of file diff --git a/api/agent.go b/api/agent.go index c21ef2016..8dfdf593f 100644 --- a/api/agent.go +++ b/api/agent.go @@ -118,6 +118,7 @@ type AgentServiceConnectProxyConfig struct { DestinationServiceID string `json:",omitempty"` LocalServiceAddress string `json:",omitempty"` LocalServicePort int `json:",omitempty"` + LocalServiceSocketPath string `json:",omitempty"` Mode ProxyMode `json:",omitempty"` TransparentProxy *TransparentProxyConfig `json:",omitempty"` Config map[string]interface{} `json:",omitempty" bexpr:"-"` @@ -407,6 +408,8 @@ type Upstream struct { Datacenter string `json:",omitempty"` LocalBindAddress string `json:",omitempty"` LocalBindPort int `json:",omitempty"` + LocalBindSocketPath string `json:",omitempty"` + LocalBindSocketMode string `json:",omitempty"` Config map[string]interface{} `json:",omitempty" bexpr:"-"` MeshGateway MeshGatewayConfig `json:",omitempty"` CentrallyConfigured bool `json:",omitempty" bexpr:"-"` diff --git a/command/connect/proxy/flag_upstreams.go b/command/connect/proxy/flag_upstreams.go index 20e2b0121..1cc51bda6 100644 --- a/command/connect/proxy/flag_upstreams.go +++ b/command/connect/proxy/flag_upstreams.go @@ -15,6 +15,10 @@ import ( // // The syntax of the value is "name:addr" where addr can be "port" or // "host:port". Examples: "db:8181", "db:127.0.0.10:8282", etc. +// +// This would need to be extended for Unix Domain Sockets; how does this get handled +// addr: path & path:mode is ambigous; maybe path alone could be sorted by checking for a numeric port +// but iffy type FlagUpstreams map[string]proxy.UpstreamConfig func (f *FlagUpstreams) String() string { diff --git a/command/connect/proxy/proxy.go b/command/connect/proxy/proxy.go index 028d39c93..d2d0b90cf 100644 --- a/command/connect/proxy/proxy.go +++ b/command/connect/proxy/proxy.go @@ -315,9 +315,16 @@ func (c *cmd) configWatcher(client *api.Client) (proxyImpl.ConfigWatcher, error) for _, k := range upstreamKeys { config := c.upstreams[k] + addr := config.LocalBindSocketPath + if addr == "" { + addr = fmt.Sprintf( + "%s:%d", + config.LocalBindAddress, config.LocalBindPort) + } + c.UI.Info(fmt.Sprintf( - " Upstream: %s => %s:%d", - k, config.LocalBindAddress, config.LocalBindPort)) + " Upstream: %s => %s", + k, addr)) upstreams = append(upstreams, config) } diff --git a/connect/proxy/config.go b/connect/proxy/config.go index b49ab9ff8..f4016f0ea 100644 --- a/connect/proxy/config.go +++ b/connect/proxy/config.go @@ -106,7 +106,7 @@ func (uc *UpstreamConfig) applyDefaults() { if uc.DestinationNamespace == "" { uc.DestinationNamespace = "default" } - if uc.LocalBindAddress == "" { + if uc.LocalBindAddress == "" && uc.LocalBindSocketPath == "" { uc.LocalBindAddress = "127.0.0.1" } } @@ -114,7 +114,13 @@ func (uc *UpstreamConfig) applyDefaults() { // String returns a string that uniquely identifies the Upstream. Used for // identifying the upstream in log output and map keys. func (uc *UpstreamConfig) String() string { - return fmt.Sprintf("%s:%d->%s:%s/%s", uc.LocalBindAddress, uc.LocalBindPort, + addr := uc.LocalBindSocketPath + if addr == "" { + addr = fmt.Sprintf( + "%s:%d", + uc.LocalBindAddress, uc.LocalBindPort) + } + return fmt.Sprintf("%s->%s:%s/%s", addr, uc.DestinationType, uc.DestinationNamespace, uc.DestinationName) } @@ -242,6 +248,9 @@ func (w *AgentConfigWatcher) handler(blockVal watch.BlockingParamVal, } cfg.PublicListener.BindAddress = resp.Address cfg.PublicListener.BindPort = resp.Port + if resp.Proxy.LocalServiceSocketPath != "" { + w.logger.Error("Unhandled unix domain socket config %+v %+v", resp.Proxy, cfg.PublicListener) + } cfg.PublicListener.LocalServiceAddress = ipaddr.FormatAddressPort( resp.Proxy.LocalServiceAddress, resp.Proxy.LocalServicePort) diff --git a/connect/proxy/proxy.go b/connect/proxy/proxy.go index a29cf352e..22c9ff83d 100644 --- a/connect/proxy/proxy.go +++ b/connect/proxy/proxy.go @@ -103,8 +103,8 @@ func (p *Proxy) Serve() error { for _, uc := range newCfg.Upstreams { uc.applyDefaults() - if uc.LocalBindPort < 1 { - p.logger.Error("upstream has no local_bind_port. "+ + if uc.LocalBindPort < 1 || uc.LocalBindSocketPath == "" { + p.logger.Error("upstream has no local_bind_port or local_bind_socket_path. "+ "Can't start upstream.", "upstream", uc.String()) continue } diff --git a/proto/pbservice/service.gen.go b/proto/pbservice/service.gen.go index 3cc0de150..942f64ede 100644 --- a/proto/pbservice/service.gen.go +++ b/proto/pbservice/service.gen.go @@ -10,6 +10,7 @@ func ConnectProxyConfigToStructs(s ConnectProxyConfig) structs.ConnectProxyConfi t.DestinationServiceID = s.DestinationServiceID t.LocalServiceAddress = s.LocalServiceAddress t.LocalServicePort = int(s.LocalServicePort) + t.LocalServiceSocketPath = s.LocalServiceSocketPath t.Mode = s.Mode t.Config = ProtobufTypesStructToMapStringInterface(s.Config) t.Upstreams = UpstreamsToStructs(s.Upstreams) @@ -24,6 +25,7 @@ func NewConnectProxyConfigFromStructs(t structs.ConnectProxyConfig) ConnectProxy s.DestinationServiceID = t.DestinationServiceID s.LocalServiceAddress = t.LocalServiceAddress s.LocalServicePort = int32(t.LocalServicePort) + s.LocalServiceSocketPath = t.LocalServiceSocketPath s.Mode = t.Mode s.Config = MapStringInterfaceToProtobufTypesStruct(t.Config) s.Upstreams = NewUpstreamsFromStructs(t.Upstreams) @@ -142,6 +144,8 @@ func UpstreamToStructs(s Upstream) structs.Upstream { t.Datacenter = s.Datacenter t.LocalBindAddress = s.LocalBindAddress t.LocalBindPort = int(s.LocalBindPort) + t.LocalBindSocketPath = s.LocalBindSocketPath + t.LocalBindSocketMode = s.LocalBindSocketMode t.Config = ProtobufTypesStructToMapStringInterface(s.Config) t.MeshGateway = MeshGatewayConfigToStructs(s.MeshGateway) t.CentrallyConfigured = s.CentrallyConfigured @@ -155,6 +159,8 @@ func NewUpstreamFromStructs(t structs.Upstream) Upstream { s.Datacenter = t.Datacenter s.LocalBindAddress = t.LocalBindAddress s.LocalBindPort = int32(t.LocalBindPort) + s.LocalBindSocketPath = t.LocalBindSocketPath + s.LocalBindSocketMode = t.LocalBindSocketMode s.Config = MapStringInterfaceToProtobufTypesStruct(t.Config) s.MeshGateway = NewMeshGatewayConfigFromStructs(t.MeshGateway) s.CentrallyConfigured = t.CentrallyConfigured diff --git a/proto/pbservice/service.pb.go b/proto/pbservice/service.pb.go index 1f0611b7b..7a0cb6ba0 100644 --- a/proto/pbservice/service.pb.go +++ b/proto/pbservice/service.pb.go @@ -76,6 +76,8 @@ type ConnectProxyConfig struct { // TransparentProxy defines configuration for when the proxy is in // transparent mode. TransparentProxy TransparentProxyConfig `protobuf:"bytes,10,opt,name=TransparentProxy,proto3" json:"TransparentProxy"` + // LocalServiceSocketPath is the path to the unix domain socket for the local service instance + LocalServiceSocketPath string `protobuf:"bytes,11,opt,name=LocalServiceSocketPath,proto3" json:"LocalServiceSocketPath,omitempty"` } func (m *ConnectProxyConfig) Reset() { *m = ConnectProxyConfig{} } @@ -154,6 +156,9 @@ type Upstream struct { // CentrallyConfigured indicates whether the upstream was defined in a proxy // instance registration or whether it was generated from a config entry. CentrallyConfigured bool `protobuf:"varint,9,opt,name=CentrallyConfigured,proto3" json:"CentrallyConfigured,omitempty"` + // LocalBindSocketPath is the socket to create to connect to the upstream service + LocalBindSocketPath string `protobuf:"bytes,10,opt,name=LocalBindSocketPath,proto3" json:"LocalBindSocketPath,omitempty"` + LocalBindSocketMode string `protobuf:"bytes,11,opt,name=LocalBindSocketMode,proto3" json:"LocalBindSocketMode,omitempty"` } func (m *Upstream) Reset() { *m = Upstream{} } @@ -450,8 +455,10 @@ type ServiceDefinition struct { TaggedAddresses map[string]ServiceAddress `protobuf:"bytes,16,rep,name=TaggedAddresses,proto3" json:"TaggedAddresses" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` Meta map[string]string `protobuf:"bytes,6,rep,name=Meta,proto3" json:"Meta,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // mog: func-to=int func-from=int32 - Port int32 `protobuf:"varint,7,opt,name=Port,proto3" json:"Port,omitempty"` - Check CheckType `protobuf:"bytes,8,opt,name=Check,proto3" json:"Check"` + Port int32 `protobuf:"varint,7,opt,name=Port,proto3" json:"Port,omitempty"` + // Path for socket + SocketPath string `protobuf:"bytes,18,opt,name=SocketPath,proto3" json:"SocketPath,omitempty"` + Check CheckType `protobuf:"bytes,8,opt,name=Check,proto3" json:"Check"` // mog: func-to=CheckTypesToStructs func-from=NewCheckTypesFromStructs Checks []*CheckType `protobuf:"bytes,9,rep,name=Checks,proto3" json:"Checks,omitempty"` // mog: func-to=WeightsPtrToStructs func-from=NewWeightsPtrFromStructs @@ -608,78 +615,81 @@ func init() { func init() { proto.RegisterFile("proto/pbservice/service.proto", fileDescriptor_cbb99233b75fb80b) } var fileDescriptor_cbb99233b75fb80b = []byte{ - // 1135 bytes of a gzipped FileDescriptorProto + // 1179 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0x4f, 0x6f, 0x1b, 0x45, - 0x14, 0xf7, 0xfa, 0x4f, 0x6c, 0xbf, 0x94, 0x34, 0x99, 0x9a, 0xb0, 0x84, 0xd6, 0x49, 0x2d, 0x84, - 0x22, 0x88, 0xec, 0x34, 0x51, 0x09, 0xad, 0x54, 0x24, 0x12, 0x07, 0x54, 0x35, 0x69, 0xcd, 0xc6, - 0xa8, 0x02, 0x89, 0xc3, 0x78, 0x3d, 0x59, 0xaf, 0x62, 0xcf, 0x58, 0x3b, 0xe3, 0xd0, 0x1c, 0xf9, - 0x06, 0x1c, 0xf9, 0x18, 0x48, 0x7c, 0x89, 0x1c, 0x7b, 0xe4, 0x14, 0x41, 0x72, 0xe6, 0x0b, 0xe4, - 0x84, 0xe6, 0xed, 0xec, 0x66, 0xbd, 0x5e, 0xa2, 0xc2, 0xc9, 0x33, 0xef, 0xf7, 0xde, 0x6f, 0x9e, - 0xdf, 0xfb, 0xbd, 0x99, 0x85, 0x07, 0xe3, 0x40, 0x28, 0xd1, 0x1a, 0xf7, 0x24, 0x0b, 0x4e, 0x7d, - 0x97, 0xb5, 0xcc, 0x6f, 0x13, 0xed, 0xa4, 0x1a, 0x03, 0x2b, 0xf7, 0x3d, 0x21, 0xbc, 0x21, 0x6b, - 0x21, 0xd0, 0x9b, 0x1c, 0xb7, 0xa4, 0x0a, 0x26, 0xae, 0x0a, 0x1d, 0x57, 0x3e, 0x8a, 0x78, 0x5c, - 0x31, 0x1a, 0x09, 0xde, 0x0a, 0x7f, 0x0c, 0xf8, 0x30, 0x7d, 0xc8, 0x80, 0xd1, 0xa1, 0x1a, 0xb8, - 0x03, 0xe6, 0x9e, 0x18, 0x97, 0x9a, 0x27, 0x3c, 0x11, 0xba, 0xe9, 0x55, 0x68, 0x6d, 0xfc, 0x5d, - 0x04, 0xb2, 0x27, 0x38, 0x67, 0xae, 0xea, 0x04, 0xe2, 0xcd, 0xd9, 0x9e, 0xe0, 0xc7, 0xbe, 0x47, - 0x3e, 0x87, 0xe5, 0x36, 0x93, 0xca, 0xe7, 0x54, 0xf9, 0x82, 0x1f, 0x85, 0xa4, 0x2f, 0xe9, 0x88, - 0xd9, 0xd6, 0x9a, 0xb5, 0x5e, 0x75, 0xfe, 0x05, 0x25, 0x5b, 0x50, 0x9b, 0x45, 0x9e, 0xb7, 0xed, - 0x3c, 0x46, 0x65, 0x62, 0x64, 0x13, 0xee, 0x1d, 0x08, 0x97, 0x0e, 0x8d, 0xe5, 0xab, 0x7e, 0x3f, - 0x60, 0x52, 0xda, 0x05, 0x0c, 0xc9, 0x82, 0xc8, 0xa7, 0xb0, 0x98, 0x34, 0x77, 0x44, 0xa0, 0xec, - 0xe2, 0x9a, 0xb5, 0x5e, 0x72, 0x66, 0xec, 0xe4, 0x31, 0xcc, 0x85, 0xff, 0xc9, 0x2e, 0xad, 0x59, - 0xeb, 0xf3, 0x5b, 0x1f, 0x34, 0xc3, 0x2a, 0x37, 0xa3, 0x2a, 0x37, 0x8f, 0xb0, 0xca, 0xbb, 0xc5, - 0xf3, 0x8b, 0x55, 0xcb, 0x31, 0xce, 0x64, 0x07, 0xaa, 0xdf, 0x8d, 0xa5, 0x0a, 0x18, 0x1d, 0x49, - 0x7b, 0x6e, 0xad, 0xb0, 0x3e, 0xbf, 0x75, 0xaf, 0x19, 0x97, 0xb7, 0x19, 0x61, 0x18, 0x95, 0x73, - 0x6e, 0x7c, 0x49, 0x1b, 0xe6, 0x0f, 0x99, 0x1c, 0x7c, 0x43, 0x15, 0xfb, 0x89, 0x9e, 0xd9, 0x65, - 0x3c, 0xf4, 0x7e, 0x22, 0x34, 0x81, 0x86, 0x67, 0x19, 0x8e, 0x64, 0x98, 0xce, 0x7a, 0xff, 0xcd, - 0x58, 0x48, 0x66, 0x57, 0x4c, 0xd6, 0x37, 0x04, 0x21, 0x30, 0x15, 0x6b, 0x9c, 0xc9, 0x0b, 0x28, - 0x1e, 0x8a, 0x3e, 0xb3, 0xab, 0xba, 0x76, 0xbb, 0x3b, 0xd7, 0x17, 0xab, 0xdb, 0x9e, 0xaf, 0x06, - 0x93, 0x5e, 0xd3, 0x15, 0xa3, 0xd6, 0x80, 0xca, 0x81, 0xef, 0x8a, 0x60, 0xdc, 0x72, 0x05, 0x97, - 0x93, 0x61, 0x8b, 0x7a, 0x8c, 0x2b, 0xa3, 0x32, 0xd9, 0xc4, 0xfe, 0xeb, 0x70, 0x07, 0x49, 0xc8, - 0x11, 0x2c, 0x76, 0x03, 0xca, 0xe5, 0x98, 0x06, 0x8c, 0x87, 0xea, 0xb0, 0x01, 0xb3, 0x79, 0x98, - 0xc8, 0x26, 0xed, 0x32, 0x95, 0xd7, 0x0c, 0x41, 0xe3, 0xf7, 0x02, 0x54, 0xa2, 0x62, 0x91, 0x75, - 0xb8, 0x9b, 0x50, 0x44, 0xf7, 0x6c, 0x1c, 0xc9, 0x2b, 0x6d, 0x4e, 0xe9, 0x4a, 0x4b, 0x4d, 0x8e, - 0xa9, 0xcb, 0x32, 0x74, 0x15, 0x63, 0x29, 0x76, 0x14, 0x6f, 0x61, 0x86, 0x1d, 0x55, 0x5b, 0x07, - 0x68, 0x53, 0x45, 0x5d, 0xc6, 0x15, 0x0b, 0x50, 0x49, 0x55, 0x27, 0x61, 0x89, 0xf5, 0xb6, 0xeb, - 0xf3, 0x7e, 0x24, 0xcf, 0x12, 0x7a, 0xcd, 0xd8, 0xc9, 0xc7, 0xf0, 0x5e, 0x6c, 0x43, 0x61, 0xce, - 0xa1, 0x30, 0xa7, 0x8d, 0x09, 0x55, 0x96, 0xff, 0x8b, 0x2a, 0x53, 0xe2, 0xaa, 0xfc, 0x3f, 0x71, - 0x6d, 0xc2, 0xbd, 0x3d, 0xc6, 0x55, 0x40, 0x87, 0x43, 0xe3, 0x35, 0x09, 0x58, 0x1f, 0x45, 0x53, - 0x71, 0xb2, 0xa0, 0x06, 0x87, 0x05, 0x33, 0x53, 0xe6, 0xae, 0x20, 0xcb, 0x30, 0xf7, 0x92, 0x2a, - 0xff, 0x34, 0xec, 0x58, 0xc5, 0x31, 0x3b, 0xd2, 0x86, 0x85, 0x23, 0xbf, 0xcf, 0x5c, 0x1a, 0x98, - 0x00, 0xac, 0xf9, 0x74, 0x92, 0x06, 0x69, 0xb3, 0x63, 0x9f, 0xfb, 0xba, 0x09, 0x4e, 0x2a, 0xa6, - 0xf1, 0x3d, 0xdc, 0x49, 0xaa, 0x5c, 0x9f, 0xb6, 0xa7, 0xaf, 0x32, 0x19, 0x9d, 0x16, 0xee, 0xc8, - 0x23, 0x28, 0x75, 0xa8, 0x1a, 0x48, 0x3b, 0x8f, 0x13, 0xfa, 0xfe, 0xcc, 0x94, 0x68, 0xd4, 0x94, - 0x20, 0xf4, 0x6c, 0xfc, 0x66, 0x01, 0xdc, 0x60, 0xa4, 0x01, 0x77, 0x0e, 0x7c, 0xa9, 0x18, 0x67, - 0x01, 0x76, 0xcb, 0xc2, 0x6e, 0x4d, 0xd9, 0x08, 0x81, 0xa2, 0xf6, 0x35, 0x62, 0xc3, 0x75, 0xdc, - 0x66, 0xbd, 0xc1, 0xc0, 0x42, 0xa2, 0xcd, 0x91, 0x91, 0xac, 0x40, 0xa5, 0xa3, 0x1b, 0xea, 0x8a, - 0xa1, 0x91, 0x55, 0xbc, 0xd7, 0xf2, 0xec, 0xd0, 0x40, 0xb2, 0xfe, 0xd7, 0x81, 0x18, 0xe1, 0xff, - 0x41, 0x4d, 0x55, 0x9c, 0xb4, 0xb9, 0x71, 0x0c, 0x4b, 0x33, 0x7d, 0x25, 0xdf, 0x9a, 0x51, 0xc7, - 0x81, 0xd9, 0x7d, 0x76, 0x7d, 0xb1, 0xfa, 0xe4, 0xdd, 0x47, 0x3d, 0x41, 0x77, 0x33, 0xf0, 0x8d, - 0x03, 0x58, 0xce, 0x9e, 0x66, 0x3d, 0x7e, 0xaf, 0x26, 0xaa, 0x27, 0x26, 0xbc, 0x9f, 0x51, 0xad, - 0x4c, 0xac, 0xf1, 0x73, 0x19, 0x96, 0x66, 0x3a, 0x4d, 0x0e, 0xa1, 0xf8, 0xc2, 0xe7, 0x7d, 0x93, - 0xf6, 0x93, 0xeb, 0x8b, 0xd5, 0xc7, 0xef, 0x9e, 0xb6, 0xa1, 0xd3, 0x04, 0x0e, 0xd2, 0x90, 0x05, - 0xc8, 0xc7, 0xaf, 0x4b, 0xfe, 0x79, 0x5b, 0xb7, 0x2a, 0x31, 0xe8, 0xb8, 0xd6, 0xb6, 0x2e, 0xf5, - 0xa4, 0x5d, 0x5c, 0x2b, 0x68, 0x9b, 0x5e, 0x13, 0x1b, 0xca, 0xd3, 0x83, 0x1c, 0x6d, 0x09, 0x85, - 0xbb, 0x5d, 0xea, 0x79, 0x2c, 0x1a, 0x68, 0x26, 0xed, 0x45, 0x14, 0xd7, 0xa3, 0xdb, 0x14, 0xdc, - 0x4c, 0xc5, 0xec, 0x73, 0x15, 0x9c, 0x19, 0xe1, 0xa5, 0xf9, 0xc8, 0x53, 0x28, 0x1e, 0x32, 0x45, - 0xcd, 0xb3, 0xf2, 0xc9, 0xad, 0xbc, 0xda, 0x11, 0xc9, 0x1c, 0x8c, 0x41, 0x2d, 0xea, 0xca, 0x97, - 0xb1, 0xf2, 0xb8, 0x26, 0x9b, 0x50, 0x0a, 0xf5, 0x13, 0xde, 0x07, 0xb5, 0x04, 0x21, 0xda, 0xf5, - 0x0d, 0x1a, 0x0d, 0x01, 0x1a, 0xc8, 0x46, 0x3c, 0x4f, 0x55, 0xcc, 0x21, 0x33, 0x24, 0x9e, 0xb2, - 0x0d, 0x28, 0xbf, 0x66, 0xbe, 0x37, 0x50, 0xd2, 0xdc, 0xff, 0x24, 0xe1, 0x6e, 0x10, 0x27, 0x72, - 0x21, 0x35, 0x28, 0x75, 0xc5, 0x09, 0xe3, 0xf6, 0x3c, 0x16, 0x36, 0xdc, 0x90, 0x0d, 0x58, 0xda, - 0xe7, 0xb4, 0x37, 0x64, 0x5d, 0xea, 0xbd, 0x3a, 0x65, 0x41, 0xe0, 0xf7, 0x99, 0x7d, 0x07, 0xf5, - 0x3e, 0x0b, 0x90, 0x6d, 0x28, 0x85, 0xef, 0xcd, 0x02, 0x9e, 0xf7, 0x20, 0x99, 0xde, 0xcc, 0xc7, - 0x8a, 0x13, 0xfa, 0xea, 0xab, 0x67, 0x5f, 0x5f, 0xd7, 0xe3, 0xc0, 0x97, 0x0c, 0x0b, 0xbc, 0x84, - 0xd1, 0xcb, 0x4d, 0xf3, 0xa9, 0x34, 0x8d, 0x9a, 0x8a, 0xa4, 0x62, 0xc8, 0x36, 0x94, 0xcd, 0x11, - 0xf6, 0x5d, 0x0c, 0xff, 0x70, 0xb6, 0x3f, 0xc6, 0xc1, 0x89, 0x3c, 0x57, 0x7e, 0x84, 0x5a, 0x96, - 0x00, 0xc8, 0x22, 0x14, 0x4e, 0xd8, 0x99, 0x79, 0xd4, 0xf4, 0x92, 0xb4, 0xa0, 0x74, 0x4a, 0x87, - 0x93, 0xf0, 0xe5, 0xca, 0x24, 0x37, 0x14, 0x4e, 0xe8, 0xf7, 0x34, 0xff, 0x85, 0xb5, 0xb2, 0x03, - 0xd5, 0x58, 0x07, 0x19, 0x9c, 0xb5, 0x24, 0x67, 0x35, 0x11, 0xd8, 0xf8, 0x32, 0xbe, 0xb7, 0x23, - 0x79, 0x27, 0x84, 0x6f, 0x4d, 0x0b, 0x3f, 0x52, 0x56, 0xfe, 0x46, 0x59, 0x8d, 0x67, 0x71, 0xe7, - 0x75, 0x60, 0x87, 0x4a, 0xe9, 0x73, 0xcf, 0x4c, 0x7d, 0xb4, 0xd5, 0xc8, 0x6b, 0x1a, 0x70, 0x8d, - 0x84, 0xb1, 0xd1, 0x76, 0xf7, 0xf0, 0xfc, 0xaf, 0x7a, 0xee, 0xfc, 0xb2, 0x6e, 0xbd, 0xbd, 0xac, - 0x5b, 0x7f, 0x5e, 0xd6, 0xad, 0x5f, 0xae, 0xea, 0xb9, 0x5f, 0xaf, 0xea, 0xb9, 0xb7, 0x57, 0xf5, - 0xdc, 0x1f, 0x57, 0xf5, 0xdc, 0x0f, 0x9f, 0xdd, 0x36, 0xfc, 0xa9, 0x6f, 0xda, 0xde, 0x1c, 0x1a, - 0xb6, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0x1f, 0xf1, 0x41, 0x33, 0x52, 0x0b, 0x00, 0x00, + 0x14, 0xf7, 0xfa, 0x4f, 0x6c, 0xbf, 0x94, 0x34, 0x99, 0x98, 0xb0, 0x84, 0xd6, 0x49, 0x2d, 0x84, + 0x22, 0x88, 0xec, 0x36, 0x51, 0x09, 0xad, 0x54, 0x24, 0x12, 0x07, 0x54, 0x35, 0x69, 0xcd, 0xc6, + 0xa8, 0x02, 0x89, 0xc3, 0x78, 0x3d, 0x59, 0xaf, 0x62, 0xef, 0x58, 0x3b, 0xe3, 0xd0, 0x7c, 0x0b, + 0x6e, 0x70, 0xe5, 0xc0, 0x9d, 0x8f, 0x91, 0x63, 0x8f, 0x9c, 0x22, 0x48, 0xbe, 0x45, 0x4e, 0x68, + 0xde, 0xcc, 0x6e, 0xd6, 0xeb, 0x25, 0x2a, 0x9c, 0x3c, 0xf3, 0x7e, 0xef, 0xf7, 0x66, 0xfc, 0xde, + 0xef, 0xbd, 0x59, 0xb8, 0x3f, 0x0e, 0xb9, 0xe4, 0xad, 0x71, 0x4f, 0xb0, 0xf0, 0xd4, 0x77, 0x59, + 0xcb, 0xfc, 0x36, 0xd1, 0x4e, 0xaa, 0x31, 0xb0, 0x7a, 0xcf, 0xe3, 0xdc, 0x1b, 0xb2, 0x16, 0x02, + 0xbd, 0xc9, 0x71, 0x4b, 0xc8, 0x70, 0xe2, 0x4a, 0xed, 0xb8, 0xfa, 0x51, 0x14, 0xc7, 0xe5, 0xa3, + 0x11, 0x0f, 0x5a, 0xfa, 0xc7, 0x80, 0x0f, 0xd2, 0x87, 0x0c, 0x18, 0x1d, 0xca, 0x81, 0x3b, 0x60, + 0xee, 0x89, 0x71, 0xa9, 0x79, 0xdc, 0xe3, 0xda, 0x4d, 0xad, 0xb4, 0xb5, 0xf1, 0x7b, 0x09, 0xc8, + 0x1e, 0x0f, 0x02, 0xe6, 0xca, 0x4e, 0xc8, 0xdf, 0x9c, 0xed, 0xf1, 0xe0, 0xd8, 0xf7, 0xc8, 0xe7, + 0xb0, 0xd2, 0x66, 0x42, 0xfa, 0x01, 0x95, 0x3e, 0x0f, 0x8e, 0x74, 0xd0, 0x97, 0x74, 0xc4, 0x6c, + 0x6b, 0xdd, 0xda, 0xa8, 0x3a, 0xff, 0x82, 0x92, 0x2d, 0xa8, 0xcd, 0x22, 0xcf, 0xdb, 0x76, 0x1e, + 0x59, 0x99, 0x18, 0x79, 0x08, 0xcb, 0x07, 0xdc, 0xa5, 0x43, 0x63, 0xf9, 0xaa, 0xdf, 0x0f, 0x99, + 0x10, 0x76, 0x01, 0x29, 0x59, 0x10, 0xf9, 0x14, 0x16, 0x93, 0xe6, 0x0e, 0x0f, 0xa5, 0x5d, 0x5c, + 0xb7, 0x36, 0x4a, 0xce, 0x8c, 0x9d, 0x3c, 0x86, 0x39, 0xfd, 0x9f, 0xec, 0xd2, 0xba, 0xb5, 0x31, + 0xbf, 0xf5, 0x41, 0x53, 0x67, 0xb9, 0x19, 0x65, 0xb9, 0x79, 0x84, 0x59, 0xde, 0x2d, 0x9e, 0x5f, + 0xac, 0x59, 0x8e, 0x71, 0x26, 0x3b, 0x50, 0xfd, 0x6e, 0x2c, 0x64, 0xc8, 0xe8, 0x48, 0xd8, 0x73, + 0xeb, 0x85, 0x8d, 0xf9, 0xad, 0xe5, 0x66, 0x9c, 0xde, 0x66, 0x84, 0x21, 0x2b, 0xe7, 0xdc, 0xf8, + 0x92, 0x36, 0xcc, 0x1f, 0x32, 0x31, 0xf8, 0x86, 0x4a, 0xf6, 0x13, 0x3d, 0xb3, 0xcb, 0x78, 0xe8, + 0xbd, 0x04, 0x35, 0x81, 0xea, 0xb3, 0x4c, 0x8c, 0x24, 0x4d, 0xdd, 0x7a, 0xff, 0xcd, 0x98, 0x0b, + 0x66, 0x57, 0xcc, 0xad, 0x6f, 0x02, 0x68, 0x60, 0x8a, 0x6b, 0x9c, 0xc9, 0x0b, 0x28, 0x1e, 0xf2, + 0x3e, 0xb3, 0xab, 0x2a, 0x77, 0xbb, 0x3b, 0xd7, 0x17, 0x6b, 0xdb, 0x9e, 0x2f, 0x07, 0x93, 0x5e, + 0xd3, 0xe5, 0xa3, 0xd6, 0x80, 0x8a, 0x81, 0xef, 0xf2, 0x70, 0xdc, 0x72, 0x79, 0x20, 0x26, 0xc3, + 0x16, 0xf5, 0x58, 0x20, 0x8d, 0xca, 0x44, 0x13, 0xeb, 0xaf, 0xe8, 0x0e, 0x06, 0x21, 0x47, 0xb0, + 0xd8, 0x0d, 0x69, 0x20, 0xc6, 0x34, 0x64, 0x81, 0x56, 0x87, 0x0d, 0x78, 0x9b, 0x07, 0x89, 0xdb, + 0xa4, 0x5d, 0xa6, 0xee, 0x35, 0x13, 0x40, 0x09, 0x2b, 0x59, 0xa2, 0x23, 0xee, 0x9e, 0x30, 0xd9, + 0xa1, 0x72, 0x60, 0xcf, 0x6b, 0x61, 0x65, 0xa3, 0x8d, 0x5f, 0x8a, 0x50, 0x89, 0x92, 0x4c, 0x36, + 0xe0, 0x6e, 0x42, 0x49, 0xdd, 0xb3, 0x71, 0x24, 0xcb, 0xb4, 0x39, 0xa5, 0x47, 0x25, 0x51, 0x31, + 0xa6, 0x2e, 0xcb, 0xd0, 0x63, 0x8c, 0xa5, 0xa2, 0xa3, 0xe8, 0x0b, 0x33, 0xd1, 0x51, 0xed, 0x75, + 0x80, 0x36, 0x95, 0xd4, 0x65, 0x81, 0x64, 0x21, 0x2a, 0xb0, 0xea, 0x24, 0x2c, 0xb1, 0x4e, 0x77, + 0xfd, 0xa0, 0x1f, 0xc9, 0xba, 0x84, 0x5e, 0x33, 0x76, 0xf2, 0x31, 0xbc, 0x17, 0xdb, 0x50, 0xd0, + 0x73, 0x28, 0xe8, 0x69, 0x63, 0x42, 0xcd, 0xe5, 0xff, 0xa2, 0xe6, 0x94, 0x28, 0x2b, 0xff, 0x4f, + 0x94, 0x0f, 0x61, 0x79, 0x8f, 0x05, 0x32, 0xa4, 0xc3, 0xa1, 0xf1, 0x9a, 0x84, 0xac, 0x8f, 0x62, + 0xab, 0x38, 0x59, 0x50, 0xdc, 0xda, 0xea, 0xfe, 0x89, 0x52, 0x43, 0xa2, 0xb5, 0xa7, 0xa1, 0x0c, + 0x06, 0x0a, 0x7a, 0x3e, 0x93, 0xa1, 0xa0, 0x46, 0x00, 0x0b, 0x46, 0x2e, 0x66, 0x8e, 0x91, 0x15, + 0x98, 0x7b, 0x49, 0xa5, 0x7f, 0xaa, 0x55, 0x51, 0x71, 0xcc, 0x8e, 0xb4, 0x61, 0xe1, 0xc8, 0xef, + 0x33, 0x97, 0x86, 0x86, 0x80, 0x75, 0x9d, 0x4e, 0x84, 0x41, 0xda, 0xec, 0xd8, 0x0f, 0x7c, 0x55, + 0x68, 0x27, 0xc5, 0x69, 0x7c, 0x0f, 0x77, 0x92, 0x1d, 0xa8, 0x4e, 0xdb, 0x53, 0x63, 0x56, 0x44, + 0xa7, 0xe9, 0x1d, 0x79, 0x04, 0x25, 0xf5, 0x8f, 0x84, 0x9d, 0xc7, 0xe9, 0xf1, 0xfe, 0x4c, 0x07, + 0x2b, 0xd4, 0xa4, 0x59, 0x7b, 0x36, 0xfe, 0xb0, 0x00, 0x6e, 0x30, 0xd2, 0x80, 0x3b, 0x07, 0xbe, + 0x90, 0x2c, 0x60, 0x21, 0x2a, 0xc2, 0x42, 0x45, 0x4c, 0xd9, 0x08, 0x81, 0x22, 0xa6, 0x54, 0x0b, + 0x1a, 0xd7, 0xb1, 0x94, 0xd4, 0x06, 0x89, 0x85, 0x84, 0x94, 0x22, 0x23, 0x59, 0x85, 0x4a, 0x47, + 0x89, 0xc6, 0xe5, 0x43, 0x23, 0xdd, 0x78, 0xaf, 0x5a, 0xa0, 0x43, 0x43, 0xc1, 0xfa, 0x5f, 0x87, + 0x7c, 0x84, 0xff, 0x07, 0x75, 0x5b, 0x71, 0xd2, 0xe6, 0xc6, 0x31, 0x2c, 0xcd, 0x68, 0x87, 0x7c, + 0x6b, 0xc6, 0x10, 0x36, 0xe5, 0xee, 0xb3, 0xeb, 0x8b, 0xb5, 0x27, 0xef, 0x3e, 0x86, 0x12, 0xe1, + 0x6e, 0x86, 0x51, 0xe3, 0x00, 0x56, 0xb2, 0x27, 0x8d, 0x6a, 0xf1, 0x57, 0x13, 0xd9, 0xe3, 0x93, + 0xa0, 0x9f, 0x91, 0xad, 0x4c, 0xac, 0xf1, 0x5b, 0x19, 0x96, 0x66, 0x2a, 0x4d, 0x0e, 0xa1, 0xf8, + 0xc2, 0x0f, 0xfa, 0xe6, 0xda, 0x4f, 0xae, 0x2f, 0xd6, 0x1e, 0xbf, 0xfb, 0xb5, 0x4d, 0x38, 0x15, + 0xc0, 0xc1, 0x30, 0x64, 0x01, 0xf2, 0xf1, 0xcb, 0x97, 0x7f, 0xde, 0x56, 0xa5, 0x4a, 0x0c, 0x13, + 0x5c, 0x2b, 0x5b, 0x97, 0x7a, 0xc2, 0x2e, 0xae, 0x17, 0x94, 0x4d, 0xad, 0x89, 0x0d, 0xe5, 0xe9, + 0x61, 0x11, 0x6d, 0x09, 0x85, 0xbb, 0x5d, 0xea, 0x79, 0x2c, 0x1a, 0x1a, 0x4c, 0xd8, 0x8b, 0x28, + 0xae, 0x47, 0xb7, 0x29, 0xb8, 0x99, 0xe2, 0xec, 0x07, 0x32, 0x3c, 0x33, 0xc2, 0x4b, 0xc7, 0x23, + 0x4f, 0xa1, 0x78, 0xc8, 0x24, 0x35, 0x4f, 0xde, 0x27, 0xb7, 0xc6, 0x55, 0x8e, 0x18, 0xcc, 0x41, + 0x0e, 0x6a, 0x51, 0x65, 0xbe, 0x8c, 0x99, 0xc7, 0xb5, 0x1a, 0x91, 0x89, 0xc6, 0x27, 0x7a, 0x44, + 0x4e, 0xf5, 0x7b, 0x49, 0xeb, 0x4b, 0xcf, 0xa4, 0x5a, 0xe2, 0x40, 0xb4, 0xab, 0x29, 0x1e, 0x35, + 0x09, 0x1a, 0xc8, 0x66, 0xdc, 0x6f, 0x55, 0xbc, 0x63, 0x26, 0x25, 0xee, 0xc2, 0x4d, 0x28, 0xbf, + 0x66, 0xbe, 0x37, 0x90, 0xc2, 0xbc, 0x5d, 0x24, 0xe1, 0x6e, 0x10, 0x27, 0x72, 0x21, 0x35, 0x28, + 0x75, 0xf9, 0x09, 0x0b, 0xcc, 0xbc, 0xd1, 0x1b, 0xb2, 0x09, 0x4b, 0xfb, 0x01, 0xed, 0x0d, 0x59, + 0x97, 0x7a, 0xaf, 0x4e, 0x59, 0x18, 0xfa, 0x7d, 0x66, 0xdf, 0xc1, 0x7e, 0x98, 0x05, 0xc8, 0x36, + 0x94, 0xf4, 0x5b, 0xb9, 0x80, 0xe7, 0xdd, 0x4f, 0x5e, 0x6f, 0xe6, 0x43, 0xcb, 0xd1, 0xbe, 0x6a, + 0x34, 0xed, 0xab, 0x27, 0x63, 0x1c, 0xfa, 0x82, 0x61, 0x01, 0x96, 0x90, 0xbd, 0xd2, 0x34, 0x9f, + 0x79, 0xd3, 0xa8, 0xc9, 0x48, 0x8a, 0x43, 0xb6, 0xa1, 0x6c, 0x8e, 0xb0, 0xef, 0x22, 0xfd, 0xc3, + 0xd9, 0xfa, 0x19, 0x07, 0x27, 0xf2, 0x5c, 0xfd, 0x11, 0x6a, 0x59, 0x02, 0x21, 0x8b, 0x50, 0x38, + 0x61, 0x67, 0xe6, 0x61, 0x55, 0x4b, 0xd2, 0x82, 0xd2, 0x29, 0x1d, 0x4e, 0xf4, 0xeb, 0x99, 0x19, + 0xdc, 0x84, 0x70, 0xb4, 0xdf, 0xd3, 0xfc, 0x17, 0xd6, 0xea, 0x0e, 0x54, 0x63, 0x9d, 0x64, 0xc4, + 0xac, 0x25, 0x63, 0x56, 0x13, 0xc4, 0xc6, 0x97, 0xf1, 0x5c, 0x8f, 0xe4, 0x9f, 0x68, 0x0c, 0x6b, + 0xba, 0x31, 0x22, 0xe5, 0xe5, 0x6f, 0x94, 0xd7, 0x78, 0x16, 0x57, 0x5e, 0x11, 0x3b, 0x54, 0x08, + 0x3f, 0xf0, 0xcc, 0x54, 0x88, 0xb6, 0x0a, 0x79, 0x4d, 0xc3, 0x40, 0x21, 0x9a, 0x1b, 0x6d, 0x77, + 0x0f, 0xcf, 0xff, 0xae, 0xe7, 0xce, 0x2f, 0xeb, 0xd6, 0xdb, 0xcb, 0xba, 0xf5, 0xd7, 0x65, 0xdd, + 0xfa, 0xf9, 0xaa, 0x9e, 0xfb, 0xf5, 0xaa, 0x9e, 0x7b, 0x7b, 0x55, 0xcf, 0xfd, 0x79, 0x55, 0xcf, + 0xfd, 0xf0, 0xd9, 0x6d, 0xc3, 0x21, 0xf5, 0x3d, 0xde, 0x9b, 0x43, 0xc3, 0xf6, 0x3f, 0x01, 0x00, + 0x00, 0xff, 0xff, 0x44, 0x39, 0xad, 0x45, 0x0e, 0x0c, 0x00, 0x00, } func (m *ConnectProxyConfig) Marshal() (dAtA []byte, err error) { @@ -702,6 +712,13 @@ func (m *ConnectProxyConfig) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.LocalServiceSocketPath) > 0 { + i -= len(m.LocalServiceSocketPath) + copy(dAtA[i:], m.LocalServiceSocketPath) + i = encodeVarintService(dAtA, i, uint64(len(m.LocalServiceSocketPath))) + i-- + dAtA[i] = 0x5a + } { size, err := m.TransparentProxy.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -814,6 +831,20 @@ func (m *Upstream) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.LocalBindSocketMode) > 0 { + i -= len(m.LocalBindSocketMode) + copy(dAtA[i:], m.LocalBindSocketMode) + i = encodeVarintService(dAtA, i, uint64(len(m.LocalBindSocketMode))) + i-- + dAtA[i] = 0x5a + } + if len(m.LocalBindSocketPath) > 0 { + i -= len(m.LocalBindSocketPath) + copy(dAtA[i:], m.LocalBindSocketPath) + i = encodeVarintService(dAtA, i, uint64(len(m.LocalBindSocketPath))) + i-- + dAtA[i] = 0x52 + } if m.CentrallyConfigured { i-- if m.CentrallyConfigured { @@ -1116,6 +1147,15 @@ func (m *ServiceDefinition) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.SocketPath) > 0 { + i -= len(m.SocketPath) + copy(dAtA[i:], m.SocketPath) + i = encodeVarintService(dAtA, i, uint64(len(m.SocketPath))) + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x92 + } { size, err := m.EnterpriseMeta.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -1415,6 +1455,10 @@ func (m *ConnectProxyConfig) Size() (n int) { } l = m.TransparentProxy.Size() n += 1 + l + sovService(uint64(l)) + l = len(m.LocalServiceSocketPath) + if l > 0 { + n += 1 + l + sovService(uint64(l)) + } return n } @@ -1456,6 +1500,14 @@ func (m *Upstream) Size() (n int) { if m.CentrallyConfigured { n += 2 } + l = len(m.LocalBindSocketPath) + if l > 0 { + n += 1 + l + sovService(uint64(l)) + } + l = len(m.LocalBindSocketMode) + if l > 0 { + n += 1 + l + sovService(uint64(l)) + } return n } @@ -1621,6 +1673,10 @@ func (m *ServiceDefinition) Size() (n int) { } l = m.EnterpriseMeta.Size() n += 2 + l + sovService(uint64(l)) + l = len(m.SocketPath) + if l > 0 { + n += 2 + l + sovService(uint64(l)) + } return n } @@ -2006,6 +2062,38 @@ func (m *ConnectProxyConfig) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LocalServiceSocketPath", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LocalServiceSocketPath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) @@ -2327,6 +2415,70 @@ func (m *Upstream) Unmarshal(dAtA []byte) error { } } m.CentrallyConfigured = bool(v != 0) + case 10: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LocalBindSocketPath", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LocalBindSocketPath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 11: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LocalBindSocketMode", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.LocalBindSocketMode = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) @@ -3623,6 +3775,38 @@ func (m *ServiceDefinition) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 18: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SocketPath", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowService + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthService + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthService + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SocketPath = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipService(dAtA[iNdEx:]) diff --git a/proto/pbservice/service.proto b/proto/pbservice/service.proto index 60f4d26da..3f95a79aa 100644 --- a/proto/pbservice/service.proto +++ b/proto/pbservice/service.proto @@ -76,6 +76,9 @@ message ConnectProxyConfig { // TransparentProxy defines configuration for when the proxy is in // transparent mode. TransparentProxyConfig TransparentProxy = 10 [(gogoproto.nullable) = false]; + + // LocalServiceSocketPath is the path to the unix domain socket for the local service instance + string LocalServiceSocketPath = 11; } // Upstream represents a single upstream dependency for a service or proxy. It @@ -127,6 +130,10 @@ message Upstream { // CentrallyConfigured indicates whether the upstream was defined in a proxy // instance registration or whether it was generated from a config entry. bool CentrallyConfigured = 9; + + // LocalBindSocketPath is the socket to create to connect to the upstream service + string LocalBindSocketPath = 10; + string LocalBindSocketMode = 11; } // ServiceConnect are the shared Connect settings between all service @@ -232,6 +239,8 @@ message ServiceDefinition { map Meta = 6; // mog: func-to=int func-from=int32 int32 Port = 7; + // Path for socket + string SocketPath = 18; CheckType Check = 8 [(gogoproto.nullable) = false]; // mog: func-to=CheckTypesToStructs func-from=NewCheckTypesFromStructs repeated CheckType Checks = 9;