Skip to content

Commit

Permalink
fingerprint: add DNS address and port to Consul fingerprint
Browse files Browse the repository at this point in the history
In order to provide a DNS address and port to Connect tasks configured for
transparent proxy, we need to fingerprint the Consul DNS address and port. The
client will pass this address/port to the iptables configuration provided to the
`consul-cni` plugin.

Ref: #10628
  • Loading branch information
tgross committed Feb 13, 2024
1 parent c364cb5 commit 8f20afc
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .changelog/19969.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
fingerprint: Added a fingerprint for Consul DNS address and port
```
61 changes: 60 additions & 1 deletion client/fingerprint/consul.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package fingerprint

import (
"fmt"
"net/netip"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -165,6 +166,8 @@ func (cfs *consulFingerprintState) initialize(cfg *config.ConsulConfig, logger h
"consul.grpc": cfs.grpc(consulConfig.Scheme, logger),
"consul.ft.namespaces": cfs.namespaces,
"consul.partition": cfs.partition,
"consul.dns_port": cfs.dnsPort,
"consul.dns_addr": cfs.dnsAddr(logger),
}
} else {
cfs.extractors = map[string]consulExtractor{
Expand All @@ -178,6 +181,8 @@ func (cfs *consulFingerprintState) initialize(cfg *config.ConsulConfig, logger h
fmt.Sprintf("consul.%s.grpc", cfg.Name): cfs.grpc(consulConfig.Scheme, logger),
fmt.Sprintf("consul.%s.ft.namespaces", cfg.Name): cfs.namespaces,
fmt.Sprintf("consul.%s.partition", cfg.Name): cfs.partition,
fmt.Sprintf("consul.%s.dns_port", cfg.Name): cfs.dnsPort,
fmt.Sprintf("consul.%s.dns_addr", cfg.Name): cfs.dnsAddr(logger),
}
}

Expand All @@ -191,7 +196,7 @@ func (cfs *consulFingerprintState) query(logger hclog.Logger) agentconsul.Self {
if err != nil {
// indicate consul no longer available
if cfs.isAvailable {
logger.Info("consul agent is unavailable: %v", err)
logger.Info("consul agent is unavailable", "error", err)
}
cfs.isAvailable = false
cfs.nextCheck = time.Time{} // force check on next interval
Expand Down Expand Up @@ -298,6 +303,60 @@ func (cfs *consulFingerprintState) grpcTLSPort(info agentconsul.Self) (string, b
return fmt.Sprintf("%d", int(p)), ok
}

func (cfs *consulFingerprintState) dnsPort(info agentconsul.Self) (string, bool) {
p, ok := info["DebugConfig"]["DNSPort"].(float64)
return fmt.Sprintf("%d", int(p)), ok
}

// dnsAddr fingerprints the Consul DNS address, but only if Nomad can use it
// usefully to provide an iptables rule to a task
func (cfs *consulFingerprintState) dnsAddr(logger hclog.Logger) func(info agentconsul.Self) (string, bool) {
return func(info agentconsul.Self) (string, bool) {

// only addresses we can use for an iptables rule from a container to the
// host will be fingerprinted
isValidForTaskUse := func(addr netip.Addr) (string, bool) {
if !addr.IsLoopback() && !addr.IsUnspecified() && addr.IsValid() {
return addr.String(), true
}
return "", false
}

// first try to find an explicitly configured address
dnsAddrs, ok := info["DebugConfig"]["DNSAddrs"].([]string)
if ok {
for _, dnsAddr := range dnsAddrs {
dnsAddr = strings.TrimPrefix(dnsAddr, "tcp://")
dnsAddr = strings.TrimPrefix(dnsAddr, "udp://")

parsed, err := netip.ParseAddrPort(dnsAddr)
if err != nil {
logger.Warn("could not parse Consul addresses.dns config", "value", dnsAddr)
return "", false
}
if val, ok := isValidForTaskUse(parsed.Addr()); ok {
return val, true
}
}
}

// fallback to the bind address
bindAddr, ok := info["DebugConfig"]["BindAddr"].(string)
if ok {
parsed, err := netip.ParseAddr(bindAddr)
if err != nil {
logger.Warn("could not parse Consul bind_addr config", "value", bindAddr)
return "", false
}
if val, ok := isValidForTaskUse(parsed); ok {
return val, true
}
}

return "", true // we can't fingerprint a useful value
}
}

func (cfs *consulFingerprintState) namespaces(info agentconsul.Self) (string, bool) {
return strconv.FormatBool(agentconsul.Namespaces(info)), true
}
Expand Down
75 changes: 75 additions & 0 deletions client/fingerprint/consul_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,77 @@ func TestConsulFingerprint_partition(t *testing.T) {
})
}

func TestConsulFingerprint_dns(t *testing.T) {
ci.Parallel(t)

cfs := consulFingerprintState{}

t.Run("dns port not enabled", func(t *testing.T) {
port, ok := cfs.dnsPort(agentconsul.Self{
"DebugConfig": {"DNSPort": -1.0}, // JSON numbers are floats
})
must.True(t, ok)
must.Eq(t, "-1", port)
})

t.Run("non-default port value", func(t *testing.T) {
port, ok := cfs.dnsPort(agentconsul.Self{
"DebugConfig": {"DNSPort": 8601.0}, // JSON numbers are floats
})
must.True(t, ok)
must.Eq(t, "8601", port)
})

t.Run("missing port", func(t *testing.T) {
port, ok := cfs.dnsPort(agentconsul.Self{
"DebugConfig": {},
})
must.False(t, ok)
must.Eq(t, "0", port)
})

t.Run("malformed port", func(t *testing.T) {
port, ok := cfs.dnsPort(agentconsul.Self{
"DebugConfig": {"DNSPort": "A"},
})
must.False(t, ok)
must.Eq(t, "0", port)
})

t.Run("bind on 0.0.0.0", func(t *testing.T) {
addr, ok := cfs.dnsAddr(testlog.HCLogger(t))(agentconsul.Self{
"DebugConfig": {
"DNSAddrs": []string{"tcp://0.0.0.0:8601", "udp://0.0.0.0:8601"},
"BindAddr": "0.0.0.0",
},
})
must.True(t, ok)
must.Eq(t, "", addr)
})

t.Run("get first IP", func(t *testing.T) {
addr, ok := cfs.dnsAddr(testlog.HCLogger(t))(agentconsul.Self{
"DebugConfig": {
"DNSAddrs": []string{"tcp://192.168.1.170:8601", "udp://192.168.1.171:8601"},
"BindAddr": "192.168.1.172",
},
})
must.True(t, ok)
must.Eq(t, "192.168.1.170", addr)
})

t.Run("fallback to bind_addr", func(t *testing.T) {
addr, ok := cfs.dnsAddr(testlog.HCLogger(t))(agentconsul.Self{
"DebugConfig": {
"DNSAddrs": []string{"tcp://0.0.0.0:8601", "udp://0.0.0.0:8601"},
"BindAddr": "192.168.1.172",
},
})
must.True(t, ok)
must.Eq(t, "192.168.1.172", addr)
})
}

func TestConsulFingerprint_Fingerprint_oss(t *testing.T) {
ci.Parallel(t)

Expand All @@ -510,6 +581,7 @@ func TestConsulFingerprint_Fingerprint_oss(t *testing.T) {
must.NoError(t, err)
must.Eq(t, map[string]string{
"consul.datacenter": "dc1",
"consul.dns_port": "8600",
"consul.revision": "3c1c22679",
"consul.segment": "seg1",
"consul.server": "true",
Expand Down Expand Up @@ -564,6 +636,7 @@ func TestConsulFingerprint_Fingerprint_oss(t *testing.T) {
"consul.version": "1.9.5",
"consul.connect": "true",
"consul.grpc": "8502",
"consul.dns_port": "8600",
"consul.ft.namespaces": "false",
"unique.consul.name": "HAL9000",
}, resp3.Attributes)
Expand Down Expand Up @@ -600,6 +673,7 @@ func TestConsulFingerprint_Fingerprint_ent(t *testing.T) {
"consul.ft.namespaces": "true",
"consul.connect": "true",
"consul.grpc": "8502",
"consul.dns_port": "8600",
"consul.partition": "default",
"unique.consul.name": "HAL9000",
}, resp.Attributes)
Expand Down Expand Up @@ -649,6 +723,7 @@ func TestConsulFingerprint_Fingerprint_ent(t *testing.T) {
"consul.ft.namespaces": "true",
"consul.connect": "true",
"consul.grpc": "8502",
"consul.dns_port": "8600",
"consul.partition": "default",
"unique.consul.name": "HAL9000",
}, resp3.Attributes)
Expand Down

0 comments on commit 8f20afc

Please sign in to comment.