mirror of
https://github.com/status-im/consul.git
synced 2025-01-24 20:51:10 +00:00
4b85aa5a97
This updates the testing/deployer (aka "topology test") framework to allow for a v2-oriented topology to opt services into enabling TransparentProxy. The restrictions are similar to that of #19046 The multiport Ports map that was added in #19046 was changed to allow for the protocol to be specified at this time, but for now the only supported protocol is TCP as only L4 functions currently on main. As part of making transparent proxy work, the DNS server needed a new zonefile for responding to virtual.consul requests, since there is no Kubernetes DNS and the Consul DNS work for v2 has not happened yet. Once Consul DNS supports v2 we should switch over. For now the format of queries is: <service>--<namespace>--<partition>.virtual.consul Additionally: - All transparent proxy enabled services are assigned a virtual ip in the 10.244.0/24 range. This is something Consul will do in v2 at a later date, likely during 1.18. - All services with exposed ports (non-mesh) are assigned a virtual port number for use with tproxy - The consul-dataplane image has been made un-distroless, and gotten the necessary tools to execute consul connect redirect-traffic before running dataplane, thus simulating a kubernetes init container in plain docker.
264 lines
5.8 KiB
Go
264 lines
5.8 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package tfgen
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
|
|
"golang.org/x/exp/maps"
|
|
|
|
"github.com/hashicorp/consul/testing/deployer/topology"
|
|
"github.com/hashicorp/consul/testing/deployer/util"
|
|
)
|
|
|
|
func (g *Generator) getCoreDNSContainer(
|
|
net *topology.Network,
|
|
ipAddress string,
|
|
hashes []string,
|
|
) Resource {
|
|
var env []string
|
|
for i, hv := range hashes {
|
|
env = append(env, fmt.Sprintf("HASH_FILE_%d_VALUE=%s", i, hv))
|
|
}
|
|
coredns := struct {
|
|
Name string
|
|
DockerNetworkName string
|
|
IPAddress string
|
|
HashValues string
|
|
Env []string
|
|
}{
|
|
Name: net.Name,
|
|
DockerNetworkName: net.DockerName,
|
|
IPAddress: ipAddress,
|
|
Env: env,
|
|
}
|
|
return Eval(tfCorednsT, &coredns)
|
|
}
|
|
|
|
func (g *Generator) writeCoreDNSFiles(net *topology.Network, dnsIPAddress string) (bool, []string, error) {
|
|
if net.IsPublic() {
|
|
return false, nil, fmt.Errorf("coredns only runs on local networks")
|
|
}
|
|
|
|
rootdir := filepath.Join(g.workdir, "terraform", "coredns-config-"+net.Name)
|
|
if err := os.MkdirAll(rootdir, 0755); err != nil {
|
|
return false, nil, err
|
|
}
|
|
|
|
for _, cluster := range g.topology.Clusters {
|
|
if cluster.NetworkName != net.Name {
|
|
continue
|
|
}
|
|
var addrs []string
|
|
for _, node := range cluster.SortedNodes() {
|
|
if node.Kind != topology.NodeKindServer || node.Disabled {
|
|
continue
|
|
}
|
|
addr := node.AddressByNetwork(net.Name)
|
|
if addr.IPAddress != "" {
|
|
addrs = append(addrs, addr.IPAddress)
|
|
}
|
|
}
|
|
|
|
// Until Consul DNS understands v2, simulate it.
|
|
//
|
|
// NOTE: this DNS is not quite what consul normally does. It's simpler
|
|
// to simulate this format here.
|
|
virtualNames := make(map[string][]string)
|
|
for id, svcData := range cluster.Services {
|
|
if len(svcData.VirtualIps) == 0 {
|
|
continue
|
|
}
|
|
vips := svcData.VirtualIps
|
|
|
|
// <service>--<namespace>--<partition>.virtual.<domain>
|
|
name := fmt.Sprintf("%s--%s--%s", id.Name, id.Namespace, id.Partition)
|
|
virtualNames[name] = vips
|
|
}
|
|
|
|
var (
|
|
clusterDNSName = cluster.Name + "-consulcluster.lan"
|
|
virtualDNSName = "virtual.consul"
|
|
|
|
corefilePath = filepath.Join(rootdir, "Corefile")
|
|
zonefilePath = filepath.Join(rootdir, "servers")
|
|
virtualZonefilePath = filepath.Join(rootdir, "virtual")
|
|
)
|
|
|
|
_, err := UpdateFileIfDifferent(
|
|
g.logger,
|
|
generateCoreDNSConfigFile(
|
|
clusterDNSName,
|
|
virtualDNSName,
|
|
addrs,
|
|
),
|
|
corefilePath,
|
|
0644,
|
|
)
|
|
if err != nil {
|
|
return false, nil, fmt.Errorf("error writing %q: %w", corefilePath, err)
|
|
}
|
|
corefileHash, err := util.HashFile(corefilePath)
|
|
if err != nil {
|
|
return false, nil, fmt.Errorf("error hashing %q: %w", corefilePath, err)
|
|
}
|
|
|
|
_, err = UpdateFileIfDifferent(
|
|
g.logger,
|
|
generateCoreDNSZoneFile(
|
|
dnsIPAddress,
|
|
clusterDNSName,
|
|
addrs,
|
|
),
|
|
zonefilePath,
|
|
0644,
|
|
)
|
|
if err != nil {
|
|
return false, nil, fmt.Errorf("error writing %q: %w", zonefilePath, err)
|
|
}
|
|
zonefileHash, err := util.HashFile(zonefilePath)
|
|
if err != nil {
|
|
return false, nil, fmt.Errorf("error hashing %q: %w", zonefilePath, err)
|
|
}
|
|
|
|
_, err = UpdateFileIfDifferent(
|
|
g.logger,
|
|
generateCoreDNSVirtualZoneFile(
|
|
dnsIPAddress,
|
|
virtualDNSName,
|
|
virtualNames,
|
|
),
|
|
virtualZonefilePath,
|
|
0644,
|
|
)
|
|
if err != nil {
|
|
return false, nil, fmt.Errorf("error writing %q: %w", virtualZonefilePath, err)
|
|
}
|
|
virtualZonefileHash, err := util.HashFile(virtualZonefilePath)
|
|
if err != nil {
|
|
return false, nil, fmt.Errorf("error hashing %q: %w", virtualZonefilePath, err)
|
|
}
|
|
|
|
return true, []string{corefileHash, zonefileHash, virtualZonefileHash}, nil
|
|
}
|
|
|
|
return false, nil, nil
|
|
}
|
|
|
|
func generateCoreDNSConfigFile(
|
|
clusterDNSName string,
|
|
virtualDNSName string,
|
|
addrs []string,
|
|
) []byte {
|
|
serverPart := ""
|
|
if len(addrs) > 0 {
|
|
var servers []string
|
|
for _, addr := range addrs {
|
|
servers = append(servers, addr+":8600")
|
|
}
|
|
serverPart = fmt.Sprintf(`
|
|
consul:53 {
|
|
forward . %s
|
|
log
|
|
errors
|
|
whoami
|
|
}
|
|
`, strings.Join(servers, " "))
|
|
}
|
|
|
|
return []byte(fmt.Sprintf(`
|
|
%[1]s:53 {
|
|
file /config/servers %[1]s
|
|
log
|
|
errors
|
|
whoami
|
|
}
|
|
|
|
%[2]s:53 {
|
|
file /config/virtual %[2]s
|
|
log
|
|
errors
|
|
whoami
|
|
}
|
|
|
|
%[3]s
|
|
|
|
.:53 {
|
|
forward . 8.8.8.8:53
|
|
log
|
|
errors
|
|
whoami
|
|
}
|
|
`, clusterDNSName, virtualDNSName, serverPart))
|
|
}
|
|
|
|
func generateCoreDNSZoneFile(
|
|
dnsIPAddress string,
|
|
clusterDNSName string,
|
|
addrs []string,
|
|
) []byte {
|
|
var buf bytes.Buffer
|
|
buf.WriteString(fmt.Sprintf(`
|
|
$TTL 60
|
|
$ORIGIN %[1]s.
|
|
@ IN SOA ns.%[1]s. webmaster.%[1]s. (
|
|
2017042745 ; serial
|
|
7200 ; refresh (2 hours)
|
|
3600 ; retry (1 hour)
|
|
1209600 ; expire (2 weeks)
|
|
3600 ; minimum (1 hour)
|
|
)
|
|
@ IN NS ns.%[1]s. ; Name server
|
|
ns IN A %[2]s ; self
|
|
`, clusterDNSName, dnsIPAddress))
|
|
|
|
for _, addr := range addrs {
|
|
buf.WriteString(fmt.Sprintf(`
|
|
server IN A %s ; Consul server
|
|
`, addr))
|
|
}
|
|
|
|
return buf.Bytes()
|
|
}
|
|
|
|
func generateCoreDNSVirtualZoneFile(
|
|
dnsIPAddress string,
|
|
virtualDNSName string,
|
|
nameToAddr map[string][]string,
|
|
) []byte {
|
|
var buf bytes.Buffer
|
|
buf.WriteString(fmt.Sprintf(`
|
|
$TTL 60
|
|
$ORIGIN %[1]s.
|
|
@ IN SOA ns.%[1]s. webmaster.%[1]s. (
|
|
2017042745 ; serial
|
|
7200 ; refresh (2 hours)
|
|
3600 ; retry (1 hour)
|
|
1209600 ; expire (2 weeks)
|
|
3600 ; minimum (1 hour)
|
|
)
|
|
@ IN NS ns.%[1]s. ; Name server
|
|
ns IN A %[2]s ; self
|
|
`, virtualDNSName, dnsIPAddress))
|
|
|
|
names := maps.Keys(nameToAddr)
|
|
sort.Strings(names)
|
|
|
|
for _, name := range names {
|
|
vips := nameToAddr[name]
|
|
for _, vip := range vips {
|
|
buf.WriteString(fmt.Sprintf(`
|
|
%s IN A %s ; Consul server
|
|
`, name, vip))
|
|
}
|
|
}
|
|
|
|
return buf.Bytes()
|
|
}
|