Skip to content

Commit aa11b2a

Browse files
Mike Terharericpaulsenjohnstcn
authored
feat: add client certs to coderd (#209)
* add client certs to coderd * update ssl to tls * add example with client/server cert * remove db password from clientcerts * feat: test coverage for coderd client tls * chore: refactor client TLS to use TLS secret type * chore: run helm-docs to update README * chore: naming & examples refactoring * run make README.md Co-authored-by: Eric Paulsen <ericpaulsen@coder.com> Co-authored-by: Cian Johnston <cian@coder.com>
1 parent fc92255 commit aa11b2a

File tree

7 files changed

+100
-1
lines changed

7 files changed

+100
-1
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@ View [our docs](https://coder.com/docs/setup/installation) for detailed installa
2525
| certs | object | Certificate that will be mounted inside Coder services. | `{"secret":{"key":"","name":""}}` |
2626
| certs.secret.key | string | Key pointing to a certificate in the secret. | `""` |
2727
| certs.secret.name | string | Name of the secret. | `""` |
28-
| coderd | object | Primary service responsible for all things Coder! | `{"affinity":{"podAntiAffinity":{"preferredDuringSchedulingIgnoredDuringExecution":[{"podAffinityTerm":{"labelSelector":{"matchExpressions":[{"key":"app.kubernetes.io/name","operator":"In","values":["coderd"]}]},"topologyKey":"kubernetes.io/hostname"},"weight":1}]}},"builtinProviderServiceAccount":{"annotations":{},"labels":{}},"devurlsHost":"","extraLabels":{},"image":"","networkPolicy":{"enable":true},"oidc":{"enableRefresh":false,"redirectOptions":{}},"podSecurityContext":{"runAsGroup":1000,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}},"proxy":{"exempt":"cluster.local","http":"","https":""},"replicas":1,"resources":{"limits":{"cpu":"250m","memory":"512Mi"},"requests":{"cpu":"250m","memory":"512Mi"}},"reverseProxy":{"headers":[],"trustedOrigins":[]},"satellite":{"accessURL":"","enable":false,"primaryURL":""},"securityContext":{"allowPrivilegeEscalation":false,"readOnlyRootFilesystem":true,"runAsGroup":1000,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}},"serviceNodePorts":{"http":null,"https":null},"serviceSpec":{"externalTrafficPolicy":"Local","loadBalancerIP":"","loadBalancerSourceRanges":[],"type":"LoadBalancer"},"superAdmin":{"passwordSecret":{"key":"password","name":""}},"tls":{"devurlsHostSecretName":"","hostSecretName":""},"trustProxyIP":false}` |
28+
| coderd | object | Primary service responsible for all things Coder! | `{"affinity":{"podAntiAffinity":{"preferredDuringSchedulingIgnoredDuringExecution":[{"podAffinityTerm":{"labelSelector":{"matchExpressions":[{"key":"app.kubernetes.io/name","operator":"In","values":["coderd"]}]},"topologyKey":"kubernetes.io/hostname"},"weight":1}]}},"builtinProviderServiceAccount":{"annotations":{},"labels":{}},"clientTLS":{"secretName":""},"devurlsHost":"","extraLabels":{},"image":"","networkPolicy":{"enable":true},"oidc":{"enableRefresh":false,"redirectOptions":{}},"podSecurityContext":{"runAsGroup":1000,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}},"proxy":{"exempt":"cluster.local","http":"","https":""},"replicas":1,"resources":{"limits":{"cpu":"250m","memory":"512Mi"},"requests":{"cpu":"250m","memory":"512Mi"}},"reverseProxy":{"headers":[],"trustedOrigins":[]},"satellite":{"accessURL":"","enable":false,"primaryURL":""},"securityContext":{"allowPrivilegeEscalation":false,"readOnlyRootFilesystem":true,"runAsGroup":1000,"runAsNonRoot":true,"runAsUser":1000,"seccompProfile":{"type":"RuntimeDefault"}},"serviceNodePorts":{"http":null,"https":null},"serviceSpec":{"externalTrafficPolicy":"Local","loadBalancerIP":"","loadBalancerSourceRanges":[],"type":"LoadBalancer"},"superAdmin":{"passwordSecret":{"key":"password","name":""}},"tls":{"devurlsHostSecretName":"","hostSecretName":""},"trustProxyIP":false}` |
2929
| coderd.affinity | object | Allows specifying an affinity rule for the `coderd` deployment. The default rule prefers to schedule coderd pods on different nodes, which is only applicable if coderd.replicas is greater than 1. | `{"podAntiAffinity":{"preferredDuringSchedulingIgnoredDuringExecution":[{"podAffinityTerm":{"labelSelector":{"matchExpressions":[{"key":"app.kubernetes.io/name","operator":"In","values":["coderd"]}]},"topologyKey":"kubernetes.io/hostname"},"weight":1}]}}` |
3030
| coderd.builtinProviderServiceAccount | object | Customize the built-in Kubernetes provider service account. | `{"annotations":{},"labels":{}}` |
3131
| coderd.builtinProviderServiceAccount.annotations | object | A KV mapping of annotations. See: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ | `{}` |
3232
| coderd.builtinProviderServiceAccount.labels | object | Add labels to the service account used for the built-in provider. | `{}` |
33+
| coderd.clientTLS | object | Client-side TLS configuration for coderd. | `{"secretName":""}` |
34+
| coderd.clientTLS.secretName | string | Secret containing a PEM encoded cert file. | `""` |
3335
| coderd.devurlsHost | string | Wildcard hostname to allow matching against custom-created dev URLs. Leaving as an empty string results in DevURLs being disabled. | `""` |
3436
| coderd.extraLabels | object | Allows specifying additional labels to pods in the `coderd` deployment (.spec.template.metadata.labels). | `{}` |
3537
| coderd.image | string | Injected by Coder during release. | `""` |
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# These sections are used for the client side of certificate authentication.
2+
coderd:
3+
clientTLS:
4+
secretName: "client-cert-secret"
5+
6+
postgres:
7+
default:
8+
enable: false
9+
host: "some.rds.hostname"
10+
port: 5432
11+
user: "coder"
12+
database: "coder"
13+
sslMode: "require"
14+
ssl:
15+
certSecret:
16+
name: "database-client-cert-secret"
17+
key: "database-client-cert-filename.crt"
18+
keySecret:
19+
name: "database-client-key-secret"
20+
key: "database-client-key-filename.key"
21+
rootCertSecret:
22+
name: "database-root-cert-secret"
23+
key: "database-root-cert-filename.crt"

templates/_common.tpl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ volumes:
9090
secret:
9191
secretName: {{ .Values.postgres.ssl.rootCertSecret.name | quote }}
9292
{{- end }}
93+
{{- if ne .Values.coderd.clientTLS.secretName "" }}
94+
- name: clientcert
95+
secret:
96+
secretName: {{ .Values.coderd.clientTLS.secretName | quote }}
97+
{{- end }}
9398
{{- end }}
9499

95100
# coder.volumeMounts adds a volume mounts stanza if a cert.secret is
@@ -128,6 +133,11 @@ volumeMounts:
128133
mountPath: /etc/ssl/certs/pg/rootcert
129134
readOnly: true
130135
{{- end }}
136+
{{- if ne .Values.coderd.clientTLS.secretName "" }}
137+
- name: clientcert
138+
mountPath: /etc/ssl/certs/client
139+
readOnly: true
140+
{{- end }}
131141
{{- end }}
132142

133143
# coder.serviceTolerations adds tolerations if any are specified to

templates/coderd.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,12 @@ spec:
187187
value: {{ join "," .Values.coderd.reverseProxy.trustedOrigins | quote }}
188188
- name: PROXY_TRUSTED_HEADERS
189189
value: {{ join "," .Values.coderd.reverseProxy.headers | quote }}
190+
{{- if ne .Values.coderd.clientTLS.secretName "" }}
191+
- name: SSL_CLIENT_CERT_FILE
192+
value: "/etc/ssl/certs/client/tls.crt"
193+
- name: SSL_CLIENT_KEY_FILE
194+
value: "/etc/ssl/certs/client/tls.key"
195+
{{- end }}
190196
{{- include "coder.workspaces.configMapEnv" . | indent 12 }}
191197
{{- include "coder.postgres.env" . | indent 12 }}
192198
command:

tests/client_tls_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package tests
2+
3+
import (
4+
"path/filepath"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
9+
v1 "k8s.io/api/core/v1"
10+
)
11+
12+
// ensures client TLS certs are correctly applied to coderd
13+
func Test_ClientTLS(t *testing.T) {
14+
t.Parallel()
15+
16+
var (
17+
chart = LoadChart(t)
18+
19+
certVolName = "clientcert"
20+
certSecretName = "coder-tls-cert"
21+
certMountPath = "/etc/ssl/certs/client"
22+
)
23+
24+
objs := chart.MustRender(t, func(cv *CoderValues) {
25+
cv.Coderd.ClientTLS = &CoderdClientTLSValues{
26+
SecretName: &certSecretName,
27+
}
28+
})
29+
30+
coderd := MustFindDeployment(t, objs, "coderd")
31+
require.Len(t, coderd.Spec.Template.Spec.Containers, 1)
32+
coderdCtr := coderd.Spec.Template.Spec.Containers[0]
33+
envVars := EnvVarsAsMap(coderdCtr.Env)
34+
35+
// Assert volumes and volume mounts for both the cert and key.
36+
AssertVolume(t, coderd.Spec.Template.Spec.Volumes, certVolName, func(t testing.TB, v v1.Volume) {
37+
require.NotNil(t, v.Secret)
38+
assert.Equal(t, certSecretName, v.Secret.SecretName)
39+
})
40+
41+
AssertVolumeMount(t, coderdCtr.VolumeMounts, certVolName, func(t testing.TB, v v1.VolumeMount) {
42+
assert.Equal(t, certMountPath, v.MountPath)
43+
assert.True(t, v.ReadOnly)
44+
})
45+
46+
assert.Equal(t, envVars["SSL_CLIENT_CERT_FILE"], filepath.Join(certMountPath, "tls.crt"))
47+
assert.Equal(t, envVars["SSL_CLIENT_KEY_FILE"], filepath.Join(certMountPath, "tls.key"))
48+
}

tests/values.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ type CoderdValues struct {
9494
Proxy *CoderdProxyValues `json:"proxy" yaml:"proxy"`
9595
ReverseProxy *CoderdReverseProxyValues `json:"reverseProxy" yaml:"reverseProxy"`
9696
NetworkPolicy *CoderdNetworkPolicyValues `json:"networkPolicy" yaml:"networkPolicy"`
97+
ClientTLS *CoderdClientTLSValues `json:"clientTLS" yaml:"clientTLS"`
98+
}
99+
100+
type CoderdClientTLSValues struct {
101+
SecretName *string `json:"secretName" yaml:"secretName"`
97102
}
98103

99104
// CoderdServiceNodePortsValues reflect values from

values.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ coderd:
5151
# coderd.tls.devurlsHostSecretName -- The secret to use for DevURL TLS.
5252
devurlsHostSecretName: ""
5353

54+
# coderd.clientTLS -- Client-side TLS configuration for coderd.
55+
clientTLS:
56+
# coderd.clientTLS.secretName -- Secret containing a PEM encoded cert file.
57+
secretName: ""
58+
5459
# coderd.proxy -- Whether Coder should initiate outbound connections using
5560
# a proxy.
5661
proxy:

0 commit comments

Comments
 (0)