Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automate DNS redirection to Consul DNS #833

Merged
merged 9 commits into from
Nov 10, 2021
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
## UNRELEASED

FEATURES:
* Helm Chart
* Add support for Consul services to utilize Consul DNS for service discovery. Set `dns.enableRedirection` to allow services to
use Consul DNS via the Consul DNS Service. [[GH-833](https://github.com/hashicorp/consul-k8s/pull/833)]
* Control Plane
* Connect: Allow services using Connect to utilize Consul DNS to perform service discovery. [[GH-833](https://github.com/hashicorp/consul-k8s/pull/833)]

BREAKING CHANGES:
* Previously [UI metrics](https://www.consul.io/docs/connect/observability/ui-visualization) would be enabled when
`global.metrics=false` and `ui.metrics.enabled=-`. If you are no longer seeing UI metrics,
Expand Down
12 changes: 12 additions & 0 deletions charts/consul/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ is passed to consul as a -config-file param on command line.
[ -n "${HOSTNAME}" ] && sed -Ei "s|HOSTNAME|${HOSTNAME?}|g" /consul/extra-config/extra-from-values.json
{{- end -}}

{{/*
Sets up a list of recusor flags for Consul agents by iterating over the IPs of every nameserver
in /etc/resolv.conf and concatenating them into a string of arguments that can be passed directly
to the consul agent command.
*/}}
{{- define "consul.recursors" -}}
recursor_flags=""
for ip in $(cat /etc/resolv.conf | grep nameserver | cut -d' ' -f2)
do
recursor_flags="$recursor_flags -recursor=$ip"
done
{{- end -}}

{{/*
Create chart name and version as used by the chart label.
Expand Down
7 changes: 7 additions & 0 deletions charts/consul/templates/client-daemonset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,10 @@ spec:
- |
CONSUL_FULLNAME="{{template "consul.fullname" . }}"

{{- if (and .Values.dns.enabled .Values.dns.enableRedirection) }}
{{ template "consul.recursors" }}
{{- end }}

{{ template "consul.extraconfig" }}

exec /usr/local/bin/docker-entrypoint.sh consul agent \
Expand Down Expand Up @@ -276,6 +280,9 @@ spec:
{{- range $value := .Values.global.recursors }}
-recursor={{ quote $value }} \
{{- end }}
{{- if (and .Values.dns.enabled .Values.dns.enableRedirection) }}
$recursor_flags \
{{- end }}
-config-file=/consul/extra-config/extra-from-values.json \
-domain={{ .Values.global.domain }}
volumeMounts:
Expand Down
6 changes: 4 additions & 2 deletions charts/consul/templates/connect-inject-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,6 @@ spec:
- "/bin/sh"
- "-ec"
- |
CONSUL_FULLNAME="{{template "consul.fullname" . }}"

consul-k8s-control-plane inject-connect \
-log-level={{ default .Values.global.logLevel .Values.connectInject.logLevel }} \
-log-json={{ .Values.global.logJSON }} \
Expand All @@ -108,6 +106,10 @@ spec:
{{- else }}
-transparent-proxy-default-overwrite-probes=false \
{{- end }}
-resource-prefix={{ template "consul.fullname" . }} \
{{- if (and .Values.dns.enabled .Values.dns.enableRedirection) }}
-enable-consul-dns=true \
{{- end }}
{{- if .Values.global.openshift.enabled }}
-enable-openshift \
{{- end }}
Expand Down
7 changes: 7 additions & 0 deletions charts/consul/templates/server-statefulset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,10 @@ spec:
- |
CONSUL_FULLNAME="{{template "consul.fullname" . }}"

{{- if (and .Values.dns.enabled .Values.dns.enableRedirection) }}
{{ template "consul.recursors" }}
{{- end }}

{{ template "consul.extraconfig" }}

exec /usr/local/bin/docker-entrypoint.sh consul agent \
Expand Down Expand Up @@ -254,6 +258,9 @@ spec:
{{- range $value := .Values.global.recursors }}
-recursor={{ quote $value }} \
{{- end }}
{{- if (and .Values.dns.enabled .Values.dns.enableRedirection) }}
$recursor_flags \
{{- end }}
-config-file=/consul/extra-config/extra-from-values.json \
-server
volumeMounts:
Expand Down
22 changes: 22 additions & 0 deletions charts/consul/test/unit/client-daemonset.bats
Original file line number Diff line number Diff line change
Expand Up @@ -1170,6 +1170,28 @@ load _helpers
[ "${actual}" = "true" ]
}

#--------------------------------------------------------------------
# DNS

@test "client/DaemonSet: recursor flags is not set by default" {
cd `chart_dir`
local actual=$(helm template \
-s templates/client-daemonset.yaml \
. | tee /dev/stderr |
yq -c -r '.spec.template.spec.containers[0].command | join(" ") | contains("$recursor_flags")' | tee /dev/stderr)
[ "${actual}" = "false" ]
}

@test "client/DaemonSet: add recursor flags if dns.enableRedirection is true" {
cd `chart_dir`
local actual=$(helm template \
-s templates/client-daemonset.yaml \
--set 'dns.enableRedirection=true' \
. | tee /dev/stderr |
yq -c -r '.spec.template.spec.containers[0].command | join(" ") | contains("$recursor_flags")' | tee /dev/stderr)
[ "${actual}" = "true" ]
}

#--------------------------------------------------------------------
# hostNetwork

Expand Down
34 changes: 34 additions & 0 deletions charts/consul/test/unit/connect-inject-deployment.bats
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,40 @@ EOF
[ "${actual}" = "true" ]
}

#--------------------------------------------------------------------
# DNS

@test "connectInject/Deployment: -enable-consul-dns unset by default" {
cd `chart_dir`
local actual=$(helm template \
-s templates/connect-inject-deployment.yaml \
--set 'connectInject.enabled=true' \
. | tee /dev/stderr |
yq -c -r '.spec.template.spec.containers[0].command | join(" ") | contains("-enable-consul-dns=true")' | tee /dev/stderr)
[ "${actual}" = "false" ]
}

@test "connectInject/Deployment: -enable-consul-dns is true if dns.enabled=true and dns.enableRedirection=true" {
cd `chart_dir`
local actual=$(helm template \
-s templates/connect-inject-deployment.yaml \
--set 'connectInject.enabled=true' \
--set 'dns.enableRedirection=true' \
. | tee /dev/stderr |
yq -c -r '.spec.template.spec.containers[0].command | join(" ") | contains("-enable-consul-dns=true")' | tee /dev/stderr)
[ "${actual}" = "true" ]
}

@test "connectInject/Deployment: -resource-prefix always set" {
cd `chart_dir`
local actual=$(helm template \
-s templates/connect-inject-deployment.yaml \
--set 'connectInject.enabled=true' \
. | tee /dev/stderr |
yq -c -r '.spec.template.spec.containers[0].command | join(" ") | contains("-resource-prefix=RELEASE-NAME-consul")' | tee /dev/stderr)
[ "${actual}" = "true" ]
}

#--------------------------------------------------------------------
# global.tls.enabled

Expand Down
22 changes: 22 additions & 0 deletions charts/consul/test/unit/server-statefulset.bats
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,28 @@ load _helpers
[ "${actualBaz}" = "qux" ]
}

#--------------------------------------------------------------------
# DNS

@test "server/StatefulSet: recursor flags unset by default" {
cd `chart_dir`
local actual=$(helm template \
-s templates/server-statefulset.yaml \
. | tee /dev/stderr |
yq -c -r '.spec.template.spec.containers[0].command | join(" ") | contains("$recursor_flags")' | tee /dev/stderr)
[ "${actual}" = "false" ]
}

@test "server/StatefulSet: add recursor flags if dns.enableRedirection is true" {
cd `chart_dir`
local actual=$(helm template \
-s templates/server-statefulset.yaml \
--set 'dns.enableRedirection=true' \
. | tee /dev/stderr |
yq -c -r '.spec.template.spec.containers[0].command | join(" ") | contains("$recursor_flags")' | tee /dev/stderr)
[ "${actual}" = "true" ]
}

#--------------------------------------------------------------------
# annotations

Expand Down
6 changes: 6 additions & 0 deletions charts/consul/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,12 @@ dns:
# @type: boolean
enabled: "-"

# If true, services using Consul Connect will use Consul DNS
# for default DNS resolution. The DNS lookups fall back to the nameserver IPs
# listed in /etc/resolv.conf if not found in Consul.
# @type: boolean
thisisnotashwin marked this conversation as resolved.
Show resolved Hide resolved
enableRedirection: false

# Used to control the type of service created. For
# example, setting this to "LoadBalancer" will create an external load
# balancer (for supported K8S installations)
Expand Down
6 changes: 6 additions & 0 deletions control-plane/connect-inject/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ const (
// annotationConsulNamespace is the Consul namespace the service is registered into.
annotationConsulNamespace = "consul.hashicorp.com/consul-namespace"

// keyConsulDNS enables or disables Consul DNS for a given pod. It can also be set as a label
// on a namespace to define the default behaviour for connect-injected pods which do not otherwise override this setting
// with their own annotation.
// This annotation/label takes a boolean value (true/false).
keyConsulDNS = "consul.hashicorp.com/consul-dns"

// keyTransparentProxy enables or disables transparent proxy for a given pod. It can also be set as a label
// on a namespace to define the default behaviour for connect-injected pods which do not otherwise override this setting
// with their own annotation.
Expand Down
51 changes: 51 additions & 0 deletions control-plane/connect-inject/container_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package connectinject

import (
"bytes"
"fmt"
"os"
"strconv"
"strings"
"text/template"
Expand All @@ -16,6 +18,7 @@ const (
envoyUserAndGroupID = 5995
copyContainerUserAndGroupID = 5996
netAdminCapability = "NET_ADMIN"
dnsServiceHostEnvSuffix = "DNS_SERVICE_HOST"
lkysow marked this conversation as resolved.
Show resolved Hide resolved
)

type initContainerCommandData struct {
Expand Down Expand Up @@ -66,6 +69,9 @@ type initContainerCommandData struct {
// TProxyExcludeUIDs is a list of additional user IDs to exclude from traffic redirection via
// the consul connect redirect-traffic command.
TProxyExcludeUIDs []string

// ConsulDNSClusterIP is the IP of the Consul DNS Service.
ConsulDNSClusterIP string
}

// initCopyContainer returns the init container spec for the copy container which places
Expand Down Expand Up @@ -107,6 +113,22 @@ func (h *Handler) containerInit(namespace corev1.Namespace, pod corev1.Pod) (cor
return corev1.Container{}, err
}

dnsEnabled, err := consulDNSEnabled(namespace, pod, h.EnableConsulDNS)
if err != nil {
return corev1.Container{}, err
}

var consulDNSClusterIP string
if dnsEnabled {
// If Consul DNS is enabled, we find the environment variable that has the value
// of the ClusterIP of the Consul DNS Service. constructDNSServiceHostName returns
// the name of the env variable whose value is the ClusterIP of the Consul DNS Service.
consulDNSClusterIP = os.Getenv(h.constructDNSServiceHostName())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should error out if the env var is not found

if consulDNSClusterIP == "" {
return corev1.Container{}, fmt.Errorf("environment variable %s is not found", h.constructDNSServiceHostName())
}
}

data := initContainerCommandData{
AuthMethod: h.AuthMethod,
ConsulPartition: h.ConsulPartition,
Expand All @@ -118,6 +140,7 @@ func (h *Handler) containerInit(namespace corev1.Namespace, pod corev1.Pod) (cor
TProxyExcludeOutboundPorts: splitCommaSeparatedItemsFromAnnotation(annotationTProxyExcludeOutboundPorts, pod),
TProxyExcludeOutboundCIDRs: splitCommaSeparatedItemsFromAnnotation(annotationTProxyExcludeOutboundCIDRs, pod),
TProxyExcludeUIDs: splitCommaSeparatedItemsFromAnnotation(annotationTProxyExcludeUIDs, pod),
ConsulDNSClusterIP: consulDNSClusterIP,
EnvoyUID: envoyUserAndGroupID,
}

Expand Down Expand Up @@ -223,6 +246,15 @@ func (h *Handler) containerInit(namespace corev1.Namespace, pod corev1.Pod) (cor
return container, nil
}

// constructDNSServiceHostName use the resource prefix and the DNS Service hostname suffix to construct the
// key of the env variable whose value is the cluster IP of the Consul DNS Service.
// It translates "resource-prefix" into "RESOURCE_PREFIX_DNS_SERVICE_HOST".
func (h *Handler) constructDNSServiceHostName() string {
upcaseResourcePrefix := strings.ToUpper(h.ResourcePrefix)
upcaseResourcePrefixWithUnderscores := strings.ReplaceAll(upcaseResourcePrefix, "-", "_")
return strings.Join([]string{upcaseResourcePrefixWithUnderscores, dnsServiceHostEnvSuffix}, "_")
}

// transparentProxyEnabled returns true if transparent proxy should be enabled for this pod.
// It returns an error when the annotation value cannot be parsed by strconv.ParseBool or if we are unable
// to read the pod's namespace label when it exists.
Expand All @@ -239,6 +271,22 @@ func transparentProxyEnabled(namespace corev1.Namespace, pod corev1.Pod, globalE
return globalEnabled, nil
}

// consulDNSEnabled returns true if Consul DNS should be enabled for this pod.
// It returns an error when the annotation value cannot be parsed by strconv.ParseBool or if we are unable
// to read the pod's namespace label when it exists.
func consulDNSEnabled(namespace corev1.Namespace, pod corev1.Pod, globalEnabled bool) (bool, error) {
// First check to see if the pod annotation exists to override the namespace or global settings.
if raw, ok := pod.Annotations[keyConsulDNS]; ok {
return strconv.ParseBool(raw)
}
// Next see if the namespace has been defaulted.
if raw, ok := namespace.Labels[keyConsulDNS]; ok {
return strconv.ParseBool(raw)
}
// Else fall back to the global default.
return globalEnabled, nil
}

// pointerToInt64 takes an int64 and returns a pointer to it.
func pointerToInt64(i int64) *int64 {
return &i
Expand Down Expand Up @@ -331,6 +379,9 @@ consul-k8s-control-plane connect-init -pod-name=${POD_NAME} -pod-namespace=${POD
{{- if .ConsulNamespace }}
-namespace="{{ .ConsulNamespace }}" \
{{- end }}
{{- if .ConsulDNSClusterIP }}
-consul-dns-ip="{{ .ConsulDNSClusterIP }}" \
{{- end }}
{{- range .TProxyExcludeInboundPorts }}
-exclude-inbound-port="{{ . }}" \
{{- end }}
Expand Down
Loading