Add support for dual stack IPv4/IPv6 network (#6640)

* Use consts for well known tagged adress keys

* Add ipv4 and ipv6 tagged addresses for node lan and wan

* Add ipv4 and ipv6 tagged addresses for service lan and wan

* Use IPv4 and IPv6 address in DNS
This commit is contained in:
Aestek 2020-01-17 15:54:17 +01:00 committed by Matt Keeler
parent 5dc8875bd3
commit ba8fd8296f
19 changed files with 670 additions and 136 deletions

View File

@ -2258,6 +2258,26 @@ func (a *Agent) addServiceInternal(req *addServiceRequest) error {
a.PauseSync()
defer a.ResumeSync()
// Set default tagged addresses
serviceIP := net.ParseIP(service.Address)
serviceAddressIs4 := serviceIP != nil && serviceIP.To4() != nil
serviceAddressIs6 := serviceIP != nil && serviceIP.To4() == nil
if service.TaggedAddresses == nil {
service.TaggedAddresses = map[string]structs.ServiceAddress{}
}
if _, ok := service.TaggedAddresses[structs.TaggedAddressLANIPv4]; !ok && serviceAddressIs4 {
service.TaggedAddresses[structs.TaggedAddressLANIPv4] = structs.ServiceAddress{Address: service.Address, Port: service.Port}
}
if _, ok := service.TaggedAddresses[structs.TaggedAddressWANIPv4]; !ok && serviceAddressIs4 {
service.TaggedAddresses[structs.TaggedAddressWANIPv4] = structs.ServiceAddress{Address: service.Address, Port: service.Port}
}
if _, ok := service.TaggedAddresses[structs.TaggedAddressLANIPv6]; !ok && serviceAddressIs6 {
service.TaggedAddresses[structs.TaggedAddressLANIPv6] = structs.ServiceAddress{Address: service.Address, Port: service.Port}
}
if _, ok := service.TaggedAddresses[structs.TaggedAddressWANIPv6]; !ok && serviceAddressIs6 {
service.TaggedAddresses[structs.TaggedAddressWANIPv6] = structs.ServiceAddress{Address: service.Address, Port: service.Port}
}
// Take a snapshot of the current state of checks (if any), and when adding
// a check that already existed carry over the state before resuming
// anti-entropy.
@ -2453,6 +2473,34 @@ func (a *Agent) validateService(service *structs.NodeService, chkTypes []*struct
}
}
// Check IPv4/IPv6 tagged addresses
if service.TaggedAddresses != nil {
if sa, ok := service.TaggedAddresses[structs.TaggedAddressLANIPv4]; ok {
ip := net.ParseIP(sa.Address)
if ip == nil || ip.To4() == nil {
return fmt.Errorf("Service tagged address %q must be a valid ipv4 address", structs.TaggedAddressLANIPv4)
}
}
if sa, ok := service.TaggedAddresses[structs.TaggedAddressWANIPv4]; ok {
ip := net.ParseIP(sa.Address)
if ip == nil || ip.To4() == nil {
return fmt.Errorf("Service tagged address %q must be a valid ipv4 address", structs.TaggedAddressWANIPv4)
}
}
if sa, ok := service.TaggedAddresses[structs.TaggedAddressLANIPv6]; ok {
ip := net.ParseIP(sa.Address)
if ip == nil || ip.To4() != nil {
return fmt.Errorf("Service tagged address %q must be a valid ipv6 address", structs.TaggedAddressLANIPv6)
}
}
if sa, ok := service.TaggedAddresses[structs.TaggedAddressLANIPv6]; ok {
ip := net.ParseIP(sa.Address)
if ip == nil || ip.To4() != nil {
return fmt.Errorf("Service tagged address %q must be a valid ipv6 address", structs.TaggedAddressLANIPv6)
}
}
}
return nil
}

View File

@ -3031,10 +3031,11 @@ func testAgent_RegisterService_UnmanagedConnectProxy(t *testing.T, extraHCL stri
func testDefaultSidecar(svc string, port int, fns ...func(*structs.NodeService)) *structs.NodeService {
ns := &structs.NodeService{
ID: svc + "-sidecar-proxy",
Kind: structs.ServiceKindConnectProxy,
Service: svc + "-sidecar-proxy",
Port: 2222,
ID: svc + "-sidecar-proxy",
Kind: structs.ServiceKindConnectProxy,
Service: svc + "-sidecar-proxy",
Port: 2222,
TaggedAddresses: map[string]structs.ServiceAddress{},
Weights: &structs.Weights{
Passing: 1,
Warning: 1,
@ -3396,9 +3397,10 @@ func testAgent_RegisterServiceDeregisterService_Sidecar(t *testing.T, extraHCL s
// Note here that although the registration here didn't register it, we
// should still see the NodeService we pre-registered here.
wantNS: &structs.NodeService{
ID: "web-sidecar-proxy",
Service: "fake-sidecar",
Port: 9999,
ID: "web-sidecar-proxy",
Service: "fake-sidecar",
Port: 9999,
TaggedAddresses: map[string]structs.ServiceAddress{},
Weights: &structs.Weights{
Passing: 1,
Warning: 1,
@ -3439,6 +3441,7 @@ func testAgent_RegisterServiceDeregisterService_Sidecar(t *testing.T, extraHCL s
Service: "web-sidecar-proxy",
LocallyRegisteredAsSidecar: true,
Port: 6666,
TaggedAddresses: map[string]structs.ServiceAddress{},
Weights: &structs.Weights{
Passing: 1,
Warning: 1,

View File

@ -718,6 +718,134 @@ func testAgent_AddServiceNoRemoteExec(t *testing.T, extraHCL string) {
}
}
func TestAddServiceIPv4TaggedDefault(t *testing.T) {
t.Helper()
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
srv := &structs.NodeService{
Service: "my_service",
ID: "my_service_id",
Port: 8100,
Address: "10.0.1.2",
}
err := a.AddService(srv, []*structs.CheckType{}, false, "", ConfigSourceRemote)
require.Nil(t, err)
ns := a.State.Service(structs.NewServiceID("my_service_id", nil))
require.NotNil(t, ns)
svcAddr := structs.ServiceAddress{Address: srv.Address, Port: srv.Port}
require.Equal(t, svcAddr, ns.TaggedAddresses[structs.TaggedAddressLANIPv4])
require.Equal(t, svcAddr, ns.TaggedAddresses[structs.TaggedAddressWANIPv4])
_, ok := ns.TaggedAddresses[structs.TaggedAddressLANIPv6]
require.False(t, ok)
_, ok = ns.TaggedAddresses[structs.TaggedAddressWANIPv6]
require.False(t, ok)
}
func TestAddServiceIPv6TaggedDefault(t *testing.T) {
t.Helper()
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
srv := &structs.NodeService{
Service: "my_service",
ID: "my_service_id",
Port: 8100,
Address: "::5",
}
err := a.AddService(srv, []*structs.CheckType{}, false, "", ConfigSourceRemote)
require.Nil(t, err)
ns := a.State.Service(structs.NewServiceID("my_service_id", nil))
require.NotNil(t, ns)
svcAddr := structs.ServiceAddress{Address: srv.Address, Port: srv.Port}
require.Equal(t, svcAddr, ns.TaggedAddresses[structs.TaggedAddressLANIPv6])
require.Equal(t, svcAddr, ns.TaggedAddresses[structs.TaggedAddressWANIPv6])
_, ok := ns.TaggedAddresses[structs.TaggedAddressLANIPv4]
require.False(t, ok)
_, ok = ns.TaggedAddresses[structs.TaggedAddressWANIPv4]
require.False(t, ok)
}
func TestAddServiceIPv4TaggedSet(t *testing.T) {
t.Helper()
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
srv := &structs.NodeService{
Service: "my_service",
ID: "my_service_id",
Port: 8100,
Address: "10.0.1.2",
TaggedAddresses: map[string]structs.ServiceAddress{
structs.TaggedAddressWANIPv4: {
Address: "10.100.200.5",
Port: 8100,
},
},
}
err := a.AddService(srv, []*structs.CheckType{}, false, "", ConfigSourceRemote)
require.Nil(t, err)
ns := a.State.Service(structs.NewServiceID("my_service_id", nil))
require.NotNil(t, ns)
svcAddr := structs.ServiceAddress{Address: srv.Address, Port: srv.Port}
require.Equal(t, svcAddr, ns.TaggedAddresses[structs.TaggedAddressLANIPv4])
require.Equal(t, structs.ServiceAddress{Address: "10.100.200.5", Port: 8100}, ns.TaggedAddresses[structs.TaggedAddressWANIPv4])
_, ok := ns.TaggedAddresses[structs.TaggedAddressLANIPv6]
require.False(t, ok)
_, ok = ns.TaggedAddresses[structs.TaggedAddressWANIPv6]
require.False(t, ok)
}
func TestAddServiceIPv6TaggedSet(t *testing.T) {
t.Helper()
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
srv := &structs.NodeService{
Service: "my_service",
ID: "my_service_id",
Port: 8100,
Address: "::5",
TaggedAddresses: map[string]structs.ServiceAddress{
structs.TaggedAddressWANIPv6: {
Address: "::6",
Port: 8100,
},
},
}
err := a.AddService(srv, []*structs.CheckType{}, false, "", ConfigSourceRemote)
require.Nil(t, err)
ns := a.State.Service(structs.NewServiceID("my_service_id", nil))
require.NotNil(t, ns)
svcAddr := structs.ServiceAddress{Address: srv.Address, Port: srv.Port}
require.Equal(t, svcAddr, ns.TaggedAddresses[structs.TaggedAddressLANIPv6])
require.Equal(t, structs.ServiceAddress{Address: "::6", Port: 8100}, ns.TaggedAddresses[structs.TaggedAddressWANIPv6])
_, ok := ns.TaggedAddresses[structs.TaggedAddressLANIPv4]
require.False(t, ok)
_, ok = ns.TaggedAddresses[structs.TaggedAddressWANIPv4]
require.False(t, ok)
}
func TestAgent_RemoveService(t *testing.T) {
t.Run("normal", func(t *testing.T) {
t.Parallel()
@ -1876,12 +2004,13 @@ func testAgent_persistedService_compat(t *testing.T, extraHCL string) {
defer a.Shutdown()
svc := &structs.NodeService{
ID: "redis",
Service: "redis",
Tags: []string{"foo"},
Port: 8000,
Weights: &structs.Weights{Passing: 1, Warning: 1},
EnterpriseMeta: *structs.DefaultEnterpriseMeta(),
ID: "redis",
Service: "redis",
Tags: []string{"foo"},
Port: 8000,
TaggedAddresses: map[string]structs.ServiceAddress{},
Weights: &structs.Weights{Passing: 1, Warning: 1},
EnterpriseMeta: *structs.DefaultEnterpriseMeta(),
}
// Encode the NodeService directly. This is what previous versions

View File

@ -138,7 +138,7 @@ RETRY_ONCE:
}
out.ConsistencyLevel = args.QueryOptions.ConsistencyLevel()
s.agent.TranslateAddresses(args.Datacenter, out.Nodes)
s.agent.TranslateAddresses(args.Datacenter, out.Nodes, TranslateAddressAcceptAny)
// Use empty list instead of nil
if out.Nodes == nil {
@ -284,7 +284,7 @@ func (s *HTTPServer) catalogServiceNodes(resp http.ResponseWriter, req *http.Req
}
out.ConsistencyLevel = args.QueryOptions.ConsistencyLevel()
s.agent.TranslateAddresses(args.Datacenter, out.ServiceNodes)
s.agent.TranslateAddresses(args.Datacenter, out.ServiceNodes, TranslateAddressAcceptAny)
// Use empty list instead of nil
if out.ServiceNodes == nil {
@ -340,7 +340,7 @@ RETRY_ONCE:
}
out.ConsistencyLevel = args.QueryOptions.ConsistencyLevel()
if out.NodeServices != nil {
s.agent.TranslateAddresses(args.Datacenter, out.NodeServices)
s.agent.TranslateAddresses(args.Datacenter, out.NodeServices, TranslateAddressAcceptAny)
}
// TODO: The NodeServices object in IndexedNodeServices is a pointer to

View File

@ -415,6 +415,7 @@ func (b *Builder) Build() (rt RuntimeConfig, err error) {
bindAddr := bindAddrs[0].(*net.IPAddr)
advertiseAddr := b.makeIPAddr(b.expandFirstIP("advertise_addr", c.AdvertiseAddrLAN), bindAddr)
if ipaddr.IsAny(advertiseAddr) {
var addrtyp string
@ -460,7 +461,39 @@ func (b *Builder) Build() (rt RuntimeConfig, err error) {
// derive other advertise addresses from the advertise address
advertiseAddrLAN := b.makeIPAddr(b.expandFirstIP("advertise_addr", c.AdvertiseAddrLAN), advertiseAddr)
advertiseAddrIsV6 := advertiseAddr.IP.To4() == nil
var advertiseAddrV4, advertiseAddrV6 *net.IPAddr
if !advertiseAddrIsV6 {
advertiseAddrV4 = advertiseAddr
} else {
advertiseAddrV6 = advertiseAddr
}
advertiseAddrLANIPv4 := b.makeIPAddr(b.expandFirstIP("advertise_addr_ipv4", c.AdvertiseAddrLANIPv4), advertiseAddrV4)
if advertiseAddrLANIPv4 != nil && advertiseAddrLANIPv4.IP.To4() == nil {
return RuntimeConfig{}, fmt.Errorf("advertise_addr_ipv4 must be an ipv4 address")
}
advertiseAddrLANIPv6 := b.makeIPAddr(b.expandFirstIP("advertise_addr_ipv6", c.AdvertiseAddrLANIPv6), advertiseAddrV6)
if advertiseAddrLANIPv6 != nil && advertiseAddrLANIPv6.IP.To4() != nil {
return RuntimeConfig{}, fmt.Errorf("advertise_addr_ipv6 must be an ipv6 address")
}
advertiseAddrWAN := b.makeIPAddr(b.expandFirstIP("advertise_addr_wan", c.AdvertiseAddrWAN), advertiseAddrLAN)
advertiseAddrWANIsV6 := advertiseAddrWAN.IP.To4() == nil
var advertiseAddrWANv4, advertiseAddrWANv6 *net.IPAddr
if !advertiseAddrWANIsV6 {
advertiseAddrWANv4 = advertiseAddrWAN
} else {
advertiseAddrWANv6 = advertiseAddrWAN
}
advertiseAddrWANIPv4 := b.makeIPAddr(b.expandFirstIP("advertise_addr_wan_ipv4", c.AdvertiseAddrWANIPv4), advertiseAddrWANv4)
if advertiseAddrWANIPv4 != nil && advertiseAddrWANIPv4.IP.To4() == nil {
return RuntimeConfig{}, fmt.Errorf("advertise_addr_wan_ipv4 must be an ipv4 address")
}
advertiseAddrWANIPv6 := b.makeIPAddr(b.expandFirstIP("advertise_addr_wan_ipv6", c.AdvertiseAddrWANIPv6), advertiseAddrWANv6)
if advertiseAddrWANIPv6 != nil && advertiseAddrWANIPv6.IP.To4() != nil {
return RuntimeConfig{}, fmt.Errorf("advertise_addr_wan_ipv6 must be an ipv6 address")
}
rpcAdvertiseAddr := &net.TCPAddr{IP: advertiseAddrLAN.IP, Port: serverPort}
serfAdvertiseAddrLAN := &net.TCPAddr{IP: advertiseAddrLAN.IP, Port: serfPortLAN}
// Only initialize serf WAN advertise address when its enabled
@ -509,8 +542,22 @@ func (b *Builder) Build() (rt RuntimeConfig, err error) {
if c.TaggedAddresses == nil {
c.TaggedAddresses = make(map[string]string)
}
c.TaggedAddresses["lan"] = advertiseAddrLAN.IP.String()
c.TaggedAddresses["wan"] = advertiseAddrWAN.IP.String()
c.TaggedAddresses[structs.TaggedAddressLAN] = advertiseAddrLAN.IP.String()
if advertiseAddrLANIPv4 != nil {
c.TaggedAddresses[structs.TaggedAddressLANIPv4] = advertiseAddrLANIPv4.IP.String()
}
if advertiseAddrLANIPv6 != nil {
c.TaggedAddresses[structs.TaggedAddressLANIPv6] = advertiseAddrLANIPv6.IP.String()
}
c.TaggedAddresses[structs.TaggedAddressWAN] = advertiseAddrWAN.IP.String()
if advertiseAddrWANIPv4 != nil {
c.TaggedAddresses[structs.TaggedAddressWANIPv4] = advertiseAddrWANIPv4.IP.String()
}
if advertiseAddrWANIPv6 != nil {
c.TaggedAddresses[structs.TaggedAddressWANIPv6] = advertiseAddrWANIPv6.IP.String()
}
// segments
var segments []structs.NetworkSegment

View File

@ -182,7 +182,11 @@ type Config struct {
ACL ACL `json:"acl,omitempty" hcl:"acl" mapstructure:"acl"`
Addresses Addresses `json:"addresses,omitempty" hcl:"addresses" mapstructure:"addresses"`
AdvertiseAddrLAN *string `json:"advertise_addr,omitempty" hcl:"advertise_addr" mapstructure:"advertise_addr"`
AdvertiseAddrLANIPv4 *string `json:"advertise_addr_ipv4,omitempty" hcl:"advertise_addr_ipv4" mapstructure:"advertise_addr_ipv4"`
AdvertiseAddrLANIPv6 *string `json:"advertise_addr_ipv6,omitempty" hcl:"advertise_addr_ipv6" mapstructure:"advertise_addr_ipv6"`
AdvertiseAddrWAN *string `json:"advertise_addr_wan,omitempty" hcl:"advertise_addr_wan" mapstructure:"advertise_addr_wan"`
AdvertiseAddrWANIPv4 *string `json:"advertise_addr_wan_ipv4,omitempty" hcl:"advertise_addr_wan_ipv4" mapstructure:"advertise_addr_wan_ipv4"`
AdvertiseAddrWANIPv6 *string `json:"advertise_addr_wan_ipv6,omitempty" hcl:"advertise_addr_wan_ipv6" mapstructure:"advertise_addr_ipv6"`
Autopilot Autopilot `json:"autopilot,omitempty" hcl:"autopilot" mapstructure:"autopilot"`
BindAddr *string `json:"bind_addr,omitempty" hcl:"bind_addr" mapstructure:"bind_addr"`
Bootstrap *bool `json:"bootstrap,omitempty" hcl:"bootstrap" mapstructure:"bootstrap"`

View File

@ -70,8 +70,10 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
rt.SerfAdvertiseAddrLAN = tcpAddr("1.2.3.4:8301")
rt.SerfAdvertiseAddrWAN = tcpAddr("1.2.3.4:8302")
rt.TaggedAddresses = map[string]string{
"lan": "1.2.3.4",
"wan": "1.2.3.4",
"lan": "1.2.3.4",
"lan_ipv4": "1.2.3.4",
"wan": "1.2.3.4",
"wan_ipv4": "1.2.3.4",
}
rt.DataDir = dataDir
},
@ -86,8 +88,10 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
rt.AdvertiseAddrWAN = ipAddr("1.2.3.4")
rt.SerfAdvertiseAddrWAN = tcpAddr("1.2.3.4:8302")
rt.TaggedAddresses = map[string]string{
"lan": "10.0.0.1",
"wan": "1.2.3.4",
"lan": "10.0.0.1",
"lan_ipv4": "10.0.0.1",
"wan": "1.2.3.4",
"wan_ipv4": "1.2.3.4",
}
rt.DataDir = dataDir
},
@ -106,8 +110,10 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
rt.SerfAdvertiseAddrLAN = tcpAddr("1.2.3.4:8301")
rt.SerfAdvertiseAddrWAN = tcpAddr("5.6.7.8:8302")
rt.TaggedAddresses = map[string]string{
"lan": "1.2.3.4",
"wan": "5.6.7.8",
"lan": "1.2.3.4",
"lan_ipv4": "1.2.3.4",
"wan": "5.6.7.8",
"wan_ipv4": "5.6.7.8",
}
rt.DataDir = dataDir
},
@ -129,8 +135,10 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
rt.SerfBindAddrLAN = tcpAddr("1.2.3.4:8301")
rt.SerfBindAddrWAN = tcpAddr("1.2.3.4:8302")
rt.TaggedAddresses = map[string]string{
"lan": "1.2.3.4",
"wan": "1.2.3.4",
"lan": "1.2.3.4",
"lan_ipv4": "1.2.3.4",
"wan": "1.2.3.4",
"wan_ipv4": "1.2.3.4",
}
rt.DataDir = dataDir
},
@ -282,7 +290,12 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
rt.SerfBindAddrWAN = tcpAddr("127.0.0.1:8302")
rt.ServerMode = true
rt.SkipLeaveOnInt = true
rt.TaggedAddresses = map[string]string{"lan": "127.0.0.1", "wan": "127.0.0.1"}
rt.TaggedAddresses = map[string]string{
"lan": "127.0.0.1",
"lan_ipv4": "127.0.0.1",
"wan": "127.0.0.1",
"wan_ipv4": "127.0.0.1",
}
rt.ConsulCoordinateUpdatePeriod = 100 * time.Millisecond
rt.ConsulRaftElectionTimeout = 52 * time.Millisecond
rt.ConsulRaftHeartbeatTimeout = 35 * time.Millisecond
@ -836,8 +849,10 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
rt.SerfBindAddrLAN = tcpAddr("0.0.0.0:8301")
rt.SerfBindAddrWAN = tcpAddr("0.0.0.0:8302")
rt.TaggedAddresses = map[string]string{
"lan": "10.0.0.1",
"wan": "10.0.0.1",
"lan": "10.0.0.1",
"lan_ipv4": "10.0.0.1",
"wan": "10.0.0.1",
"wan_ipv4": "10.0.0.1",
}
rt.DataDir = dataDir
},
@ -858,8 +873,10 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
rt.SerfBindAddrLAN = tcpAddr("[::]:8301")
rt.SerfBindAddrWAN = tcpAddr("[::]:8302")
rt.TaggedAddresses = map[string]string{
"lan": "dead:beef::1",
"wan": "dead:beef::1",
"lan": "dead:beef::1",
"lan_ipv6": "dead:beef::1",
"wan": "dead:beef::1",
"wan_ipv6": "dead:beef::1",
}
rt.DataDir = dataDir
},
@ -883,8 +900,10 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
rt.SerfBindAddrLAN = tcpAddr("0.0.0.0:8301")
rt.SerfBindAddrWAN = tcpAddr("0.0.0.0:8302")
rt.TaggedAddresses = map[string]string{
"lan": "1.2.3.4",
"wan": "1.2.3.4",
"lan": "1.2.3.4",
"lan_ipv4": "1.2.3.4",
"wan": "1.2.3.4",
"wan_ipv4": "1.2.3.4",
}
rt.DataDir = dataDir
},
@ -1101,8 +1120,10 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
rt.SerfAdvertiseAddrLAN = tcpAddr("1.2.3.4:8301")
rt.SerfAdvertiseAddrWAN = tcpAddr("1.2.3.4:8302")
rt.TaggedAddresses = map[string]string{
"lan": "1.2.3.4",
"wan": "1.2.3.4",
"lan": "1.2.3.4",
"lan_ipv4": "1.2.3.4",
"wan": "1.2.3.4",
"wan_ipv4": "1.2.3.4",
}
rt.DataDir = dataDir
},
@ -1116,8 +1137,10 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
rt.AdvertiseAddrWAN = ipAddr("1.2.3.4")
rt.SerfAdvertiseAddrWAN = tcpAddr("1.2.3.4:8302")
rt.TaggedAddresses = map[string]string{
"lan": "10.0.0.1",
"wan": "1.2.3.4",
"lan": "10.0.0.1",
"lan_ipv4": "10.0.0.1",
"wan": "1.2.3.4",
"wan_ipv4": "1.2.3.4",
}
rt.DataDir = dataDir
},
@ -1154,8 +1177,10 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
rt.SerfPortWAN = 3000
rt.ServerPort = 1000
rt.TaggedAddresses = map[string]string{
"lan": "1.2.3.4",
"wan": "1.2.3.4",
"lan": "1.2.3.4",
"lan_ipv4": "1.2.3.4",
"wan": "1.2.3.4",
"wan_ipv4": "1.2.3.4",
}
rt.DataDir = dataDir
},
@ -1192,8 +1217,10 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
rt.SerfPortWAN = 3000
rt.ServerPort = 1000
rt.TaggedAddresses = map[string]string{
"lan": "10.0.0.1",
"wan": "1.2.3.4",
"lan": "10.0.0.1",
"lan_ipv4": "10.0.0.1",
"wan": "1.2.3.4",
"wan_ipv4": "1.2.3.4",
}
rt.DataDir = dataDir
},
@ -1218,8 +1245,10 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
rt.SerfAdvertiseAddrWAN = nil
rt.SerfBindAddrWAN = nil
rt.TaggedAddresses = map[string]string{
"lan": "10.0.0.1",
"wan": "1.2.3.4",
"lan": "10.0.0.1",
"lan_ipv4": "10.0.0.1",
"wan": "1.2.3.4",
"wan_ipv4": "1.2.3.4",
}
rt.DataDir = dataDir
rt.SerfPortWAN = -1
@ -1430,8 +1459,10 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
rt.SerfBindAddrWAN = tcpAddr("4.4.4.4:8302")
rt.StartJoinAddrsLAN = []string{"c", "d", "a", "b"}
rt.TaggedAddresses = map[string]string{
"lan": "1.1.1.1",
"wan": "2.2.2.2",
"lan": "1.1.1.1",
"lan_ipv4": "1.1.1.1",
"wan": "2.2.2.2",
"wan_ipv4": "2.2.2.2",
}
rt.DataDir = dataDir
},
@ -5464,7 +5495,9 @@ func TestFullConfig(t *testing.T) {
"7MYgHrYH": "dALJAhLD",
"h6DdBy6K": "ebrr9zZ8",
"lan": "17.99.29.16",
"lan_ipv4": "17.99.29.16",
"wan": "78.63.37.19",
"wan_ipv4": "78.63.37.19",
},
TranslateWANAddrs: true,
UIContentPath: "/consul/",

View File

@ -1308,24 +1308,25 @@ RPC:
// serviceNodeRecords is used to add the node records for a service lookup
func (d *DNSServer) serviceNodeRecords(cfg *dnsConfig, dc string, nodes structs.CheckServiceNodes, req, resp *dns.Msg, ttl time.Duration, maxRecursionLevel int) {
qName := req.Question[0].Name
handled := make(map[string]struct{})
var answerCNAME []dns.RR = nil
count := 0
for _, node := range nodes {
addr := d.serviceNodeAddr(node, dc, qName)
// Avoid duplicate entries, possible if a node has
// the same service on multiple ports, etc.
if _, ok := handled[addr]; ok {
continue
}
handled[addr] = struct{}{}
// Add the node record
had_answer := false
records, _ := d.nodeServiceRecords(dc, node, req, ttl, cfg, maxRecursionLevel)
if len(records) == 0 {
continue
}
// Avoid duplicate entries, possible if a node has
// the same service on multiple ports, etc.
if _, ok := handled[records[0].String()]; ok {
continue
}
handled[records[0].String()] = struct{}{}
if records != nil {
switch records[0].(type) {
case *dns.CNAME:
@ -1399,25 +1400,6 @@ func findWeight(node structs.CheckServiceNode) int {
}
}
// serviceNodeAddr is used to identify target service address
func (d *DNSServer) serviceNodeAddr(serviceNode structs.CheckServiceNode, dc string, dnsQuery string) string {
nodeAddress := d.agent.TranslateAddress(dc, serviceNode.Node.Address, serviceNode.Node.TaggedAddresses)
serviceAddress := d.agent.TranslateServiceAddress(dc, serviceNode.Service.Address, serviceNode.Service.TaggedAddresses)
addr := nodeAddress
if serviceAddress != "" {
addr = serviceAddress
}
// If the service address is a CNAME for the service we are looking
// for then use the node address.
if dnsQuery == strings.TrimSuffix(addr, ".")+"." {
addr = nodeAddress
}
return addr
}
func (d *DNSServer) encodeIPAsFqdn(dc string, ip net.IP) string {
ipv4 := ip.To4()
if ipv4 != nil {
@ -1460,7 +1442,16 @@ func makeARecord(qType uint16, ip net.IP, ttl time.Duration) dns.RR {
// In case of an SRV query the answer will be a IN SRV and additional data will store an IN A to the node IP
// Otherwise it will return a IN A record
func (d *DNSServer) makeRecordFromNode(dc string, node *structs.Node, qType uint16, qName string, ttl time.Duration, maxRecursionLevel int) []dns.RR {
addr := d.agent.TranslateAddress(node.Datacenter, node.Address, node.TaggedAddresses)
addrTranslate := TranslateAddressAcceptDomain
if qType == dns.TypeA {
addrTranslate |= TranslateAddressAcceptIPv4
} else if qType == dns.TypeAAAA {
addrTranslate |= TranslateAddressAcceptIPv6
} else {
addrTranslate |= TranslateAddressAcceptAny
}
addr := d.agent.TranslateAddress(node.Datacenter, node.Address, node.TaggedAddresses, addrTranslate)
ip := net.ParseIP(addr)
var res []dns.RR
@ -1621,8 +1612,20 @@ MORE_REC:
}
func (d *DNSServer) nodeServiceRecords(dc string, node structs.CheckServiceNode, req *dns.Msg, ttl time.Duration, cfg *dnsConfig, maxRecursionLevel int) ([]dns.RR, []dns.RR) {
serviceAddr := d.agent.TranslateServiceAddress(dc, node.Service.Address, node.Service.TaggedAddresses)
nodeAddr := d.agent.TranslateAddress(node.Node.Datacenter, node.Node.Address, node.Node.TaggedAddresses)
addrTranslate := TranslateAddressAcceptDomain
if req.Question[0].Qtype == dns.TypeA {
addrTranslate |= TranslateAddressAcceptIPv4
} else if req.Question[0].Qtype == dns.TypeAAAA {
addrTranslate |= TranslateAddressAcceptIPv6
} else {
addrTranslate |= TranslateAddressAcceptAny
}
serviceAddr := d.agent.TranslateServiceAddress(dc, node.Service.Address, node.Service.TaggedAddresses, addrTranslate)
nodeAddr := d.agent.TranslateAddress(node.Node.Datacenter, node.Node.Address, node.Node.TaggedAddresses, addrTranslate)
if serviceAddr == "" && nodeAddr == "" {
return nil, nil
}
nodeIPAddr := net.ParseIP(nodeAddr)
serviceIPAddr := net.ParseIP(serviceAddr)
@ -1685,7 +1688,7 @@ func (d *DNSServer) serviceSRVRecords(cfg *dnsConfig, dc string, nodes structs.C
for _, node := range nodes {
// Avoid duplicate entries, possible if a node has
// the same service the same port, etc.
serviceAddress := d.agent.TranslateServiceAddress(dc, node.Service.Address, node.Service.TaggedAddresses)
serviceAddress := d.agent.TranslateServiceAddress(dc, node.Service.Address, node.Service.TaggedAddresses, TranslateAddressAcceptAny)
servicePort := d.agent.TranslateServicePort(dc, node.Service.Port, node.Service.TaggedAddresses)
tuple := fmt.Sprintf("%s:%s:%d", node.Node.Node, serviceAddress, servicePort)
if _, ok := handled[tuple]; ok {

View File

@ -2518,6 +2518,196 @@ func TestDNS_ServiceLookup_WanTranslation(t *testing.T) {
}
}
func TestDNS_Lookup_TaggedIPAddresses(t *testing.T) {
t.Parallel()
a := NewTestAgent(t, t.Name(), "")
defer a.Shutdown()
testrpc.WaitForLeader(t, a.RPC, "dc1")
// Register an equivalent prepared query.
var id string
{
args := &structs.PreparedQueryRequest{
Datacenter: "dc1",
Op: structs.PreparedQueryCreate,
Query: &structs.PreparedQuery{
Name: "test",
Service: structs.ServiceQuery{
Service: "db",
},
},
}
require.NoError(t, a.RPC("PreparedQuery.Apply", args, &id))
}
type testCase struct {
nodeAddress string
nodeTaggedAddresses map[string]string
serviceAddress string
serviceTaggedAddresses map[string]structs.ServiceAddress
expectedServiceIPv4Address string
expectedServiceIPv6Address string
expectedNodeIPv4Address string
expectedNodeIPv6Address string
}
cases := map[string]testCase{
"simple-ipv4": testCase{
serviceAddress: "127.0.0.2",
nodeAddress: "127.0.0.1",
expectedServiceIPv4Address: "127.0.0.2",
expectedServiceIPv6Address: "",
expectedNodeIPv4Address: "127.0.0.1",
expectedNodeIPv6Address: "",
},
"simple-ipv6": testCase{
serviceAddress: "::2",
nodeAddress: "::1",
expectedServiceIPv6Address: "::2",
expectedServiceIPv4Address: "",
expectedNodeIPv6Address: "::1",
expectedNodeIPv4Address: "",
},
"ipv4-with-tagged-ipv6": testCase{
serviceAddress: "127.0.0.2",
nodeAddress: "127.0.0.1",
serviceTaggedAddresses: map[string]structs.ServiceAddress{
structs.TaggedAddressLANIPv6: {Address: "::2"},
},
nodeTaggedAddresses: map[string]string{
structs.TaggedAddressLANIPv6: "::1",
},
expectedServiceIPv4Address: "127.0.0.2",
expectedServiceIPv6Address: "::2",
expectedNodeIPv4Address: "127.0.0.1",
expectedNodeIPv6Address: "::1",
},
"ipv6-with-tagged-ipv4": testCase{
serviceAddress: "::2",
nodeAddress: "::1",
serviceTaggedAddresses: map[string]structs.ServiceAddress{
structs.TaggedAddressLANIPv4: {Address: "127.0.0.2"},
},
nodeTaggedAddresses: map[string]string{
structs.TaggedAddressLANIPv4: "127.0.0.1",
},
expectedServiceIPv4Address: "127.0.0.2",
expectedServiceIPv6Address: "::2",
expectedNodeIPv4Address: "127.0.0.1",
expectedNodeIPv6Address: "::1",
},
}
for name, tc := range cases {
name := name
tc := tc
t.Run(name, func(t *testing.T) {
args := &structs.RegisterRequest{
Datacenter: "dc1",
Node: "foo",
Address: tc.nodeAddress,
TaggedAddresses: tc.nodeTaggedAddresses,
Service: &structs.NodeService{
Service: "db",
Address: tc.serviceAddress,
Port: 8080,
TaggedAddresses: tc.serviceTaggedAddresses,
},
}
var out struct{}
require.NoError(t, a.RPC("Catalog.Register", args, &out))
// Look up the SRV record via service and prepared query.
questions := []string{
"db.service.consul.",
id + ".query.consul.",
}
for _, question := range questions {
m := new(dns.Msg)
m.SetQuestion(question, dns.TypeA)
c := new(dns.Client)
addr := a.config.DNSAddrs[0].String()
in, _, err := c.Exchange(m, addr)
require.NoError(t, err)
if tc.expectedServiceIPv4Address != "" {
require.Len(t, in.Answer, 1)
aRec, ok := in.Answer[0].(*dns.A)
require.True(t, ok, "Bad: %#v", in.Answer[0])
require.Equal(t, question, aRec.Hdr.Name)
require.Equal(t, tc.expectedServiceIPv4Address, aRec.A.String())
} else {
require.Len(t, in.Answer, 0)
}
m = new(dns.Msg)
m.SetQuestion(question, dns.TypeAAAA)
c = new(dns.Client)
addr = a.config.DNSAddrs[0].String()
in, _, err = c.Exchange(m, addr)
require.NoError(t, err)
if tc.expectedServiceIPv6Address != "" {
require.Len(t, in.Answer, 1)
aRec, ok := in.Answer[0].(*dns.AAAA)
require.True(t, ok, "Bad: %#v", in.Answer[0])
require.Equal(t, question, aRec.Hdr.Name)
require.Equal(t, tc.expectedServiceIPv6Address, aRec.AAAA.String())
} else {
require.Len(t, in.Answer, 0)
}
}
// Look up node
m := new(dns.Msg)
m.SetQuestion("foo.node.consul.", dns.TypeA)
c := new(dns.Client)
addr := a.config.DNSAddrs[0].String()
in, _, err := c.Exchange(m, addr)
require.NoError(t, err)
if tc.expectedNodeIPv4Address != "" {
require.Len(t, in.Answer, 1)
aRec, ok := in.Answer[0].(*dns.A)
require.True(t, ok, "Bad: %#v", in.Answer[0])
require.Equal(t, "foo.node.consul.", aRec.Hdr.Name)
require.Equal(t, tc.expectedNodeIPv4Address, aRec.A.String())
} else {
require.Len(t, in.Answer, 0)
}
m = new(dns.Msg)
m.SetQuestion("foo.node.consul.", dns.TypeAAAA)
c = new(dns.Client)
addr = a.config.DNSAddrs[0].String()
in, _, err = c.Exchange(m, addr)
require.NoError(t, err)
if tc.expectedNodeIPv6Address != "" {
require.Len(t, in.Answer, 1)
aRec, ok := in.Answer[0].(*dns.AAAA)
require.True(t, ok, "Bad: %#v", in.Answer[0])
require.Equal(t, "foo.node.consul.", aRec.Hdr.Name)
require.Equal(t, tc.expectedNodeIPv6Address, aRec.AAAA.String())
} else {
require.Len(t, in.Answer, 0)
}
})
}
}
func TestDNS_CaseInsensitiveServiceLookup(t *testing.T) {
t.Parallel()
a := NewTestAgent(t, t.Name(), "")

View File

@ -247,7 +247,7 @@ func (s *HTTPServer) healthServiceNodes(resp http.ResponseWriter, req *http.Requ
}
// Translate addresses after filtering so we don't waste effort.
s.agent.TranslateAddresses(args.Datacenter, out.Nodes)
s.agent.TranslateAddresses(args.Datacenter, out.Nodes, TranslateAddressAcceptAny)
// Use empty list instead of nil
if out.Nodes == nil {

View File

@ -163,7 +163,7 @@ func (s *HTTPServer) preparedQueryExecute(id string, resp http.ResponseWriter, r
// a query can fail over to a different DC than where the execute request
// was sent to. That's why we use the reply's DC and not the one from
// the args.
s.agent.TranslateAddresses(reply.Datacenter, reply.Nodes)
s.agent.TranslateAddresses(reply.Datacenter, reply.Nodes, TranslateAddressAcceptAny)
// Use empty list instead of nil.
if reply.Nodes == nil {

View File

@ -986,11 +986,11 @@ func testConfigSnapshotMeshGateway(t testing.T, populateServices bool) *ConfigSn
Config: map[string]interface{}{},
},
TaggedAddresses: map[string]structs.ServiceAddress{
"lan": structs.ServiceAddress{
structs.TaggedAddressLAN: structs.ServiceAddress{
Address: "1.2.3.4",
Port: 8443,
},
"wan": structs.ServiceAddress{
structs.TaggedAddressWAN: structs.ServiceAddress{
Address: "198.18.0.1",
Port: 443,
},

View File

@ -50,9 +50,10 @@ func TestServiceManager_RegisterService(t *testing.T) {
redisService := a.State.Service(structs.NewServiceID("redis", nil))
require.NotNil(redisService)
require.Equal(&structs.NodeService{
ID: "redis",
Service: "redis",
Port: 8000,
ID: "redis",
Service: "redis",
Port: 8000,
TaggedAddresses: map[string]structs.ServiceAddress{},
Weights: &structs.Weights{
Passing: 1,
Warning: 1,
@ -116,10 +117,11 @@ func TestServiceManager_RegisterSidecar(t *testing.T) {
sidecarService := a.State.Service(structs.NewServiceID("web-sidecar-proxy", nil))
require.NotNil(sidecarService)
require.Equal(&structs.NodeService{
Kind: structs.ServiceKindConnectProxy,
ID: "web-sidecar-proxy",
Service: "web-sidecar-proxy",
Port: 21000,
Kind: structs.ServiceKindConnectProxy,
ID: "web-sidecar-proxy",
Service: "web-sidecar-proxy",
Port: 21000,
TaggedAddresses: map[string]structs.ServiceAddress{},
Proxy: structs.ConnectProxyConfig{
DestinationServiceName: "web",
DestinationServiceID: "web",
@ -184,10 +186,11 @@ func TestServiceManager_RegisterMeshGateway(t *testing.T) {
gateway := a.State.Service(structs.NewServiceID("mesh-gateway", nil))
require.NotNil(gateway)
require.Equal(&structs.NodeService{
Kind: structs.ServiceKindMeshGateway,
ID: "mesh-gateway",
Service: "mesh-gateway",
Port: 443,
Kind: structs.ServiceKindMeshGateway,
ID: "mesh-gateway",
Service: "mesh-gateway",
Port: 443,
TaggedAddresses: map[string]structs.ServiceAddress{},
Proxy: structs.ConnectProxyConfig{
Config: map[string]interface{}{
"foo": int64(1),
@ -276,10 +279,11 @@ func TestServiceManager_PersistService_API(t *testing.T) {
}
expectState := &structs.NodeService{
Kind: structs.ServiceKindConnectProxy,
ID: "web-sidecar-proxy",
Service: "web-sidecar-proxy",
Port: 21000,
Kind: structs.ServiceKindConnectProxy,
ID: "web-sidecar-proxy",
Service: "web-sidecar-proxy",
Port: 21000,
TaggedAddresses: map[string]structs.ServiceAddress{},
Proxy: structs.ConnectProxyConfig{
DestinationServiceName: "web",
DestinationServiceID: "web",
@ -487,10 +491,11 @@ func TestServiceManager_PersistService_ConfigFiles(t *testing.T) {
svcID := "web-sidecar-proxy"
expectState := &structs.NodeService{
Kind: structs.ServiceKindConnectProxy,
ID: "web-sidecar-proxy",
Service: "web-sidecar-proxy",
Port: 21000,
Kind: structs.ServiceKindConnectProxy,
ID: "web-sidecar-proxy",
Service: "web-sidecar-proxy",
Port: 21000,
TaggedAddresses: map[string]structs.ServiceAddress{},
Proxy: structs.ConnectProxyConfig{
DestinationServiceName: "web",
DestinationServiceID: "web",
@ -637,10 +642,11 @@ func TestServiceManager_Disabled(t *testing.T) {
sidecarService := a.State.Service(structs.NewServiceID("web-sidecar-proxy", nil))
require.NotNil(sidecarService)
require.Equal(&structs.NodeService{
Kind: structs.ServiceKindConnectProxy,
ID: "web-sidecar-proxy",
Service: "web-sidecar-proxy",
Port: 21000,
Kind: structs.ServiceKindConnectProxy,
ID: "web-sidecar-proxy",
Service: "web-sidecar-proxy",
Port: 21000,
TaggedAddresses: map[string]structs.ServiceAddress{},
Proxy: structs.ConnectProxyConfig{
DestinationServiceName: "web",
DestinationServiceID: "web",

View File

@ -119,6 +119,15 @@ var (
NodeMaintCheckID = NewCheckID(NodeMaint, nil)
)
const (
TaggedAddressWAN = "wan"
TaggedAddressWANIPv4 = "wan_ipv4"
TaggedAddressWANIPv6 = "wan_ipv6"
TaggedAddressLAN = "lan"
TaggedAddressLANIPv4 = "lan_ipv4"
TaggedAddressLANIPv6 = "lan_ipv6"
)
// metaKeyFormat checks if a metadata key string is valid
var metaKeyFormat = regexp.MustCompile(`^[a-zA-Z0-9_-]+$`).MatchString
@ -612,7 +621,7 @@ type Node struct {
func (n *Node) BestAddress(wan bool) string {
if wan {
if addr, ok := n.TaggedAddresses["wan"]; ok {
if addr, ok := n.TaggedAddresses[TaggedAddressWAN]; ok {
return addr
}
}
@ -919,7 +928,7 @@ func (ns *NodeService) BestAddress(wan bool) (string, int) {
port := ns.Port
if wan {
if wan, ok := ns.TaggedAddresses["wan"]; ok {
if wan, ok := ns.TaggedAddresses[TaggedAddressWAN]; ok {
addr = wan.Address
if wan.Port != 0 {
port = wan.Port

View File

@ -97,8 +97,8 @@ func TestNodeServiceMeshGatewayWithAddrs(t testing.T, address string, port int,
},
},
TaggedAddresses: map[string]ServiceAddress{
"lan": lanAddr,
"wan": wanAddr,
TaggedAddressLAN: lanAddr,
TaggedAddressWAN: wanAddr,
},
}
}

View File

@ -2,16 +2,27 @@ package agent
import (
"fmt"
"net"
"github.com/hashicorp/consul/agent/structs"
)
type TranslateAddressAccept int
const (
TranslateAddressAcceptDomain TranslateAddressAccept = 1 << iota
TranslateAddressAcceptIPv4
TranslateAddressAcceptIPv6
TranslateAddressAcceptAny TranslateAddressAccept = ^0
)
// TranslateServicePort is used to provide the final, translated port for a service,
// depending on how the agent and the other node are configured. The dc
// parameter is the dc the datacenter this node is from.
func (a *Agent) TranslateServicePort(dc string, port int, taggedAddresses map[string]structs.ServiceAddress) int {
if a.config.TranslateWANAddrs && (a.config.Datacenter != dc) {
if wanAddr, ok := taggedAddresses["wan"]; ok && wanAddr.Port != 0 {
if wanAddr, ok := taggedAddresses[structs.TaggedAddressWAN]; ok && wanAddr.Port != 0 {
return wanAddr.Port
}
}
@ -21,32 +32,78 @@ func (a *Agent) TranslateServicePort(dc string, port int, taggedAddresses map[st
// TranslateServiceAddress is used to provide the final, translated address for a node,
// depending on how the agent and the other node are configured. The dc
// parameter is the dc the datacenter this node is from.
func (a *Agent) TranslateServiceAddress(dc string, addr string, taggedAddresses map[string]structs.ServiceAddress) string {
if a.config.TranslateWANAddrs && (a.config.Datacenter != dc) {
if wanAddr, ok := taggedAddresses["wan"]; ok && wanAddr.Address != "" {
return wanAddr.Address
func (a *Agent) TranslateServiceAddress(dc string, addr string, taggedAddresses map[string]structs.ServiceAddress, accept TranslateAddressAccept) string {
def := addr
v4 := taggedAddresses[structs.TaggedAddressLANIPv4].Address
v6 := taggedAddresses[structs.TaggedAddressLANIPv6].Address
shouldUseWan := a.config.TranslateWANAddrs && (a.config.Datacenter != dc)
if shouldUseWan {
if v, ok := taggedAddresses[structs.TaggedAddressWAN]; ok {
def = v.Address
}
if v, ok := taggedAddresses[structs.TaggedAddressWANIPv4]; ok {
v4 = v.Address
}
if v, ok := taggedAddresses[structs.TaggedAddressWANIPv6]; ok {
v6 = v.Address
}
}
return addr
return translateAddressAccept(accept, def, v4, v6)
}
// TranslateAddress is used to provide the final, translated address for a node,
// depending on how the agent and the other node are configured. The dc
// parameter is the dc the datacenter this node is from.
func (a *Agent) TranslateAddress(dc string, addr string, taggedAddresses map[string]string) string {
if a.config.TranslateWANAddrs && (a.config.Datacenter != dc) {
wanAddr := taggedAddresses["wan"]
if wanAddr != "" {
addr = wanAddr
func (a *Agent) TranslateAddress(dc string, addr string, taggedAddresses map[string]string, accept TranslateAddressAccept) string {
def := addr
v4 := taggedAddresses[structs.TaggedAddressLANIPv4]
v6 := taggedAddresses[structs.TaggedAddressLANIPv6]
shouldUseWan := a.config.TranslateWANAddrs && (a.config.Datacenter != dc)
if shouldUseWan {
if v, ok := taggedAddresses[structs.TaggedAddressWAN]; ok {
def = v
}
if v, ok := taggedAddresses[structs.TaggedAddressWANIPv4]; ok {
v4 = v
}
if v, ok := taggedAddresses[structs.TaggedAddressWANIPv6]; ok {
v6 = v
}
}
return addr
return translateAddressAccept(accept, def, v4, v6)
}
func translateAddressAccept(accept TranslateAddressAccept, def, v4, v6 string) string {
switch {
case accept&TranslateAddressAcceptIPv6 > 0 && v6 != "":
return v6
case accept&TranslateAddressAcceptIPv4 > 0 && v4 != "":
return v4
case accept&TranslateAddressAcceptAny > 0 && def != "":
return def
default:
defIP := net.ParseIP(def)
switch {
case defIP != nil && defIP.To4() != nil && accept&TranslateAddressAcceptIPv4 > 0:
return def
case defIP != nil && defIP.To4() == nil && accept&TranslateAddressAcceptIPv6 > 0:
return def
case defIP == nil && accept&TranslateAddressAcceptDomain > 0:
return def
}
}
return ""
}
// TranslateAddresses translates addresses in the given structure into the
// final, translated address, depending on how the agent and the other node are
// configured. The dc parameter is the datacenter this structure is from.
func (a *Agent) TranslateAddresses(dc string, subj interface{}) {
func (a *Agent) TranslateAddresses(dc string, subj interface{}, accept TranslateAddressAccept) {
// CAUTION - SUBTLE! An agent running on a server can, in some cases,
// return pointers directly into the immutable state store for
// performance (it's via the in-memory RPC mechanism). It's never safe
@ -68,28 +125,28 @@ func (a *Agent) TranslateAddresses(dc string, subj interface{}) {
switch v := subj.(type) {
case structs.CheckServiceNodes:
for _, entry := range v {
entry.Node.Address = a.TranslateAddress(dc, entry.Node.Address, entry.Node.TaggedAddresses)
entry.Service.Address = a.TranslateServiceAddress(dc, entry.Service.Address, entry.Service.TaggedAddresses)
entry.Node.Address = a.TranslateAddress(dc, entry.Node.Address, entry.Node.TaggedAddresses, accept)
entry.Service.Address = a.TranslateServiceAddress(dc, entry.Service.Address, entry.Service.TaggedAddresses, accept)
entry.Service.Port = a.TranslateServicePort(dc, entry.Service.Port, entry.Service.TaggedAddresses)
}
case *structs.Node:
v.Address = a.TranslateAddress(dc, v.Address, v.TaggedAddresses)
v.Address = a.TranslateAddress(dc, v.Address, v.TaggedAddresses, accept)
case structs.Nodes:
for _, node := range v {
node.Address = a.TranslateAddress(dc, node.Address, node.TaggedAddresses)
node.Address = a.TranslateAddress(dc, node.Address, node.TaggedAddresses, accept)
}
case structs.ServiceNodes:
for _, entry := range v {
entry.Address = a.TranslateAddress(dc, entry.Address, entry.TaggedAddresses)
entry.ServiceAddress = a.TranslateServiceAddress(dc, entry.ServiceAddress, entry.ServiceTaggedAddresses)
entry.Address = a.TranslateAddress(dc, entry.Address, entry.TaggedAddresses, accept)
entry.ServiceAddress = a.TranslateServiceAddress(dc, entry.ServiceAddress, entry.ServiceTaggedAddresses, accept)
entry.ServicePort = a.TranslateServicePort(dc, entry.ServicePort, entry.ServiceTaggedAddresses)
}
case *structs.NodeServices:
if v.Node != nil {
v.Node.Address = a.TranslateAddress(dc, v.Node.Address, v.Node.TaggedAddresses)
v.Node.Address = a.TranslateAddress(dc, v.Node.Address, v.Node.TaggedAddresses, accept)
}
for _, entry := range v.Services {
entry.Address = a.TranslateServiceAddress(dc, entry.Address, entry.TaggedAddresses)
entry.Address = a.TranslateServiceAddress(dc, entry.Address, entry.TaggedAddresses, accept)
entry.Port = a.TranslateServicePort(dc, entry.Port, entry.TaggedAddresses)
}
default:

View File

@ -52,8 +52,10 @@ func TestAPI_CatalogNodes(t *testing.T) {
Address: "127.0.0.1",
Datacenter: "dc1",
TaggedAddresses: map[string]string{
"lan": "127.0.0.1",
"wan": "127.0.0.1",
"lan": "127.0.0.1",
"lan_ipv4": "127.0.0.1",
"wan": "127.0.0.1",
"wan_ipv4": "127.0.0.1",
},
Meta: map[string]string{
"consul-network-segment": "",

View File

@ -269,8 +269,10 @@ func TestAPI_ClientTxn(t *testing.T) {
Address: "127.0.0.1",
Datacenter: "dc1",
TaggedAddresses: map[string]string{
"lan": s.Config.Bind,
"wan": s.Config.Bind,
"lan": s.Config.Bind,
"lan_ipv4": s.Config.Bind,
"wan": s.Config.Bind,
"wan_ipv4": s.Config.Bind,
},
Meta: map[string]string{"consul-network-segment": ""},
CreateIndex: ret.Results[1].Node.CreateIndex,

View File

@ -13,6 +13,7 @@ import (
"github.com/mitchellh/mapstructure"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/agent/xds"
"github.com/hashicorp/consul/api"
proxyCmd "github.com/hashicorp/consul/command/connect/proxy"
@ -244,7 +245,7 @@ func (c *cmd) Run(args []string) int {
taggedAddrs := make(map[string]api.ServiceAddress)
if lanAddr != "" {
taggedAddrs["lan"] = api.ServiceAddress{Address: lanAddr, Port: lanPort}
taggedAddrs[structs.TaggedAddressLAN] = api.ServiceAddress{Address: lanAddr, Port: lanPort}
}
wanAddr := ""
@ -255,7 +256,7 @@ func (c *cmd) Run(args []string) int {
c.UI.Error(fmt.Sprintf("Failed to parse the -wan-address parameter: %v", err))
return 1
}
taggedAddrs["wan"] = api.ServiceAddress{Address: wanAddr, Port: wanPort}
taggedAddrs[structs.TaggedAddressWAN] = api.ServiceAddress{Address: wanAddr, Port: wanPort}
}
tcpCheckAddr := lanAddr