diff --git a/CHANGELOG.md b/CHANGELOG.md index b4de5b5ce4..0cd55a59cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## UNRELEASED +FEATURES: +* Helm Chart + * Add automatic generation of gossip encryption with `global.gossipEncryption.autoGenerate=true`. [[GH-738](https://github.com/hashicorp/consul-k8s/pull/738)] + IMPROVEMENTS: * Control Plane * Upgrade Docker image Alpine version from 3.13 to 3.14. [[GH-737](https://github.com/hashicorp/consul-k8s/pull/737)] diff --git a/charts/consul/templates/client-daemonset.yaml b/charts/consul/templates/client-daemonset.yaml index d2a2dcb583..d4e1c05107 100644 --- a/charts/consul/templates/client-daemonset.yaml +++ b/charts/consul/templates/client-daemonset.yaml @@ -168,12 +168,17 @@ spec: fieldPath: status.podIP - name: CONSUL_DISABLE_PERM_MGMT value: "true" - {{- if (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey) }} + {{- if (or .Values.global.gossipEncryption.autoGenerate (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey)) }} - name: GOSSIP_KEY valueFrom: secretKeyRef: + {{- if .Values.global.gossipEncryption.autoGenerate }} + name: {{ template "consul.fullname" . }}-gossip-encryption-key + key: key + {{- else if (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey) }} name: {{ .Values.global.gossipEncryption.secretName }} key: {{ .Values.global.gossipEncryption.secretKey }} + {{- end }} {{- end }} {{- if (and .Values.server.enterpriseLicense.secretName .Values.server.enterpriseLicense.secretKey .Values.server.enterpriseLicense.enableLicenseAutoload (not .Values.global.acls.manageSystemACLs)) }} - name: CONSUL_LICENSE_PATH @@ -252,7 +257,7 @@ spec: {{- end }} -datacenter={{ .Values.global.datacenter }} \ -data-dir=/consul/data \ - {{- if (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey) }} + {{- if (or .Values.global.gossipEncryption.autoGenerate (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey)) }} -encrypt="${GOSSIP_KEY}" \ {{- end }} {{- if .Values.client.join }} diff --git a/charts/consul/templates/gossip-encryption-autogenerate-job.yaml b/charts/consul/templates/gossip-encryption-autogenerate-job.yaml new file mode 100644 index 0000000000..ae5c0674d1 --- /dev/null +++ b/charts/consul/templates/gossip-encryption-autogenerate-job.yaml @@ -0,0 +1,71 @@ +{{- if .Values.global.gossipEncryption.autoGenerate }} +{{- if (or .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey) }} + {{ fail "If global.gossipEncryption.autoGenerate is true, global.gossipEncryption.secretName and global.gossipEncryption.secretKey must not be set." }} +{{ end }} +# automatically generate encryption key for gossip protocol and save it in Kubernetes secret +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-weight": "1" + "helm.sh/hook-delete-policy": hook-succeeded,before-hook-creation +spec: + template: + metadata: + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + release: {{ .Release.Name }} + component: gossip-encryption-autogeneneration + annotations: + "consul.hashicorp.com/connect-inject": "false" + spec: + restartPolicy: Never + serviceAccountName: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate + securityContext: + runAsNonRoot: true + runAsGroup: 1000 + runAsUser: 100 + fsGroup: 1000 + containers: + - name: gossip-encryption-autogen + image: "{{ .Values.global.image }}" + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + # We're using POST requests below to create secrets via Kubernetes API. + # Note that in the subsequent runs of the job, POST requests will + # return a 409 because these secrets would already exist; + # we are ignoring these response codes. + command: + - "/bin/sh" + - "-ec" + - | + secretName={{ template "consul.fullname" . }}-gossip-encryption-key + secretKey=key + keyValue=$(consul keygen | base64) + curl -s -X POST --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \ + https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}/api/v1/namespaces/${NAMESPACE}/secrets \ + -H "Authorization: Bearer $( cat /var/run/secrets/kubernetes.io/serviceaccount/token )" \ + -H "Content-Type: application/json" \ + -H "Accept: application/json" \ + -d "{ \"kind\": \"Secret\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"${secretName}\", \"namespace\": \"${NAMESPACE}\" }, \"type\": \"Opaque\", \"data\": { \"${secretKey}\": \"${keyValue}\" }}" > /dev/null + resources: + requests: + memory: "50Mi" + cpu: "50m" + limits: + memory: "50Mi" + cpu: "50m" +{{- end }} diff --git a/charts/consul/templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml b/charts/consul/templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml new file mode 100644 index 0000000000..6121fbbe30 --- /dev/null +++ b/charts/consul/templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml @@ -0,0 +1,39 @@ +{{- if .Values.global.gossipEncryption.autoGenerate }} +--- +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +spec: + privileged: false + # Required to prevent escalations to root. + allowPrivilegeEscalation: false + # This is redundant with non-root + disallow privilege escalation, + # but we can provide it for defense in depth. + requiredDropCapabilities: + - ALL + # Allow core volume types. + volumes: + - 'secret' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'RunAsAny' + fsGroup: + rule: 'RunAsAny' + readOnlyRootFilesystem: false +{{- end }} diff --git a/charts/consul/templates/gossip-encryption-autogenerate-role.yaml b/charts/consul/templates/gossip-encryption-autogenerate-role.yaml new file mode 100644 index 0000000000..5f9d354f38 --- /dev/null +++ b/charts/consul/templates/gossip-encryption-autogenerate-role.yaml @@ -0,0 +1,30 @@ +{{- if .Values.global.gossipEncryption.autoGenerate }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +rules: +- apiGroups: [""] + resources: + - secrets + verbs: + - create +{{- if .Values.global.enablePodSecurityPolicies }} +- apiGroups: ["policy"] + resources: + - podsecuritypolicies + verbs: + - use + resourceNames: + - {{ template "consul.fullname" . }}-gossip-encryption-autogenerate +{{- end }} +{{- end }} diff --git a/charts/consul/templates/gossip-encryption-autogenerate-rolebinding.yaml b/charts/consul/templates/gossip-encryption-autogenerate-rolebinding.yaml new file mode 100644 index 0000000000..caef0d221e --- /dev/null +++ b/charts/consul/templates/gossip-encryption-autogenerate-rolebinding.yaml @@ -0,0 +1,22 @@ +{{- if .Values.global.gossipEncryption.autoGenerate }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate +subjects: +- kind: ServiceAccount + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate +{{- end }} diff --git a/charts/consul/templates/gossip-encryption-autogenerate-serviceaccount.yaml b/charts/consul/templates/gossip-encryption-autogenerate-serviceaccount.yaml new file mode 100644 index 0000000000..a711f9a4c1 --- /dev/null +++ b/charts/consul/templates/gossip-encryption-autogenerate-serviceaccount.yaml @@ -0,0 +1,21 @@ +{{- if .Values.global.gossipEncryption.autoGenerate }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "consul.fullname" . }}-gossip-encryption-autogenerate + namespace: {{ .Release.Namespace }} + labels: + app: {{ template "consul.name" . }} + chart: {{ template "consul.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation +{{- with .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range . }} + - name: {{ .name }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/consul/templates/server-statefulset.yaml b/charts/consul/templates/server-statefulset.yaml index 05460ed217..fe2dcff468 100644 --- a/charts/consul/templates/server-statefulset.yaml +++ b/charts/consul/templates/server-statefulset.yaml @@ -156,12 +156,17 @@ spec: fieldPath: metadata.namespace - name: CONSUL_DISABLE_PERM_MGMT value: "true" - {{- if (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey) }} + {{- if (or .Values.global.gossipEncryption.autoGenerate (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey)) }} - name: GOSSIP_KEY valueFrom: secretKeyRef: + {{- if .Values.global.gossipEncryption.autoGenerate }} + name: {{ template "consul.fullname" . }}-gossip-encryption-key + key: key + {{- else if (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey) }} name: {{ .Values.global.gossipEncryption.secretName }} key: {{ .Values.global.gossipEncryption.secretKey }} + {{- end }} {{- end }} {{- if .Values.global.tls.enabled }} - name: CONSUL_HTTP_ADDR @@ -223,7 +228,7 @@ spec: -datacenter={{ .Values.global.datacenter }} \ -data-dir=/consul/data \ -domain={{ .Values.global.domain }} \ - {{- if (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey) }} + {{- if (or .Values.global.gossipEncryption.autoGenerate (and .Values.global.gossipEncryption.secretName .Values.global.gossipEncryption.secretKey)) }} -encrypt="${GOSSIP_KEY}" \ {{- end }} {{- if .Values.server.connect }} diff --git a/charts/consul/test/acceptance/tests/basic/basic_test.go b/charts/consul/test/acceptance/tests/basic/basic_test.go index cc3cdc9877..95861e5f98 100644 --- a/charts/consul/test/acceptance/tests/basic/basic_test.go +++ b/charts/consul/test/acceptance/tests/basic/basic_test.go @@ -1,8 +1,10 @@ package basic import ( + "context" "fmt" "strconv" + "strings" "testing" "github.com/hashicorp/consul-k8s/charts/consul/test/acceptance/framework/consul" @@ -10,6 +12,7 @@ import ( "github.com/hashicorp/consul-k8s/charts/consul/test/acceptance/framework/logger" "github.com/hashicorp/consul/api" "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // Test that the basic installation, i.e. just @@ -39,9 +42,10 @@ func TestBasicInstallation(t *testing.T) { t.Run(name, func(t *testing.T) { releaseName := helpers.RandomName() helmValues := map[string]string{ - "global.acls.manageSystemACLs": strconv.FormatBool(c.secure), - "global.tls.enabled": strconv.FormatBool(c.secure), - "global.tls.enableAutoEncrypt": strconv.FormatBool(c.autoEncrypt), + "global.acls.manageSystemACLs": strconv.FormatBool(c.secure), + "global.tls.enabled": strconv.FormatBool(c.secure), + "global.gossipEncryption.autoGenerate": strconv.FormatBool(c.secure), + "global.tls.enableAutoEncrypt": strconv.FormatBool(c.autoEncrypt), } consulCluster := consul.NewHelmCluster(t, helmValues, suite.Environment().DefaultContext(t), suite.Config(), releaseName) @@ -63,6 +67,24 @@ func TestBasicInstallation(t *testing.T) { kv, _, err := client.KV().Get(randomKey, nil) require.NoError(t, err) require.Equal(t, kv.Value, randomValue) + + // Check that autogenerated gossip encryption key is being used + if c.secure { + secretName := fmt.Sprintf("%s-consul-gossip-encryption-key", releaseName) + secretKey := "key" + + keyring, err := client.Operator().KeyringList(nil) + require.NoError(t, err) + + testContext := suite.Environment().DefaultContext(t) + secret, err := testContext.KubernetesClient(t).CoreV1().Secrets(testContext.KubectlOptions(t).Namespace).Get(context.Background(), secretName, metav1.GetOptions{}) + require.NoError(t, err) + gossipEncryptionKey := strings.TrimSpace(string(secret.Data[secretKey])) + + require.Len(t, keyring, 2) + require.Contains(t, keyring[0].Keys, gossipEncryptionKey) + require.Contains(t, keyring[1].Keys, gossipEncryptionKey) + } }) } } diff --git a/charts/consul/test/unit/client-daemonset.bats b/charts/consul/test/unit/client-daemonset.bats index ce202bca3e..cbbbc10ef9 100755 --- a/charts/consul/test/unit/client-daemonset.bats +++ b/charts/consul/test/unit/client-daemonset.bats @@ -606,6 +606,27 @@ load _helpers [ "${actual}" = "" ] } +@test "client/DaemonSet: gossip encryption autogeneration properly sets secretName and secretKey" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/client-daemonset.yaml \ + --set 'global.gossipEncryption.autoGenerate=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[] | select(.name=="consul") | .env[] | select(.name == "GOSSIP_KEY") | .valueFrom.secretKeyRef | [.name=="RELEASE-NAME-consul-gossip-encryption-key", .key="key"] | all' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "client/DaemonSet: gossip encryption key is passed in via the -encrypt flag" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/client-daemonset.yaml \ + --set 'global.gossipEncryption.autoGenerate=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[] | select(.name=="consul") | .command | any(contains("-encrypt=\"${GOSSIP_KEY}\""))' + | tee /dev/stderr) + [ "${actual}" = "true" ] +} + @test "client/DaemonSet: gossip encryption disabled in client DaemonSet when secretName is missing" { cd `chart_dir` local actual=$(helm template \ diff --git a/charts/consul/test/unit/gossip-encryption-autogenerate-job.bats b/charts/consul/test/unit/gossip-encryption-autogenerate-job.bats new file mode 100644 index 0000000000..b78b9c231d --- /dev/null +++ b/charts/consul/test/unit/gossip-encryption-autogenerate-job.bats @@ -0,0 +1,63 @@ +#!/usr/bin/env bats + +load _helpers + +@test "gossipEncryptionAutogenerate/Job: disabled by default" { + cd `chart_dir` + assert_empty helm template \ + -s templates/gossip-encryption-autogenerate-job.yaml \ + . +} + +@test "gossipEncryptionAutogenerate/Job: enabled with global.gossipEncryption.autoGenerate=true" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/gossip-encryption-autogenerate-job.yaml \ + --set 'global.gossipEncryption.autoGenerate=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "gossipEncryptionAutogenerate/Job: disabled when global.gossipEncryption.autoGenerate=false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/gossip-encryption-autogenerate-job.yaml \ + --set 'global.gossipEncryption.autoGenerate=false' \ + . +} + +@test "gossipEncryptionAutogenerate/Job: fails if global.gossipEncryption.autoGenerate=true and global.gossipEncryption.secretName and global.gossipEncryption.secretKey are set" { + cd `chart_dir` + run helm template \ + -s templates/gossip-encryption-autogenerate-job.yaml \ + --set 'global.gossipEncryption.autoGenerate=true' \ + --set 'global.gossipEncryption.secretName=name' \ + --set 'global.gossipEncryption.secretKey=key' \ + . + [ "$status" -eq 1 ] + [[ "$output" =~ "If global.gossipEncryption.autoGenerate is true, global.gossipEncryption.secretName and global.gossipEncryption.secretKey must not be set." ]] +} + +@test "gossipEncryptionAutogenerate/Job: fails if global.gossipEncryption.autoGenerate=true and global.gossipEncryption.secretName is set" { + cd `chart_dir` + run helm template \ + -s templates/gossip-encryption-autogenerate-job.yaml \ + --set 'global.gossipEncryption.autoGenerate=true' \ + --set 'global.gossipEncryption.secretName=name' \ + . + [ "$status" -eq 1 ] + [[ "$output" =~ "If global.gossipEncryption.autoGenerate is true, global.gossipEncryption.secretName and global.gossipEncryption.secretKey must not be set." ]] +} + +@test "gossipEncryptionAutogenerate/Job: fails if global.gossipEncryption.autoGenerate=true and global.gossipEncryption.secretKey is set" { + cd `chart_dir` + run helm template \ + -s templates/gossip-encryption-autogenerate-job.yaml \ + --set 'global.gossipEncryption.autoGenerate=true' \ + --set 'global.gossipEncryption.secretKey=key' \ + . + [ "$status" -eq 1 ] + [[ "$output" =~ "If global.gossipEncryption.autoGenerate is true, global.gossipEncryption.secretName and global.gossipEncryption.secretKey must not be set." ]] +} + diff --git a/charts/consul/test/unit/gossip-encryption-autogenerate-podsecuritypolicy.bats b/charts/consul/test/unit/gossip-encryption-autogenerate-podsecuritypolicy.bats new file mode 100644 index 0000000000..810147bed3 --- /dev/null +++ b/charts/consul/test/unit/gossip-encryption-autogenerate-podsecuritypolicy.bats @@ -0,0 +1,28 @@ +#!/usr/bin/env bats + +load _helpers + +@test "gossipEncryptionAutogenerate/PodSecurityPolicy: disabled by default" { + cd `chart_dir` + assert_empty helm template \ + -s templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml \ + . +} + +@test "gossipEncryptionAutogenerate/PodSecurityPolicy: disabled with global.gossipEncryption.autoGenerate=false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml \ + --set 'global.gossipEncryption.autoGenerate=false' \ + . +} + +@test "gossipEncryptionAutogenerate/PodSecurityPolicy: enabled with global.gossipEncryption.autoGenerate=true" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/gossip-encryption-autogenerate-podsecuritypolicy.yaml \ + --set 'global.gossipEncryption.autoGenerate=true' \ + . | tee /dev/stderr | + yq -s 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} diff --git a/charts/consul/test/unit/gossip-encryption-autogenerate-role.bats b/charts/consul/test/unit/gossip-encryption-autogenerate-role.bats new file mode 100644 index 0000000000..7707a872f0 --- /dev/null +++ b/charts/consul/test/unit/gossip-encryption-autogenerate-role.bats @@ -0,0 +1,28 @@ +#!/usr/bin/env bats + +load _helpers + +@test "gossipEncryptionAutogenerate/Role: disabled by default" { + cd `chart_dir` + assert_empty helm template \ + -s templates/gossip-encryption-autogenerate-role.yaml \ + . +} + +@test "gossipEncryptionAutogenerate/Role: disabled with global.gossipEncryption.autoGenerate=false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/gossip-encryption-autogenerate-role.yaml \ + --set 'global.gossipEncryption.autoGenerate=false' \ + . +} + +@test "gossipEncryptionAutogenerate/Role: enabled when global.gossipEncryption.autoGenerate=true" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/gossip-encryption-autogenerate-role.yaml \ + --set 'global.gossipEncryption.autoGenerate=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} diff --git a/charts/consul/test/unit/gossip-encryption-autogenerate-rolebinding.bats b/charts/consul/test/unit/gossip-encryption-autogenerate-rolebinding.bats new file mode 100644 index 0000000000..9847beaa24 --- /dev/null +++ b/charts/consul/test/unit/gossip-encryption-autogenerate-rolebinding.bats @@ -0,0 +1,29 @@ + +#!/usr/bin/env bats + +load _helpers + +@test "gossipEncryptionAutogenerate/RoleBinding: disabled by default" { + cd `chart_dir` + assert_empty helm template \ + -s templates/gossip-encryption-autogenerate-rolebinding.yaml \ + . +} + +@test "gossipEncryptionAutogenerate/RoleBinding: disabled with global.gossipEncryption.autoGenerate=false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/gossip-encryption-autogenerate-rolebinding.yaml \ + --set 'global.gossipEncryption.autoGenerate=false' \ + . +} + +@test "gossipEncryptionAutogenerate/RoleBinding: enabled with global.gossipEncryption.autoGenerate=true" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/gossip-encryption-autogenerate-rolebinding.yaml \ + --set 'global.gossipEncryption.autoGenerate=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} diff --git a/charts/consul/test/unit/gossip-encryption-autogenerate-serviceaccount.bats b/charts/consul/test/unit/gossip-encryption-autogenerate-serviceaccount.bats new file mode 100644 index 0000000000..782d1b1ad6 --- /dev/null +++ b/charts/consul/test/unit/gossip-encryption-autogenerate-serviceaccount.bats @@ -0,0 +1,50 @@ +#!/usr/bin/env bats + +load _helpers + +@test "gossipEncryptionAutogenerate/ServiceAccount: disabled by default" { + cd `chart_dir` + assert_empty helm template \ + -s templates/gossip-encryption-autogenerate-serviceaccount.yaml \ + . +} + +@test "gossipEncryptionAutogenerate/ServiceAccount: disabled with global.gossipEncryption.autoGenerate=false" { + cd `chart_dir` + assert_empty helm template \ + -s templates/gossip-encryption-autogenerate-serviceaccount.yaml \ + --set 'global.gossipEncryption.autoGenerate=false' \ + . +} + +@test "gossipEncryptionAutogenerate/ServiceAccount: enabled with global.gossipEncryption.autoGenerate=true" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/gossip-encryption-autogenerate-serviceaccount.yaml \ + --set 'global.gossipEncryption.autoGenerate=true' \ + . | tee /dev/stderr | + yq 'length > 0' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +#-------------------------------------------------------------------- +# global.imagePullSecrets + +@test "gossipEncryptionAutogenerate/ServiceAccount: can set image pull secrets" { + cd `chart_dir` + local object=$(helm template \ + -s templates/gossip-encryption-autogenerate-serviceaccount.yaml \ + --set 'global.gossipEncryption.autoGenerate=true' \ + --set 'global.imagePullSecrets[0].name=my-secret' \ + --set 'global.imagePullSecrets[1].name=my-secret2' \ + . | tee /dev/stderr) + + local actual=$(echo "$object" | + yq -r '.imagePullSecrets[0].name' | tee /dev/stderr) + [ "${actual}" = "my-secret" ] + + local actual=$(echo "$object" | + yq -r '.imagePullSecrets[1].name' | tee /dev/stderr) + [ "${actual}" = "my-secret2" ] +} + diff --git a/charts/consul/test/unit/server-statefulset.bats b/charts/consul/test/unit/server-statefulset.bats index ac2be4c2c9..a7e48a3951 100755 --- a/charts/consul/test/unit/server-statefulset.bats +++ b/charts/consul/test/unit/server-statefulset.bats @@ -842,6 +842,28 @@ load _helpers [ "${actual}" = "" ] } +@test "server/StatefulSet: gossip encryption autogeneration properly sets secretName and secretKey" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.gossipEncryption.autoGenerate=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[] | select(.name=="consul") | .env[] | select(.name == "GOSSIP_KEY") | .valueFrom.secretKeyRef | [.name=="RELEASE-NAME-consul-gossip-encryption-key", .key="key"] | all' | tee /dev/stderr) + [ "${actual}" = "true" ] +} + +@test "server/StatefulSet: gossip encryption key is passed via the -encrypt flag" { + cd `chart_dir` + local actual=$(helm template \ + -s templates/server-statefulset.yaml \ + --set 'global.gossipEncryption.autoGenerate=true' \ + . | tee /dev/stderr | + yq '.spec.template.spec.containers[] | select(.name=="consul") | .command | any(contains("-encrypt=\"${GOSSIP_KEY}\""))' + | tee /dev/stderr) + [ "${actual}" = "true" ] +} + + @test "server/StatefulSet: gossip encryption disabled in server StatefulSet when secretName is missing" { cd `chart_dir` local actual=$(helm template \ diff --git a/charts/consul/values.yaml b/charts/consul/values.yaml index 9105565572..a0d4472a74 100644 --- a/charts/consul/values.yaml +++ b/charts/consul/values.yaml @@ -117,26 +117,21 @@ global: # created by this chart. See https://kubernetes.io/docs/concepts/policy/pod-security-policy/. enablePodSecurityPolicies: false - # Configures which Kubernetes secret to retrieve Consul's - # gossip encryption key from (see `-encrypt` (https://consul.io/docs/agent/options#_encrypt)). If secretName or - # secretKey are not set, gossip encryption will not be enabled. The secret must - # be in the same namespace that Consul is installed into. + # Configures Consul's gossip encryption key, set as a Kubernetes secret + # (see `-encrypt` (https://consul.io/docs/agent/options#_encrypt)). + # By default, gossip encryption is not enabled. The gossip encryption key may be set automatically or manually. + # The recommended method is to automatically generate the key. + # To automatically generate and set a gossip encryption key, set autoGenerate to true. + # Values for secretName and secretKey should not be set if autoGenerate is true. + # To manually generate a gossip encryption key, set secretName and secretKey and use Consul to generate + # a Kubernetes secret referencing these values. # - # The secret can be created by running: - # - # ```shell - # $ kubectl create secret generic consul-gossip-encryption-key --from-literal=key=$(consul keygen) # ``` - # - # To reference, use: - # - # ```yaml - # global: - # gossipEncryption: - # secretName: consul-gossip-encryption-key - # secretKey: key + # $ kubectl create secret generic consul-gossip-encryption-key --from-literal=key=$(consul keygen) # ``` gossipEncryption: + # Automatically generate a gossip encryption key and save it to a Kubernetes secret. + autoGenerate: false # secretName is the name of the Kubernetes secret that holds the gossip # encryption key. The secret must be in the same namespace that Consul is installed into. secretName: ""