Skip to content

Commit 4a4c63b

Browse files
pierresouchaymkeeler
authored andcommitted
Ensure Consul is IPv6 compliant (hashicorp#5468)
1 parent 2ba6c3a commit 4a4c63b

File tree

10 files changed

+205
-113
lines changed

10 files changed

+205
-113
lines changed

agent/agent.go

+6-3
Original file line numberDiff line numberDiff line change
@@ -2537,11 +2537,14 @@ func (a *Agent) addProxyLocked(proxy *structs.ConnectManagedProxy, persist, From
25372537
chkAddr := a.resolveProxyCheckAddress(proxyCfg)
25382538
chkTypes := []*structs.CheckType{}
25392539
if chkAddr != "" {
2540+
bindPort, ok := proxyCfg["bind_port"].(int)
2541+
if !ok {
2542+
return fmt.Errorf("Cannot convert bind_port=%v to an int for creating TCP Check for address %s", proxyCfg["bind_port"], chkAddr)
2543+
}
25402544
chkTypes = []*structs.CheckType{
25412545
&structs.CheckType{
2542-
Name: "Connect Proxy Listening",
2543-
TCP: fmt.Sprintf("%s:%d", chkAddr,
2544-
proxyCfg["bind_port"]),
2546+
Name: "Connect Proxy Listening",
2547+
TCP: ipaddr.FormatAddressPort(chkAddr, bindPort),
25452548
Interval: 10 * time.Second,
25462549
},
25472550
}

agent/agent_endpoint_test.go

+123-102
Original file line numberDiff line numberDiff line change
@@ -2479,17 +2479,26 @@ func TestAgent_RegisterService(t *testing.T) {
24792479

24802480
func TestAgent_RegisterService_TranslateKeys(t *testing.T) {
24812481
t.Parallel()
2482-
a := NewTestAgent(t, t.Name(), `
2482+
tests := []struct {
2483+
ip string
2484+
expectedTCPCheckStart string
2485+
}{
2486+
{"127.0.0.1", "127.0.0.1:"}, // private network address
2487+
{"::1", "[::1]:"}, // shared address space
2488+
}
2489+
for _, tt := range tests {
2490+
t.Run(tt.ip, func(t *testing.T) {
2491+
a := NewTestAgent(t, t.Name(), `
24832492
connect {
24842493
proxy {
24852494
allow_managed_api_registration = true
24862495
}
24872496
}
24882497
`)
2489-
defer a.Shutdown()
2490-
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
2498+
defer a.Shutdown()
2499+
testrpc.WaitForTestAgent(t, a.RPC, "dc1")
24912500

2492-
json := `
2501+
json := `
24932502
{
24942503
"name":"test",
24952504
"port":8000,
@@ -2499,14 +2508,14 @@ func TestAgent_RegisterService_TranslateKeys(t *testing.T) {
24992508
"enable_tag_override": "meta is 'opaque' so should not get translated"
25002509
},
25012510
"kind": "connect-proxy",` +
2502-
// Note the uppercase P is important here - it ensures translation works
2503-
// correctly in case-insensitive way. Without it this test can pass even
2504-
// when translation is broken for other valid inputs.
2505-
`"Proxy": {
2511+
// Note the uppercase P is important here - it ensures translation works
2512+
// correctly in case-insensitive way. Without it this test can pass even
2513+
// when translation is broken for other valid inputs.
2514+
`"Proxy": {
25062515
"destination_service_name": "web",
25072516
"destination_service_id": "web",
25082517
"local_service_port": 1234,
2509-
"local_service_address": "127.0.0.1",
2518+
"local_service_address": "` + tt.ip + `",
25102519
"config": {
25112520
"destination_type": "proxy.config is 'opaque' so should not get translated"
25122521
},
@@ -2515,7 +2524,7 @@ func TestAgent_RegisterService_TranslateKeys(t *testing.T) {
25152524
"destination_type": "service",
25162525
"destination_namespace": "default",
25172526
"destination_name": "db",
2518-
"local_bind_address": "127.0.0.1",
2527+
"local_bind_address": "` + tt.ip + `",
25192528
"local_bind_port": 1234,
25202529
"config": {
25212530
"destination_type": "proxy.upstreams.config is 'opaque' so should not get translated"
@@ -2534,7 +2543,7 @@ func TestAgent_RegisterService_TranslateKeys(t *testing.T) {
25342543
"destination_type": "service",
25352544
"destination_namespace": "default",
25362545
"destination_name": "db",
2537-
"local_bind_address": "127.0.0.1",
2546+
"local_bind_address": "` + tt.ip + `",
25382547
"local_bind_port": 1234,
25392548
"config": {
25402549
"destination_type": "connect.proxy.upstreams.config is 'opaque' so should not get translated"
@@ -2555,13 +2564,13 @@ func TestAgent_RegisterService_TranslateKeys(t *testing.T) {
25552564
"destination_service_name": "test",
25562565
"destination_service_id": "test",
25572566
"local_service_port": 4321,
2558-
"local_service_address": "127.0.0.1",
2567+
"local_service_address": "` + tt.ip + `",
25592568
"upstreams": [
25602569
{
25612570
"destination_type": "service",
25622571
"destination_namespace": "default",
25632572
"destination_name": "db",
2564-
"local_bind_address": "127.0.0.1",
2573+
"local_bind_address": "` + tt.ip + `",
25652574
"local_bind_port": 1234,
25662575
"config": {
25672576
"destination_type": "sidecar_service.proxy.upstreams.config is 'opaque' so should not get translated"
@@ -2575,109 +2584,121 @@ func TestAgent_RegisterService_TranslateKeys(t *testing.T) {
25752584
"passing": 16
25762585
}
25772586
}`
2578-
req, _ := http.NewRequest("PUT", "/v1/agent/service/register", strings.NewReader(json))
2587+
req, _ := http.NewRequest("PUT", "/v1/agent/service/register", strings.NewReader(json))
25792588

2580-
rr := httptest.NewRecorder()
2581-
obj, err := a.srv.AgentRegisterService(rr, req)
2582-
require.NoError(t, err)
2583-
require.Nil(t, obj)
2584-
require.Equal(t, 200, rr.Code, "body: %s", rr.Body)
2589+
rr := httptest.NewRecorder()
2590+
obj, err := a.srv.AgentRegisterService(rr, req)
2591+
require.NoError(t, err)
2592+
require.Nil(t, obj)
2593+
require.Equal(t, 200, rr.Code, "body: %s", rr.Body)
25852594

2586-
svc := &structs.NodeService{
2587-
ID: "test",
2588-
Service: "test",
2589-
Meta: map[string]string{
2590-
"some": "meta",
2591-
"enable_tag_override": "meta is 'opaque' so should not get translated",
2592-
},
2593-
Port: 8000,
2594-
EnableTagOverride: true,
2595-
Weights: &structs.Weights{Passing: 16, Warning: 0},
2596-
Kind: structs.ServiceKindConnectProxy,
2597-
Proxy: structs.ConnectProxyConfig{
2598-
DestinationServiceName: "web",
2599-
DestinationServiceID: "web",
2600-
LocalServiceAddress: "127.0.0.1",
2601-
LocalServicePort: 1234,
2602-
Config: map[string]interface{}{
2603-
"destination_type": "proxy.config is 'opaque' so should not get translated",
2604-
},
2605-
Upstreams: structs.Upstreams{
2606-
{
2607-
DestinationType: structs.UpstreamDestTypeService,
2608-
DestinationName: "db",
2609-
DestinationNamespace: "default",
2610-
LocalBindAddress: "127.0.0.1",
2611-
LocalBindPort: 1234,
2595+
svc := &structs.NodeService{
2596+
ID: "test",
2597+
Service: "test",
2598+
Meta: map[string]string{
2599+
"some": "meta",
2600+
"enable_tag_override": "meta is 'opaque' so should not get translated",
2601+
},
2602+
Port: 8000,
2603+
EnableTagOverride: true,
2604+
Weights: &structs.Weights{Passing: 16, Warning: 0},
2605+
Kind: structs.ServiceKindConnectProxy,
2606+
Proxy: structs.ConnectProxyConfig{
2607+
DestinationServiceName: "web",
2608+
DestinationServiceID: "web",
2609+
LocalServiceAddress: tt.ip,
2610+
LocalServicePort: 1234,
26122611
Config: map[string]interface{}{
2613-
"destination_type": "proxy.upstreams.config is 'opaque' so should not get translated",
2612+
"destination_type": "proxy.config is 'opaque' so should not get translated",
2613+
},
2614+
Upstreams: structs.Upstreams{
2615+
{
2616+
DestinationType: structs.UpstreamDestTypeService,
2617+
DestinationName: "db",
2618+
DestinationNamespace: "default",
2619+
LocalBindAddress: tt.ip,
2620+
LocalBindPort: 1234,
2621+
Config: map[string]interface{}{
2622+
"destination_type": "proxy.upstreams.config is 'opaque' so should not get translated",
2623+
},
2624+
},
26142625
},
26152626
},
2616-
},
2617-
},
2618-
Connect: structs.ServiceConnect{
2619-
Proxy: &structs.ServiceDefinitionConnectProxy{
2620-
ExecMode: "script",
2621-
Config: map[string]interface{}{
2622-
"destination_type": "connect.proxy.config is 'opaque' so should not get translated",
2623-
},
2624-
Upstreams: structs.Upstreams{
2625-
{
2626-
DestinationType: structs.UpstreamDestTypeService,
2627-
DestinationName: "db",
2628-
DestinationNamespace: "default",
2629-
LocalBindAddress: "127.0.0.1",
2630-
LocalBindPort: 1234,
2627+
Connect: structs.ServiceConnect{
2628+
Proxy: &structs.ServiceDefinitionConnectProxy{
2629+
ExecMode: "script",
26312630
Config: map[string]interface{}{
2632-
"destination_type": "connect.proxy.upstreams.config is 'opaque' so should not get translated",
2631+
"destination_type": "connect.proxy.config is 'opaque' so should not get translated",
2632+
},
2633+
Upstreams: structs.Upstreams{
2634+
{
2635+
DestinationType: structs.UpstreamDestTypeService,
2636+
DestinationName: "db",
2637+
DestinationNamespace: "default",
2638+
LocalBindAddress: tt.ip,
2639+
LocalBindPort: 1234,
2640+
Config: map[string]interface{}{
2641+
"destination_type": "connect.proxy.upstreams.config is 'opaque' so should not get translated",
2642+
},
2643+
},
26332644
},
26342645
},
2646+
// The sidecar service is nilled since it is only config sugar and
2647+
// shouldn't be represented in state. We assert that the translations
2648+
// there worked by inspecting the registered sidecar below.
2649+
SidecarService: nil,
26352650
},
2636-
},
2637-
// The sidecar service is nilled since it is only config sugar and
2638-
// shouldn't be represented in state. We assert that the translations
2639-
// there worked by inspecting the registered sidecar below.
2640-
SidecarService: nil,
2641-
},
2642-
}
2651+
}
26432652

2644-
got := a.State.Service("test")
2645-
require.Equal(t, svc, got)
2653+
got := a.State.Service("test")
2654+
require.Equal(t, svc, got)
26462655

2647-
sidecarSvc := &structs.NodeService{
2648-
Kind: structs.ServiceKindConnectProxy,
2649-
ID: "test-sidecar-proxy",
2650-
Service: "test-proxy",
2651-
Meta: map[string]string{
2652-
"some": "meta",
2653-
"enable_tag_override": "sidecar_service.meta is 'opaque' so should not get translated",
2654-
},
2655-
Port: 8001,
2656-
EnableTagOverride: true,
2657-
Weights: &structs.Weights{Passing: 1, Warning: 1},
2658-
LocallyRegisteredAsSidecar: true,
2659-
Proxy: structs.ConnectProxyConfig{
2660-
DestinationServiceName: "test",
2661-
DestinationServiceID: "test",
2662-
LocalServiceAddress: "127.0.0.1",
2663-
LocalServicePort: 4321,
2664-
Upstreams: structs.Upstreams{
2665-
{
2666-
DestinationType: structs.UpstreamDestTypeService,
2667-
DestinationName: "db",
2668-
DestinationNamespace: "default",
2669-
LocalBindAddress: "127.0.0.1",
2670-
LocalBindPort: 1234,
2671-
Config: map[string]interface{}{
2672-
"destination_type": "sidecar_service.proxy.upstreams.config is 'opaque' so should not get translated",
2656+
sidecarSvc := &structs.NodeService{
2657+
Kind: structs.ServiceKindConnectProxy,
2658+
ID: "test-sidecar-proxy",
2659+
Service: "test-proxy",
2660+
Meta: map[string]string{
2661+
"some": "meta",
2662+
"enable_tag_override": "sidecar_service.meta is 'opaque' so should not get translated",
2663+
},
2664+
Port: 8001,
2665+
EnableTagOverride: true,
2666+
Weights: &structs.Weights{Passing: 1, Warning: 1},
2667+
LocallyRegisteredAsSidecar: true,
2668+
Proxy: structs.ConnectProxyConfig{
2669+
DestinationServiceName: "test",
2670+
DestinationServiceID: "test",
2671+
LocalServiceAddress: tt.ip,
2672+
LocalServicePort: 4321,
2673+
Upstreams: structs.Upstreams{
2674+
{
2675+
DestinationType: structs.UpstreamDestTypeService,
2676+
DestinationName: "db",
2677+
DestinationNamespace: "default",
2678+
LocalBindAddress: tt.ip,
2679+
LocalBindPort: 1234,
2680+
Config: map[string]interface{}{
2681+
"destination_type": "sidecar_service.proxy.upstreams.config is 'opaque' so should not get translated",
2682+
},
2683+
},
26732684
},
26742685
},
2675-
},
2676-
},
2686+
}
2687+
gotSidecar := a.State.Service("test-sidecar-proxy")
2688+
hasNoCorrectTCPCheck := true
2689+
for _, v := range a.checkTCPs {
2690+
if strings.HasPrefix(v.TCP, tt.expectedTCPCheckStart) {
2691+
hasNoCorrectTCPCheck = false
2692+
break
2693+
}
2694+
fmt.Println("TCP Check:= ", v)
2695+
}
2696+
if hasNoCorrectTCPCheck {
2697+
t.Fatalf("Did not find the expected TCP Healtcheck '%s' in %#v ", tt.expectedTCPCheckStart, a.checkTCPs)
2698+
}
2699+
require.Equal(t, sidecarSvc, gotSidecar)
2700+
})
26772701
}
2678-
2679-
gotSidecar := a.State.Service("test-sidecar-proxy")
2680-
require.Equal(t, sidecarSvc, gotSidecar)
26812702
}
26822703

26832704
func TestAgent_RegisterService_ACLDeny(t *testing.T) {

agent/dns.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/hashicorp/consul/agent/consul"
2020
"github.com/hashicorp/consul/agent/structs"
2121
"github.com/hashicorp/consul/api"
22+
"github.com/hashicorp/consul/ipaddr"
2223
"github.com/hashicorp/consul/lib"
2324
"github.com/miekg/dns"
2425
)
@@ -264,7 +265,7 @@ func recursorAddr(recursor string) (string, error) {
264265
START:
265266
_, _, err := net.SplitHostPort(recursor)
266267
if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
267-
recursor = fmt.Sprintf("%s:%d", recursor, 53)
268+
recursor = ipaddr.FormatAddressPort(recursor, 53)
268269
goto START
269270
}
270271
if err != nil {

agent/sidecar_service.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"fmt"
55
"time"
66

7+
"github.com/hashicorp/consul/ipaddr"
8+
79
"github.com/hashicorp/consul/agent/structs"
810
)
911

@@ -171,7 +173,7 @@ func (a *Agent) sidecarServiceFromNodeService(ns *structs.NodeService, token str
171173
Name: "Connect Sidecar Listening",
172174
// Default to localhost rather than agent/service public IP. The checks
173175
// can always be overridden if a non-loopback IP is needed.
174-
TCP: fmt.Sprintf("127.0.0.1:%d", sidecar.Port),
176+
TCP: ipaddr.FormatAddressPort(sidecar.Proxy.LocalServiceAddress, sidecar.Port),
175177
Interval: 10 * time.Second,
176178
},
177179
&structs.CheckType{

connect/proxy/config.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/hashicorp/consul/api"
1111
"github.com/hashicorp/consul/api/watch"
1212
"github.com/hashicorp/consul/connect"
13+
"github.com/hashicorp/consul/ipaddr"
1314
"github.com/hashicorp/consul/lib"
1415
)
1516

@@ -242,7 +243,7 @@ func (w *AgentConfigWatcher) handler(blockVal watch.BlockingParamVal,
242243
}
243244
cfg.PublicListener.BindAddress = resp.Address
244245
cfg.PublicListener.BindPort = resp.Port
245-
cfg.PublicListener.LocalServiceAddress = fmt.Sprintf("%s:%d",
246+
cfg.PublicListener.LocalServiceAddress = ipaddr.FormatAddressPort(
246247
resp.Proxy.LocalServiceAddress, resp.Proxy.LocalServicePort)
247248

248249
cfg.PublicListener.applyDefaults()

connect/proxy/listener.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"crypto/tls"
66
"errors"
7-
"fmt"
87
"log"
98
"net"
109
"sync"
@@ -14,6 +13,7 @@ import (
1413
metrics "github.com/armon/go-metrics"
1514
"github.com/hashicorp/consul/api"
1615
"github.com/hashicorp/consul/connect"
16+
"github.com/hashicorp/consul/ipaddr"
1717
)
1818

1919
const (
@@ -58,7 +58,7 @@ type Listener struct {
5858
// connections and proxy them to the configured local application over TCP.
5959
func NewPublicListener(svc *connect.Service, cfg PublicListenerConfig,
6060
logger *log.Logger) *Listener {
61-
bindAddr := fmt.Sprintf("%s:%d", cfg.BindAddress, cfg.BindPort)
61+
bindAddr := ipaddr.FormatAddressPort(cfg.BindAddress, cfg.BindPort)
6262
return &Listener{
6363
Service: svc,
6464
listenFunc: func() (net.Listener, error) {
@@ -96,7 +96,7 @@ func NewUpstreamListener(svc *connect.Service, client *api.Client,
9696
func newUpstreamListenerWithResolver(svc *connect.Service, cfg UpstreamConfig,
9797
resolverFunc func(UpstreamConfig) (connect.Resolver, error),
9898
logger *log.Logger) *Listener {
99-
bindAddr := fmt.Sprintf("%s:%d", cfg.LocalBindAddress, cfg.LocalBindPort)
99+
bindAddr := ipaddr.FormatAddressPort(cfg.LocalBindAddress, cfg.LocalBindPort)
100100
return &Listener{
101101
Service: svc,
102102
listenFunc: func() (net.Listener, error) {

0 commit comments

Comments
 (0)