From f8be9cb6bea0ccbacb44a4cd5bd30775f2f0311d Mon Sep 17 00:00:00 2001 From: Hugo Shaka Date: Wed, 3 Aug 2022 12:23:40 -0400 Subject: [PATCH] helm: Add support for mounting existing TLS root CA (#13671) * helm: Add support for mounting existing TLS root CA #12594 * Document per-database CA trust --- .../helm-reference/teleport-kube-agent.mdx | 69 ++++++++ .../.lint/existing-tls-secret-with-ca.yaml | 6 + .../templates/deployment.yaml | 23 ++- .../templates/statefulset.yaml | 21 ++- .../__snapshot__/deployment_test.yaml.snap | 130 +++++++++++++++ .../__snapshot__/statefulset_test.yaml.snap | 154 ++++++++++++++++++ .../tests/deployment_test.yaml | 66 ++++++++ .../tests/statefulset_test.yaml | 55 +++++++ .../teleport-kube-agent/values.schema.json | 14 ++ .../chart/teleport-kube-agent/values.yaml | 10 ++ 10 files changed, 546 insertions(+), 2 deletions(-) create mode 100644 examples/chart/teleport-kube-agent/.lint/existing-tls-secret-with-ca.yaml diff --git a/docs/pages/setup/helm-reference/teleport-kube-agent.mdx b/docs/pages/setup/helm-reference/teleport-kube-agent.mdx index a97043ca276..9115bbf7e63 100644 --- a/docs/pages/setup/helm-reference/teleport-kube-agent.mdx +++ b/docs/pages/setup/helm-reference/teleport-kube-agent.mdx @@ -364,6 +364,34 @@ You can specify multiple databases by adding additional list elements. You can see a list of all the supported [values which can be used in a Teleport database service configuration here](../../database-access/reference/configuration.mdx). + + Database CAs can be trusted on a per-database basis. + You must create a secret containing the database CA certificate in the same namespace as Teleport using a command like: + + ```code + $ kubectl create secret generic my-postgres-ca --from-file=ca.pem=/path/to/database-ca.pem + ``` + + Then, deploy the Helm chart with the following values: + + ```yaml + databases: + - name: my-postgres + uri: postgres.example.com:5432 + protocol: postgres + tls: + ca_cert_file: "/etc/teleport-tls-db/my-postgres/ca.pem" + extraVolumes: + - name: my-postgres-ca + secret: + secretName: my-postgres-ca + extraVolumeMounts: + - name: my-postgres-ca + mountPath: /etc/teleport-tls-db/my-postgres + readOnly: true + ``` + + ## `dbResources` | Type | Default value | Required? | @@ -497,6 +525,47 @@ This can be used for joining a Teleport instance to a Teleport cluster which doe One option might be to use Teleport's built-in [ACME support](./teleport-cluster.mdx#acme) or enable [cert-manager support](./teleport-cluster.mdx#highavailabilitycertmanager). +## `tls` + +### `existingCASecretName` + +| Type | Default value | +| - | - | +| `string` | `""` | + +`tls.existingCASecretName` sets the `SSL_CERT_FILE` environment variable to load a trusted CA or bundle in PEM format into Teleport pods. +This can be set to inject a root and/or intermediate CA so that Teleport can build a full trust chain on startup. +The injected CA will be used to validate TLS communications, with the Proxy Service, with upstream applications or databases. + + +The recommended way to trust a database CA is to do it per-database instead of adding the CA to the global Teleport trust store. +It allows to trust multiple CAs while limiting the trust scope to their specific databases. See [the `databases` section](#databases). + + +You must create a secret containing the CA certs in the same namespace as Teleport using a command like: + +```code +$ kubectl create secret generic my-root-ca --from-file=ca.pem=/path/to/root-ca.pem +``` + + + The key containing the root CA in the secret must be `ca.pem`. + + + + + ```yaml + tls: + existingCASecretName: my-root-ca + ``` + + + ```shell + --set tls.existingSecretName=my-root-ca + ``` + + + ## `existingDataVolume` | Type | Default value | diff --git a/examples/chart/teleport-kube-agent/.lint/existing-tls-secret-with-ca.yaml b/examples/chart/teleport-kube-agent/.lint/existing-tls-secret-with-ca.yaml new file mode 100644 index 00000000000..a8e2a468fd7 --- /dev/null +++ b/examples/chart/teleport-kube-agent/.lint/existing-tls-secret-with-ca.yaml @@ -0,0 +1,6 @@ +authToken: auth-token +proxyAddr: proxy.example.com:3080 +roles: kube +kubeClusterName: test-kube-cluster +tls: + existingCASecretName: "helm-lint-existing-tls-secret-ca" diff --git a/examples/chart/teleport-kube-agent/templates/deployment.yaml b/examples/chart/teleport-kube-agent/templates/deployment.yaml index 133272011c4..18b63464669 100644 --- a/examples/chart/teleport-kube-agent/templates/deployment.yaml +++ b/examples/chart/teleport-kube-agent/templates/deployment.yaml @@ -100,6 +100,11 @@ spec: readOnly: true - mountPath: /var/lib/teleport name: "data" + {{- if .Values.tls.existingCASecretName }} + - mountPath: /etc/teleport-tls-ca + name: "teleport-tls-ca" + readOnly: true + {{- end }} {{- if .Values.extraVolumeMounts }} {{- toYaml .Values.extraVolumeMounts | nindent 8 }} {{- end }} @@ -114,10 +119,16 @@ spec: {{- if .Values.imagePullPolicy }} imagePullPolicy: {{ toYaml .Values.imagePullPolicy }} {{- end }} - {{- if .Values.extraEnv }} + {{- if or .Values.extraEnv .Values.tls.existingCASecretName }} env: + {{- if (gt (len .Values.extraEnv) 0) }} {{- toYaml .Values.extraEnv | nindent 8 }} {{- end }} + {{- if .Values.tls.existingCASecretName }} + - name: SSL_CERT_FILE + value: /etc/teleport-tls-ca/ca.pem + {{- end }} + {{- end }} args: - "--diag-addr=0.0.0.0:3000" {{- if .Values.insecureSkipProxyTLSVerify }} @@ -167,6 +178,11 @@ spec: readOnly: true - mountPath: /var/lib/teleport name: {{ default "data" .Values.existingDataVolume }} + {{- if .Values.tls.existingCASecretName }} + - mountPath: /etc/teleport-tls-ca + name: "teleport-tls-ca" + readOnly: true + {{- end }} {{- if .Values.extraVolumeMounts }} {{- toYaml .Values.extraVolumeMounts | nindent 8 }} {{- end }} @@ -181,6 +197,11 @@ spec: - name: "data" emptyDir: {} {{- end }} + {{- if .Values.tls.existingCASecretName }} + - name: "teleport-tls-ca" + secret: + secretName: {{ .Values.tls.existingCASecretName }} + {{- end }} {{- if .Values.extraVolumes }} {{- toYaml .Values.extraVolumes | nindent 6 }} {{- end }} diff --git a/examples/chart/teleport-kube-agent/templates/statefulset.yaml b/examples/chart/teleport-kube-agent/templates/statefulset.yaml index a0d897af259..c357dd00366 100644 --- a/examples/chart/teleport-kube-agent/templates/statefulset.yaml +++ b/examples/chart/teleport-kube-agent/templates/statefulset.yaml @@ -94,6 +94,11 @@ spec: readOnly: true - mountPath: /var/lib/teleport name: "{{ .Release.Name }}-teleport-data" + {{- if .Values.tls.existingCASecretName }} + - mountPath: /etc/teleport-tls-ca + name: "teleport-tls-ca" + readOnly: true + {{- end }} {{- if .Values.extraVolumeMounts }} {{- toYaml .Values.extraVolumeMounts | nindent 8 }} {{- end }} @@ -123,6 +128,10 @@ spec: fieldPath: metadata.namespace - name: RELEASE_NAME value: {{ .Release.Name }} + {{- if .Values.tls.existingCASecretName }} + - name: SSL_CERT_FILE + value: /etc/teleport-tls-ca/ca.pem + {{- end }} {{- if .Values.extraEnv }} {{- toYaml .Values.extraEnv | nindent 10 }} {{- end }} @@ -180,6 +189,11 @@ spec: - mountPath: /var/lib/teleport name: "data" {{- end }} +{{- if .Values.tls.existingCASecretName }} + - mountPath: /etc/teleport-tls-ca + name: "teleport-tls-ca" + readOnly: true +{{- end }} {{- if .Values.extraVolumeMounts }} {{- toYaml .Values.extraVolumeMounts | nindent 8 }} {{- end }} @@ -194,6 +208,11 @@ spec: - name: "data" emptyDir: {} {{- end}} +{{- if .Values.tls.existingCASecretName }} + - name: "teleport-tls-ca" + secret: + secretName: {{ .Values.tls.existingCASecretName }} +{{- end }} {{- if .Values.extraVolumes }} {{- toYaml .Values.extraVolumes | nindent 6 }} {{- end }} @@ -207,4 +226,4 @@ spec: resources: requests: storage: {{ .Values.storage.requests }} -{{- end }} \ No newline at end of file +{{- end }} diff --git a/examples/chart/teleport-kube-agent/tests/__snapshot__/deployment_test.yaml.snap b/examples/chart/teleport-kube-agent/tests/__snapshot__/deployment_test.yaml.snap index ca3765d166c..86f818c3371 100644 --- a/examples/chart/teleport-kube-agent/tests/__snapshot__/deployment_test.yaml.snap +++ b/examples/chart/teleport-kube-agent/tests/__snapshot__/deployment_test.yaml.snap @@ -723,6 +723,136 @@ should mount extraVolumes and extraVolumeMounts if action is Upgrade: - name: my-mount secret: secretName: mySecret +should mount tls.existingCASecretName and set environment when set in values if action is Upgrade: + 1: | + containers: + - args: + - --diag-addr=0.0.0.0:3000 + env: + - name: SSL_CERT_FILE + value: /etc/teleport-tls-ca/ca.pem + image: quay.io/gravitational/teleport:11.0.0-dev + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 6 + httpGet: + path: /healthz + port: diag + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 1 + name: teleport + ports: + - containerPort: 3000 + name: diag + protocol: TCP + readinessProbe: + failureThreshold: 12 + httpGet: + path: /readyz + port: diag + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 1 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - all + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 9807 + volumeMounts: + - mountPath: /etc/teleport + name: config + readOnly: true + - mountPath: /etc/teleport-secrets + name: auth-token + readOnly: true + - mountPath: /var/lib/teleport + name: data + - mountPath: /etc/teleport-tls-ca + name: teleport-tls-ca + readOnly: true + serviceAccountName: RELEASE-NAME + volumes: + - configMap: + name: RELEASE-NAME + name: config + - name: auth-token + secret: + secretName: teleport-kube-agent-join-token + - emptyDir: {} + name: data + - name: teleport-tls-ca + secret: + secretName: helm-lint-existing-tls-secret-ca +should mount tls.existingCASecretName and set extra environment when set in values if action is Upgrade: + 1: | + containers: + - args: + - --diag-addr=0.0.0.0:3000 + env: + - name: HTTPS_PROXY + value: http://username:password@my.proxy.host:3128 + - name: SSL_CERT_FILE + value: /etc/teleport-tls-ca/ca.pem + image: quay.io/gravitational/teleport:11.0.0-dev + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 6 + httpGet: + path: /healthz + port: diag + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 1 + name: teleport + ports: + - containerPort: 3000 + name: diag + protocol: TCP + readinessProbe: + failureThreshold: 12 + httpGet: + path: /readyz + port: diag + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 1 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - all + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 9807 + volumeMounts: + - mountPath: /etc/teleport + name: config + readOnly: true + - mountPath: /etc/teleport-secrets + name: auth-token + readOnly: true + - mountPath: /var/lib/teleport + name: data + - mountPath: /etc/teleport-tls-ca + name: teleport-tls-ca + readOnly: true + serviceAccountName: RELEASE-NAME + volumes: + - configMap: + name: RELEASE-NAME + name: config + - name: auth-token + secret: + secretName: teleport-kube-agent-join-token + - emptyDir: {} + name: data + - name: teleport-tls-ca + secret: + secretName: helm-lint-existing-tls-secret-ca should provision initContainer correctly when set in values if action is Upgrade: 1: | containers: diff --git a/examples/chart/teleport-kube-agent/tests/__snapshot__/statefulset_test.yaml.snap b/examples/chart/teleport-kube-agent/tests/__snapshot__/statefulset_test.yaml.snap index b5992c0bc5d..6044e0322ce 100644 --- a/examples/chart/teleport-kube-agent/tests/__snapshot__/statefulset_test.yaml.snap +++ b/examples/chart/teleport-kube-agent/tests/__snapshot__/statefulset_test.yaml.snap @@ -1020,6 +1020,160 @@ should mount extraVolumes and extraVolumeMounts: - name: my-mount secret: secretName: mySecret +should mount tls.existingCASecretName and set environment when set in values: + 1: | + containers: + - args: + - --diag-addr=0.0.0.0:3000 + env: + - name: TELEPORT_REPLICA_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: KUBE_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: RELEASE_NAME + value: RELEASE-NAME + - name: SSL_CERT_FILE + value: /etc/teleport-tls-ca/ca.pem + image: quay.io/gravitational/teleport:11.0.0-dev + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 6 + httpGet: + path: /healthz + port: diag + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 1 + name: teleport + ports: + - containerPort: 3000 + name: diag + protocol: TCP + readinessProbe: + failureThreshold: 12 + httpGet: + path: /readyz + port: diag + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 1 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - all + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 9807 + volumeMounts: + - mountPath: /etc/teleport + name: config + readOnly: true + - mountPath: /etc/teleport-secrets + name: auth-token + readOnly: true + - mountPath: /var/lib/teleport + name: data + - mountPath: /etc/teleport-tls-ca + name: teleport-tls-ca + readOnly: true + securityContext: + fsGroup: 9807 + serviceAccountName: RELEASE-NAME + volumes: + - configMap: + name: RELEASE-NAME + name: config + - name: auth-token + secret: + secretName: teleport-kube-agent-join-token + - emptyDir: {} + name: data + - name: teleport-tls-ca + secret: + secretName: helm-lint-existing-tls-secret-ca +should mount tls.existingCASecretName and set extra environment when set in values: + 1: | + containers: + - args: + - --diag-addr=0.0.0.0:3000 + env: + - name: TELEPORT_REPLICA_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: KUBE_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: RELEASE_NAME + value: RELEASE-NAME + - name: SSL_CERT_FILE + value: /etc/teleport-tls-ca/ca.pem + - name: HTTPS_PROXY + value: http://username:password@my.proxy.host:3128 + image: quay.io/gravitational/teleport:11.0.0-dev + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 6 + httpGet: + path: /healthz + port: diag + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 1 + name: teleport + ports: + - containerPort: 3000 + name: diag + protocol: TCP + readinessProbe: + failureThreshold: 12 + httpGet: + path: /readyz + port: diag + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 1 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - all + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 9807 + volumeMounts: + - mountPath: /etc/teleport + name: config + readOnly: true + - mountPath: /etc/teleport-secrets + name: auth-token + readOnly: true + - mountPath: /var/lib/teleport + name: data + - mountPath: /etc/teleport-tls-ca + name: teleport-tls-ca + readOnly: true + securityContext: + fsGroup: 9807 + serviceAccountName: RELEASE-NAME + volumes: + - configMap: + name: RELEASE-NAME + name: config + - name: auth-token + secret: + secretName: teleport-kube-agent-join-token + - emptyDir: {} + name: data + - name: teleport-tls-ca + secret: + secretName: helm-lint-existing-tls-secret-ca should not add emptyDir for data when using StatefulSet: 1: | containers: diff --git a/examples/chart/teleport-kube-agent/tests/deployment_test.yaml b/examples/chart/teleport-kube-agent/tests/deployment_test.yaml index 4cc5e6a0993..6e9a1ba425e 100644 --- a/examples/chart/teleport-kube-agent/tests/deployment_test.yaml +++ b/examples/chart/teleport-kube-agent/tests/deployment_test.yaml @@ -499,6 +499,72 @@ tests: - matchSnapshot: path: spec.template.spec + - it: should mount tls.existingCASecretName and set environment when set in values if action is Upgrade + release: + isupgrade: true + set: + # unit test does not support lookup functions, so to test the behavior we use this undoc value + # https://github.com/helm/helm/issues/8137 + unitTestUpgrade: true + values: + - ../.lint/existing-tls-secret-with-ca.yaml + asserts: + - contains: + path: spec.template.spec.volumes + content: + name: teleport-tls-ca + secret: + secretName: helm-lint-existing-tls-secret-ca + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + mountPath: /etc/teleport-tls-ca + name: teleport-tls-ca + readOnly: true + - contains: + path: spec.template.spec.containers[0].env + content: + name: SSL_CERT_FILE + value: /etc/teleport-tls-ca/ca.pem + - matchSnapshot: + path: spec.template.spec + + - it: should mount tls.existingCASecretName and set extra environment when set in values if action is Upgrade + release: + isupgrade: true + set: + # unit test does not support lookup functions, so to test the behavior we use this undoc value + # https://github.com/helm/helm/issues/8137 + unitTestUpgrade: true + values: + - ../.lint/existing-tls-secret-with-ca.yaml + - ../.lint/extra-env.yaml + asserts: + - contains: + path: spec.template.spec.volumes + content: + name: teleport-tls-ca + secret: + secretName: helm-lint-existing-tls-secret-ca + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + mountPath: /etc/teleport-tls-ca + name: teleport-tls-ca + readOnly: true + - contains: + path: spec.template.spec.containers[0].env + content: + name: SSL_CERT_FILE + value: /etc/teleport-tls-ca/ca.pem + - contains: + path: spec.template.spec.containers[0].env + content: + name: HTTPS_PROXY + value: http://username:password@my.proxy.host:3128 + - matchSnapshot: + path: spec.template.spec + - it: should set priorityClassName when set in values if action is Upgrade release: isupgrade: true diff --git a/examples/chart/teleport-kube-agent/tests/statefulset_test.yaml b/examples/chart/teleport-kube-agent/tests/statefulset_test.yaml index c44b44a7806..d5c3b87b0f4 100644 --- a/examples/chart/teleport-kube-agent/tests/statefulset_test.yaml +++ b/examples/chart/teleport-kube-agent/tests/statefulset_test.yaml @@ -424,6 +424,61 @@ tests: - matchSnapshot: path: spec.template.spec + - it: should mount tls.existingCASecretName and set environment when set in values + values: + - ../.lint/existing-tls-secret-with-ca.yaml + asserts: + - contains: + path: spec.template.spec.volumes + content: + name: teleport-tls-ca + secret: + secretName: helm-lint-existing-tls-secret-ca + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + mountPath: /etc/teleport-tls-ca + name: teleport-tls-ca + readOnly: true + - contains: + path: spec.template.spec.containers[0].env + content: + name: SSL_CERT_FILE + value: /etc/teleport-tls-ca/ca.pem + - matchSnapshot: + path: spec.template.spec + + - it: should mount tls.existingCASecretName and set extra environment when set in values + values: + - ../.lint/existing-tls-secret-with-ca.yaml + - ../.lint/extra-env.yaml + asserts: + - contains: + path: spec.template.spec.volumes + content: + name: teleport-tls-ca + secret: + secretName: helm-lint-existing-tls-secret-ca + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + mountPath: /etc/teleport-tls-ca + name: teleport-tls-ca + readOnly: true + - contains: + path: spec.template.spec.containers[0].env + content: + name: SSL_CERT_FILE + value: /etc/teleport-tls-ca/ca.pem + - contains: + path: spec.template.spec.containers[0].env + content: + name: HTTPS_PROXY + value: http://username:password@my.proxy.host:3128 + - matchSnapshot: + path: spec.template.spec + + - it: should set serviceAccountName when set in values values: - ../.lint/stateful.yaml diff --git a/examples/chart/teleport-kube-agent/values.schema.json b/examples/chart/teleport-kube-agent/values.schema.json index cfcf610b551..a7574856447 100644 --- a/examples/chart/teleport-kube-agent/values.schema.json +++ b/examples/chart/teleport-kube-agent/values.schema.json @@ -158,6 +158,20 @@ "type": "boolean", "default": false }, + "tls": { + "$id": "#/properties/tls", + "type": "object", + "required": [ + "existingCASecretName" + ], + "properties": { + "existingCASecretName": { + "$id": "#/properties/tls/properties/existingCASecretName", + "type": "string", + "default": "" + } + } + }, "existingDataVolume": { "$id": "#/properties/existingDataVolume", "type": "string", diff --git a/examples/chart/teleport-kube-agent/values.yaml b/examples/chart/teleport-kube-agent/values.yaml index aff38b8477b..0051f1281c4 100644 --- a/examples/chart/teleport-kube-agent/values.yaml +++ b/examples/chart/teleport-kube-agent/values.yaml @@ -89,6 +89,16 @@ caPin: [] # certificate. insecureSkipProxyTLSVerify: false +# Settings for mounting your own TLS material in the agent pod. +# The agent does not expose a TLS server, so this is only used to trust CAs. +tls: + # Name of an existing secret to use which contains a CA or trust bundle in x509 PEM format. + # This is useful to trust private CAs. + # This will automatically set the SSL_CERT_FILE environment variable to trust the CA. + # Create the secret with `kubectl create secret generic --from-file=ca.pem=/path/to/root-ca.pem` + # The filename inside the secret is important - it _must_ be ca.pem + existingCASecretName: "" + # If set, will use an existing volume mounted via extraVolumes # as the Teleport data directory. # If anything is set under the "storage" key, this will be ignored.