Skip to content

Commit 2915765

Browse files
committed
tls: add client identity configuration for mTLS
## Background An upcoming release of Gremlin and Chao will support configuring a client identity for TLS, facilitating mutual TLS (mTLS) ## Change * Expose `chao.tls.identity` and `gremlin.tls.identity` objects for configuring a client identity certificate and private key * When enabled, add generation for chao and gremlin secrets to hold certificate and private key data # Test - [ ] unit tests for `remoteSecret` - [ ] unit tests for `createSecret` - [ ] unit tests for `existingSecret` - [ ] unit tests for multiple configurations
1 parent 94929bf commit 2915765

6 files changed

Lines changed: 331 additions & 0 deletions

File tree

gremlin/templates/_helpers.tpl

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,173 @@ Create a computed value for the intended Gremlin secret type which can either be
142142
{{- "https://api.gremlin.com/v1" -}}
143143
{{- end -}}
144144
{{- end -}}
145+
146+
{{/*
147+
gremlinTlsIdentityValidate fails if more than one identity strategy is fully configured for gremlin
148+
*/}}
149+
{{- define "gremlinTlsIdentityValidate" -}}
150+
{{- $remoteSecret := and .Values.gremlin.tls.identity.remoteSecret.cert .Values.gremlin.tls.identity.remoteSecret.key -}}
151+
{{- $createSecret := and .Values.gremlin.tls.identity.createSecret.name .Values.gremlin.tls.identity.createSecret.cert .Values.gremlin.tls.identity.createSecret.key -}}
152+
{{- $existingSecret := and .Values.gremlin.tls.identity.existingSecret.name .Values.gremlin.tls.identity.existingSecret.cert .Values.gremlin.tls.identity.existingSecret.key -}}
153+
{{- $count := 0 -}}
154+
{{- if $remoteSecret }}{{- $count = add $count 1 -}}{{- end -}}
155+
{{- if $createSecret }}{{- $count = add $count 1 -}}{{- end -}}
156+
{{- if $existingSecret }}{{- $count = add $count 1 -}}{{- end -}}
157+
{{- if gt (int $count) 1 -}}
158+
{{- fail "gremlin.tls.identity: only one of remoteSecret, createSecret, or existingSecret should be fully configured" -}}
159+
{{- end -}}
160+
{{- end -}}
161+
162+
{{/*
163+
chaoTlsIdentityValidate fails if more than one identity strategy is fully configured for chao
164+
*/}}
165+
{{- define "chaoTlsIdentityValidate" -}}
166+
{{- $remoteSecret := and .Values.chao.tls.identity.remoteSecret.cert .Values.chao.tls.identity.remoteSecret.key -}}
167+
{{- $createSecret := and .Values.chao.tls.identity.createSecret.name .Values.chao.tls.identity.createSecret.cert .Values.chao.tls.identity.createSecret.key -}}
168+
{{- $existingSecret := and .Values.chao.tls.identity.existingSecret.name .Values.chao.tls.identity.existingSecret.cert .Values.chao.tls.identity.existingSecret.key -}}
169+
{{- $count := 0 -}}
170+
{{- if $remoteSecret }}{{- $count = add $count 1 -}}{{- end -}}
171+
{{- if $createSecret }}{{- $count = add $count 1 -}}{{- end -}}
172+
{{- if $existingSecret }}{{- $count = add $count 1 -}}{{- end -}}
173+
{{- if gt (int $count) 1 -}}
174+
{{- fail "chao.tls.identity: only one of remoteSecret, createSecret, or existingSecret should be fully configured" -}}
175+
{{- end -}}
176+
{{- end -}}
177+
178+
{{/*
179+
gremlinTlsIdentityEnv returns the environment variables needed to configure TLS client identity
180+
When remoteSecret is configured
181+
- sets GREMLIN_TLS_IDENTITY_CERTIFICATE and GREMLIN_TLS_IDENTITY_PRIVATE_KEY to their respective `cert` and `key` values
182+
When createSecret or existingSecret are configured
183+
- sets GREMLIN_TLS_IDENTITY_CERTIFICATE and GREMLIN_TLS_IDENTITY_PRIVATE_KEY to their respective file paths, mounted by gremlinTlsIdentityVolumeMounts
184+
*/}}
185+
{{- define "gremlinTlsIdentityEnv" -}}
186+
{{- if .Values.gremlin.tls.identity.enabled -}}
187+
{{- include "gremlinTlsIdentityValidate" . -}}
188+
{{- if and .Values.gremlin.tls.identity.remoteSecret.cert .Values.gremlin.tls.identity.remoteSecret.key -}}
189+
- name: GREMLIN_TLS_IDENTITY_CERTIFICATE
190+
value: {{ .Values.gremlin.tls.identity.remoteSecret.cert | quote }}
191+
- name: GREMLIN_TLS_IDENTITY_PRIVATE_KEY
192+
value: {{ .Values.gremlin.tls.identity.remoteSecret.key | quote }}
193+
{{- else if and .Values.gremlin.tls.identity.createSecret.cert .Values.gremlin.tls.identity.createSecret.key -}}
194+
- name: GREMLIN_TLS_IDENTITY_CERTIFICATE
195+
value: /var/lib/gremlin/tls/identity/cert
196+
- name: GREMLIN_TLS_IDENTITY_PRIVATE_KEY
197+
value: /var/lib/gremlin/tls/identity/key
198+
{{- else if .Values.gremlin.tls.identity.existingSecret.name -}}
199+
- name: GREMLIN_TLS_IDENTITY_CERTIFICATE
200+
value: /var/lib/gremlin/tls/identity/{{ .Values.gremlin.tls.identity.existingSecret.cert }}
201+
- name: GREMLIN_TLS_IDENTITY_PRIVATE_KEY
202+
value: /var/lib/gremlin/tls/identity/{{ .Values.gremlin.tls.identity.existingSecret.key }}
203+
{{- end -}}
204+
{{- end -}}
205+
{{- end -}}
206+
207+
{{/*
208+
gremlinTlsIdentityVolumeMounts returns the mounts needed to access TLS client identity files
209+
When createSecret or existingSecret are configured
210+
- mounts to desginated secret files under /var/lib/gremlin/tls/identity
211+
*/}}
212+
{{- define "gremlinTlsIdentityVolumeMounts" -}}
213+
{{- if .Values.gremlin.tls.identity.enabled -}}
214+
{{- include "gremlinTlsIdentityValidate" . -}}
215+
{{- if and .Values.gremlin.tls.identity.createSecret.cert .Values.gremlin.tls.identity.createSecret.key -}}
216+
- name: gremlin-tls-identity
217+
mountPath: /var/lib/gremlin/tls/identity
218+
readOnly: true
219+
{{- else if .Values.gremlin.tls.identity.existingSecret.name -}}
220+
- name: gremlin-tls-identity
221+
mountPath: /var/lib/gremlin/tls/identity
222+
readOnly: true
223+
{{- end -}}
224+
{{- end -}}
225+
{{- end -}}
226+
227+
{{/*
228+
gremlinTlsIdentityVolumes returns the volumes that contain TLS client identity files
229+
When createSecret or existingSecret are configured
230+
- defines the volume associated with the desginated secret
231+
*/}}
232+
{{- define "gremlinTlsIdentityVolumes" -}}
233+
{{- if .Values.gremlin.tls.identity.enabled -}}
234+
{{- include "gremlinTlsIdentityValidate" . -}}
235+
{{- if and .Values.gremlin.tls.identity.createSecret.cert .Values.gremlin.tls.identity.createSecret.key -}}
236+
- name: gremlin-tls-identity
237+
secret:
238+
secretName: {{ .Values.gremlin.tls.identity.createSecret.name }}
239+
{{- else if .Values.gremlin.tls.identity.existingSecret.name -}}
240+
- name: gremlin-tls-identity
241+
secret:
242+
secretName: {{ .Values.gremlin.tls.identity.existingSecret.name }}
243+
{{- end -}}
244+
{{- end -}}
245+
{{- end -}}
246+
247+
{{/*
248+
chaoTlsIdentityArgs returns the chao cli arguments needed to configure TLS client identity
249+
When remoteSecret is configured
250+
- sets -tls-identity-cert and -tls-identity-private-key to their respective `cert` and `key` values
251+
When createSecret or existingSecret are configured
252+
- sets -tls-identity-cert and -tls-identity-private-key to their respective file paths, mounted by chaoTlsIdentityVolumeMounts
253+
*/}}
254+
{{- define "chaoTlsIdentityArgs" -}}
255+
{{- if .Values.chao.tls.identity.enabled -}}
256+
{{- include "chaoTlsIdentityValidate" . -}}
257+
{{- if and .Values.chao.tls.identity.remoteSecret.cert .Values.chao.tls.identity.remoteSecret.key -}}
258+
- "-tls_identity_cert"
259+
- {{ .Values.chao.tls.identity.remoteSecret.cert | quote }}
260+
- "-tls_identity_private_key"
261+
- {{ .Values.chao.tls.identity.remoteSecret.key | quote }}
262+
{{- else if and .Values.chao.tls.identity.createSecret.cert .Values.chao.tls.identity.createSecret.key -}}
263+
- "-tls_identity_cert"
264+
- "/var/lib/gremlin/tls/identity/cert"
265+
- "-tls_identity_private_key"
266+
- "/var/lib/gremlin/tls/identity/key"
267+
{{- else if .Values.chao.tls.identity.existingSecret.name -}}
268+
- "-tls_identity_cert"
269+
- "/var/lib/gremlin/tls/identity/{{ .Values.chao.tls.identity.existingSecret.cert }}"
270+
- "-tls_identity_private_key"
271+
- "/var/lib/gremlin/tls/identity/{{ .Values.chao.tls.identity.existingSecret.key }}"
272+
{{- end -}}
273+
{{- end -}}
274+
{{- end -}}
275+
276+
{{/*
277+
chaoTlsIdentityVolumes returns the volumes that contain TLS client identity files
278+
When createSecret or existingSecret are configured
279+
- defines the volume associated with the desginated secret
280+
*/}}
281+
{{- define "chaoTlsIdentityVolumeMounts" -}}
282+
{{- if .Values.chao.tls.identity.enabled -}}
283+
{{- include "chaoTlsIdentityValidate" . -}}
284+
{{- if and .Values.chao.tls.identity.createSecret.cert .Values.chao.tls.identity.createSecret.key -}}
285+
- name: chao-tls-identity
286+
mountPath: /var/lib/gremlin/tls/identity
287+
readOnly: true
288+
{{- else if .Values.chao.tls.identity.existingSecret.name -}}
289+
- name: chao-tls-identity
290+
mountPath: /var/lib/gremlin/tls/identity
291+
readOnly: true
292+
{{- end -}}
293+
{{- end -}}
294+
{{- end -}}
295+
296+
{{/*
297+
chaoTlsIdentityVolumes returns the volumes that contain TLS client identity files
298+
When createSecret or existingSecret are configured
299+
- defines the volume associated with the desginated secret
300+
*/}}
301+
{{- define "chaoTlsIdentityVolumes" -}}
302+
{{- if .Values.chao.tls.identity.enabled -}}
303+
{{- include "chaoTlsIdentityValidate" . -}}
304+
{{- if and .Values.chao.tls.identity.createSecret.cert .Values.chao.tls.identity.createSecret.key -}}
305+
- name: chao-tls-identity
306+
secret:
307+
secretName: {{ .Values.chao.tls.identity.createSecret.name }}
308+
{{- else if .Values.chao.tls.identity.existingSecret.name -}}
309+
- name: chao-tls-identity
310+
secret:
311+
secretName: {{ .Values.chao.tls.identity.existingSecret.name }}
312+
{{- end -}}
313+
{{- end -}}
314+
{{- end -}}

gremlin/templates/chao-deployment.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ spec:
124124
- "-namespaces"
125125
- "{{ join "," .Values.chao.namespaces }}"
126126
{{- end}}
127+
{{- if include "chaoTlsIdentityArgs" . }}
128+
{{- include "chaoTlsIdentityArgs" . | nindent 12 }}
129+
{{- end }}
127130
imagePullPolicy: {{ .Values.chaoimage.pullPolicy }}
128131
name: chao
129132
{{- if (or ((eq (include "gremlin.secretType" .) "certificate")) .Values.ssl.certFile) }}
@@ -138,6 +141,9 @@ spec:
138141
- name: ssl-cert-file
139142
mountPath: /etc/gremlin/ssl
140143
readOnly: true
144+
{{- end }}
145+
{{- if include "chaoTlsIdentityVolumeMounts" . }}
146+
{{- include "chaoTlsIdentityVolumeMounts" . | nindent 10 }}
141147
{{- end }}
142148
volumes:
143149
- name: gremlin-cert
@@ -148,6 +154,9 @@ spec:
148154
secret:
149155
secretName: ssl-cert-file
150156
{{ end }}
157+
{{- if include "chaoTlsIdentityVolumes" . }}
158+
{{- include "chaoTlsIdentityVolumes" . | nindent 6 }}
159+
{{- end }}
151160
{{- if .Values.chao.priorityClassName }}
152161
priorityClassName: {{ .Values.chao.priorityClassName }}
153162
{{- end }}

gremlin/templates/daemonset.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,9 @@ spec:
154154
- name: SSL_CERT_DIR
155155
value: {{ .Values.ssl.certDir }}
156156
{{- end }}
157+
{{- if include "gremlinTlsIdentityEnv" . }}
158+
{{- include "gremlinTlsIdentityEnv" . | nindent 10 }}
159+
{{- end }}
157160
{{- with .Values.gremlin.extraEnv }}
158161
{{- toYaml . | nindent 10 }}
159162
{{- end }}
@@ -183,6 +186,9 @@ spec:
183186
mountPath: /etc/gremlin/ssl
184187
readOnly: true
185188
{{- end }}
189+
{{- if include "gremlinTlsIdentityVolumeMounts" . }}
190+
{{- include "gremlinTlsIdentityVolumeMounts" . | nindent 10 }}
191+
{{- end }}
186192
volumes:
187193
- name: cgroup-root
188194
hostPath:
@@ -220,6 +226,9 @@ spec:
220226
secret:
221227
secretName: ssl-cert-file
222228
{{- end }}
229+
{{- if include "gremlinTlsIdentityVolumes" . }}
230+
{{- include "gremlinTlsIdentityVolumes" . | nindent 8 }}
231+
{{- end }}
223232
{{- if .Values.gremlin.priorityClassName }}
224233
priorityClassName: {{ .Values.gremlin.priorityClassName }}
225234
{{- end }}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{{- if and .Values.chao.tls.identity.enabled .Values.chao.tls.identity.createSecret.name (and .Values.chao.tls.identity.createSecret.cert .Values.chao.tls.identity.createSecret.key) }}
2+
---
3+
apiVersion: v1
4+
kind: Secret
5+
metadata:
6+
name: {{ .Values.chao.tls.identity.createSecret.name }}
7+
namespace: {{ .Release.Namespace }}
8+
labels:
9+
app.kubernetes.io/name: {{ include "gremlin.name" . }}
10+
helm.sh/chart: {{ include "gremlin.chart" . }}
11+
app.kubernetes.io/instance: {{ .Release.Name }}
12+
app.kubernetes.io/managed-by: {{ .Release.Service }}
13+
version: v1
14+
type: kubernetes.io/Opaque
15+
data:
16+
cert: {{ .Values.chao.tls.identity.createSecret.cert | toString | b64enc }}
17+
key: {{ .Values.chao.tls.identity.createSecret.key | toString | b64enc }}
18+
{{- end }}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{{- if and .Values.gremlin.tls.identity.enabled .Values.gremlin.tls.identity.createSecret.name (and .Values.gremlin.tls.identity.createSecret.cert .Values.gremlin.tls.identity.createSecret.key) }}
2+
---
3+
apiVersion: v1
4+
kind: Secret
5+
metadata:
6+
name: {{ .Values.gremlin.tls.identity.createSecret.name }}
7+
namespace: {{ .Release.Namespace }}
8+
labels:
9+
app.kubernetes.io/name: {{ include "gremlin.name" . }}
10+
helm.sh/chart: {{ include "gremlin.chart" . }}
11+
app.kubernetes.io/instance: {{ .Release.Name }}
12+
app.kubernetes.io/managed-by: {{ .Release.Service }}
13+
version: v1
14+
type: kubernetes.io/Opaque
15+
data:
16+
cert: {{ .Values.gremlin.tls.identity.createSecret.cert | toString | b64enc }}
17+
key: {{ .Values.gremlin.tls.identity.createSecret.key | toString | b64enc }}
18+
{{- end }}

gremlin/values.yaml

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,59 @@ gremlin:
290290
pushCIDRTags:
291291
enabled: true
292292

293+
# gremlin.tls -
294+
# A collection of TLS configurations specific to the Gremlin Daemonset.
295+
tls:
296+
# gremlin.tls.identity -
297+
# Configuration items for configuring the TLS identity of the Gremlin HTTP client for mTLS.
298+
# Identity values are made up of
299+
# - `cert` portion, containing the leaf certificate for the client (plus any intermediate certificates)
300+
# - `key` portion, containing the private key for the client
301+
#
302+
# These values can be supplied in three different strategies:
303+
# - `remoteSecret`: a collection of ARN values for AWS Secrets Manager
304+
# - `createSecret`: content supplied directly to this chart for which a new Kubernetes secret will be created
305+
# - `existingSecret`: a reference to an existing Kubernetes secret (e.g. created from cert-manager)
306+
#
307+
# Only one of the above strategies should be fully configured. A strategy is not used until all of its fields are configured.
308+
identity:
309+
# gremlin.tls.identity.enabled -
310+
# Decides whether the Gremlin Daemonset should be configured for a TLS identity
311+
enabled: false
312+
# gremlin.tls.identity.remoteSecret -
313+
# The remoteSecret strategy accepts `cert` and `key` strings in the form of ARN values for AWS Secrets Manager
314+
remoteSecret:
315+
# gremlin.tls.identity.remoteSecret.cert -
316+
# The ARN for the identity certificate
317+
cert: ""
318+
# gremlin.tls.identity.remoteSecret.key -
319+
# The ARN for the identity private key
320+
key: ""
321+
# gremlin.tls.identity.createSecret -
322+
# The createSecret strategy accepts PEM encoded certificate and private key content to create a new Kubernetes secret
323+
createSecret:
324+
# gremlin.tls.identity.createSecret.name -
325+
# The name of the new secret
326+
name: "gremlin-tls-identity"
327+
# gremlin.tls.identity.createSecret.cert -
328+
# The PEM-encoded certificate (expected to be a multi-line string)
329+
cert: ""
330+
# gremlin.tls.identity.createSecret.key -
331+
# The PEM-encoded private key (expected to be a multi-line string)
332+
key: ""
333+
# gremlin.tls.identity.existingSecret -
334+
# The existingSecret strategy accepts a reference to an existing Kubernetes secret
335+
existingSecret:
336+
# gremlin.tls.identity.existingSecret.name -
337+
# The name of the existing secret
338+
name: ""
339+
# gremlin.tls.identity.existingSecret.cert -
340+
# The name of the key inside the existing secret that maps to the certificate value
341+
cert: "tls.crt"
342+
# gremlin.tls.identity.existingSecret.key -
343+
# The name of the key inside the existing secret that maps to the private key value
344+
key: "tls.key"
345+
293346
chao:
294347

295348
# chao.create
@@ -332,6 +385,60 @@ chao:
332385
# list of namespaces for Gremlin to watch for attacking
333386
namespaces: []
334387

388+
389+
# chao.tls -
390+
# A collection of TLS configurations specific to the Chao Deployment.
391+
tls:
392+
# chao.tls.identity -
393+
# Configuration items for configuring the TLS identity of the Gremlin HTTP client for mTLS.
394+
# Identity values are made up of
395+
# - `cert` portion, containing the leaf certificate for the client (plus any intermediate certificates)
396+
# - `key` portion, containing the private key for the client
397+
#
398+
# These values can be supplied in three different strategies:
399+
# - `remoteSecret`: a collection of ARN values for AWS Secrets Manager
400+
# - `createSecret`: content supplied directly to this chart for which a new Kubernetes secret will be created
401+
# - `existingSecret`: a reference to an existing Kubernetes secret (e.g. created from cert-manager)
402+
#
403+
# Only one of the above strategies should be fully configured. A strategy is not used until all of its fields are configured.
404+
identity:
405+
# chao.tls.identity.enabled -
406+
# Decides whether the Chao Deployment should be configured for a TLS identity
407+
enabled: false
408+
# chao.tls.identity.remoteSecret -
409+
# The remoteSecret strategy accepts `cert` and `key` strings in the form of ARN values for AWS Secrets Manager
410+
remoteSecret:
411+
# chao.tls.identity.remoteSecret.cert -
412+
# The ARN for the identity certificate
413+
cert: ""
414+
# chao.tls.identity.remoteSecret.key -
415+
# The ARN for the identity private key
416+
key: ""
417+
# chao.tls.identity.createSecret -
418+
# The createSecret strategy accepts PEM encoded certificate and private key content to create a new Kubernetes secret
419+
createSecret:
420+
# chao.tls.identity.createSecret.name -
421+
# The name of the new secret
422+
name: "chao-tls-identity"
423+
# chao.tls.identity.createSecret.cert -
424+
# The PEM-encoded certificate (expected to be a multi-line string)
425+
cert: ""
426+
# chao.tls.identity.createSecret.key -
427+
# The PEM-encoded private key (expected to be a multi-line string)
428+
key: ""
429+
# chao.tls.identity.existingSecret -
430+
# The existingSecret strategy accepts a reference to an existing Kubernetes secret
431+
existingSecret:
432+
# chao.tls.identity.existingSecret.name -
433+
# The name of the existing secret
434+
name: ""
435+
# chao.tls.identity.existingSecret.cert -
436+
# The name of the key inside the existing secret that maps to the certificate value
437+
cert: "tls.crt"
438+
# chao.tls.identity.existingSecret.key -
439+
# The name of the key inside the existing secret that maps to the private key value
440+
key: "tls.key"
441+
335442
ssl:
336443
# ssl.certFile -
337444
# Add a certificate file to Gremlin's set of certificate authorities. This argument expects a file containing the

0 commit comments

Comments
 (0)