Skip to content

Commit 3b423e3

Browse files
authored
feat: support postgres mtls (#178)
1 parent 92e8f3b commit 3b423e3

File tree

6 files changed

+270
-62
lines changed

6 files changed

+270
-62
lines changed

templates/_common.tpl

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,27 @@ storageClassName: {{ $storageClass | default "" | quote }}
4040
value: {{ .Values.postgres.sslMode | quote }}
4141
- name: DB_NAME
4242
value: {{ .Values.postgres.database | quote }}
43+
{{- if ne .Values.postgres.ssl.certSecret.name "" }}
44+
- name: DB_CERT
45+
value: "/etc/ssl/certs/pg/cert/{{ .Values.postgres.ssl.certSecret.key }}"
46+
{{- end }}
47+
{{- if ne .Values.postgres.ssl.keySecret.name "" }}
48+
- name: DB_KEY
49+
value: "/etc/ssl/certs/pg/key/{{ .Values.postgres.ssl.keySecret.key }}"
50+
{{- end }}
51+
{{- if ne .Values.postgres.ssl.rootCertSecret.name "" }}
52+
- name: DB_ROOT_CERT
53+
value: "/etc/ssl/certs/pg/rootcert/{{ .Values.postgres.ssl.rootCertSecret.key }}"
54+
{{- end }}
4355
{{- end }}
4456
{{- end }}
4557
{{/*
4658
coder.volumes adds a volumes stanza if a cert.secret is provided.
4759
*/}}
4860
{{- define "coder.volumes" }}
49-
{{- if or (merge .Values dict | dig "certs" "secret" "name" false) (ne (include "movedValue" (dict "Values" .Values "Key" "coderd.tls.hostSecretName")) "") }}
5061
volumes:
51-
{{- end }}
62+
- name: tmp-pgcerts
63+
emptyDir: {}
5264
{{- if (merge .Values dict | dig "certs" "secret" "name" false) }}
5365
- name: {{ .Values.certs.secret.name | quote }}
5466
secret:
@@ -64,15 +76,30 @@ volumes:
6476
secret:
6577
secretName: {{ include "movedValue" (dict "Values" .Values "Key" "coderd.tls.devurlsHostSecretName") }}
6678
{{- end }}
79+
{{- if ne .Values.postgres.ssl.certSecret.name "" }}
80+
- name: pgcert
81+
secret:
82+
secretName: {{ .Values.postgres.ssl.certSecret.name | quote }}
83+
{{- end }}
84+
{{- if ne .Values.postgres.ssl.keySecret.name "" }}
85+
- name: pgkey
86+
secret:
87+
secretName: {{ .Values.postgres.ssl.keySecret.name | quote }}
88+
{{- end }}
89+
{{- if ne .Values.postgres.ssl.rootCertSecret.name "" }}
90+
- name: pgrootcert
91+
secret:
92+
secretName: {{ .Values.postgres.ssl.rootCertSecret.name | quote }}
93+
{{- end }}
6794
{{- end }}
6895

6996
{{/*
7097
coder.volumeMounts adds a volume mounts stanza if a cert.secret is provided.
7198
*/}}
7299
{{- define "coder.volumeMounts" }}
73-
{{- if or (merge .Values dict | dig "certs" "secret" "name" false) (ne (include "movedValue" (dict "Values" .Values "Key" "coderd.tls.hostSecretName")) "") }}
74100
volumeMounts:
75-
{{- end }}
101+
- name: tmp-pgcerts
102+
mountPath: /tmp/pgcerts
76103
{{- if (merge .Values dict | dig "certs" "secret" "name" false) }}
77104
- name: {{ .Values.certs.secret.name | quote }}
78105
mountPath: /etc/ssl/certs/{{ .Values.certs.secret.key }}
@@ -88,6 +115,21 @@ volumeMounts:
88115
mountPath: /etc/ssl/certs/devurls
89116
readOnly: true
90117
{{- end }}
118+
{{- if ne .Values.postgres.ssl.certSecret.name "" }}
119+
- name: pgcert
120+
mountPath: /etc/ssl/certs/pg/cert
121+
readOnly: true
122+
{{- end }}
123+
{{- if ne .Values.postgres.ssl.keySecret.name "" }}
124+
- name: pgkey
125+
mountPath: /etc/ssl/certs/pg/key
126+
readOnly: true
127+
{{- end }}
128+
{{- if ne .Values.postgres.ssl.rootCertSecret.name "" }}
129+
- name: pgrootcert
130+
mountPath: /etc/ssl/certs/pg/rootcert
131+
readOnly: true
132+
{{- end }}
91133
{{- end }}
92134
{{/*
93135
coder.serviceTolerations adds tolerations if any are specified to

templates/coderd.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ spec:
9090
{{- else }}
9191
{{- toYaml .Values.coderd.securityContext | nindent 12 }}
9292
{{- end }}
93+
{{- include "coder.volumeMounts" . | indent 10 }}
9394
{{- end }}
9495
containers:
9596
- name: {{ include "coder.serviceName" . }}

tests/examples_test.go

Lines changed: 44 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@ package tests
33
import (
44
"testing"
55

6+
"github.com/stretchr/testify/assert"
67
"github.com/stretchr/testify/require"
7-
"helm.sh/helm/v3/pkg/chart/loader"
88
"helm.sh/helm/v3/pkg/chartutil"
99
"helm.sh/helm/v3/pkg/engine"
10-
appsv1 "k8s.io/api/apps/v1"
1110
corev1 "k8s.io/api/core/v1"
1211
"k8s.io/utils/pointer"
1312
)
@@ -17,50 +16,24 @@ import (
1716
func TestExamples(t *testing.T) {
1817
t.Parallel()
1918

20-
chart, err := loader.LoadDir("..")
21-
require.NoError(t, err, "loaded chart successfully")
22-
require.NotNil(t, chart, "chart must be non-nil")
19+
chart := LoadChart(t)
2320

24-
exampleOpenShift, err := ReadValuesAsMap("../examples/openshift/openshift.values.yaml")
21+
exampleOpenShift, err := ReadValuesFileAsMap("../examples/openshift/openshift.values.yaml")
2522
require.NoError(t, err, "failed to load OpenShift example values")
2623

27-
exampleKind, err := ReadValuesAsMap("../examples/kind/kind.values.yaml")
24+
exampleKind, err := ReadValuesFileAsMap("../examples/kind/kind.values.yaml")
2825
require.NoError(t, err, "failed to load Kind example values")
2926

3027
tests := []struct {
3128
Name string
3229
Values map[string]interface{}
3330
PodSecurityContext *corev1.PodSecurityContext
3431
ContainerSecurityContext *corev1.SecurityContext
32+
Postgres *PostgresValues
3533
}{
3634
{
3735
Name: "default",
3836
Values: nil,
39-
PodSecurityContext: &corev1.PodSecurityContext{
40-
RunAsUser: pointer.Int64(1000),
41-
RunAsGroup: nil,
42-
RunAsNonRoot: pointer.Bool(true),
43-
SeccompProfile: &corev1.SeccompProfile{
44-
Type: corev1.SeccompProfileTypeRuntimeDefault,
45-
LocalhostProfile: nil,
46-
},
47-
},
48-
ContainerSecurityContext: &corev1.SecurityContext{
49-
RunAsUser: nil,
50-
RunAsGroup: nil,
51-
RunAsNonRoot: nil,
52-
Capabilities: nil,
53-
Privileged: nil,
54-
SELinuxOptions: nil,
55-
WindowsOptions: nil,
56-
ReadOnlyRootFilesystem: pointer.Bool(true),
57-
AllowPrivilegeEscalation: pointer.Bool(false),
58-
ProcMount: nil,
59-
SeccompProfile: &corev1.SeccompProfile{
60-
Type: corev1.SeccompProfileTypeRuntimeDefault,
61-
LocalhostProfile: nil,
62-
},
63-
},
6437
}, {
6538
Name: "openshift",
6639
Values: exampleOpenShift,
@@ -113,40 +86,58 @@ func TestExamples(t *testing.T) {
11386
},
11487
}
11588

89+
var (
90+
defaultPsp = &corev1.PodSecurityContext{
91+
RunAsUser: pointer.Int64(1000),
92+
RunAsNonRoot: pointer.Bool(true),
93+
SeccompProfile: &corev1.SeccompProfile{
94+
Type: corev1.SeccompProfileTypeRuntimeDefault,
95+
},
96+
}
97+
98+
defaultCsc = &corev1.SecurityContext{
99+
ReadOnlyRootFilesystem: pointer.Bool(true),
100+
AllowPrivilegeEscalation: pointer.Bool(false),
101+
SeccompProfile: &corev1.SeccompProfile{
102+
Type: corev1.SeccompProfileTypeRuntimeDefault,
103+
},
104+
}
105+
)
106+
116107
for _, test := range tests {
117108
test := test
109+
110+
if test.PodSecurityContext == nil {
111+
test.PodSecurityContext = defaultPsp
112+
}
113+
if test.ContainerSecurityContext == nil {
114+
test.ContainerSecurityContext = defaultCsc
115+
}
116+
118117
t.Run(test.Name, func(t *testing.T) {
119118
t.Parallel()
120119

121-
values, err := chartutil.ToRenderValues(chart, test.Values, DefaultReleaseOptions(), chartutil.DefaultCapabilities.Copy())
120+
values, err := chartutil.ToRenderValues(chart.chart, test.Values, DefaultReleaseOptions(), chartutil.DefaultCapabilities.Copy())
122121
require.NoError(t, err, "failed to generate render values")
123122

124-
manifests, err := engine.Render(chart, values)
123+
manifests, err := engine.Render(chart.chart, values)
125124
require.NoError(t, err, "failed to render chart")
126125

127126
objs, err := LoadObjectsFromManifests(manifests)
128127
require.NoError(t, err, "failed to convert manifests to objects")
129128

130129
// Find the coderd Deployment
131-
var found bool
132-
for _, obj := range objs {
133-
deployment, ok := obj.(*appsv1.Deployment)
134-
if ok && deployment.Name == "coderd" {
135-
found = true
136-
137-
require.Equal(t, test.PodSecurityContext,
138-
deployment.Spec.Template.Spec.SecurityContext,
139-
"expected matching pod securityContext")
140-
require.Len(t, deployment.Spec.Template.Spec.Containers, 1,
141-
"expected one container")
142-
require.Equal(t, test.ContainerSecurityContext,
143-
deployment.Spec.Template.Spec.Containers[0].SecurityContext,
144-
"expected matching container securityContext")
145-
146-
break
147-
}
148-
}
149-
require.True(t, found, "expected coderd deployment in manifests")
130+
coderd := MustFindDeployment(t, objs, "coderd")
131+
132+
assert.Equal(t, test.PodSecurityContext, coderd.Spec.Template.Spec.SecurityContext,
133+
"expected matching pod securityContext",
134+
)
135+
require.Len(t, coderd.Spec.Template.Spec.Containers, 1,
136+
"expected one container",
137+
)
138+
assert.Equal(t, test.ContainerSecurityContext, coderd.Spec.Template.Spec.Containers[0].SecurityContext,
139+
"expected matching container securityContext",
140+
)
150141
})
151142
}
152143
}

tests/pgssl_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package tests
2+
3+
import (
4+
"path"
5+
"strings"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
corev1 "k8s.io/api/core/v1"
11+
"k8s.io/utils/pointer"
12+
)
13+
14+
func TestPgSSL(t *testing.T) {
15+
t.Parallel()
16+
17+
var (
18+
secretName = pointer.String("pg-certs")
19+
pgval = &PostgresValues{
20+
Default: &PostgresDefaultValues{Enable: pointer.Bool(false)},
21+
Host: pointer.String("1.1.1.1"),
22+
Port: pointer.String("5432"),
23+
User: pointer.String("postgres"),
24+
Database: pointer.String("postgres"),
25+
PasswordSecret: pointer.String("pg-pass"),
26+
SSLMode: pointer.String("require"),
27+
SSL: &PostgresSSLValues{
28+
CertSecret: &CertsSecretValues{
29+
Name: secretName,
30+
Key: pointer.String("cert"),
31+
},
32+
KeySecret: &CertsSecretValues{
33+
Name: secretName,
34+
Key: pointer.String("key"),
35+
},
36+
RootCertSecret: &CertsSecretValues{
37+
Name: secretName,
38+
Key: pointer.String("rootcert"),
39+
},
40+
},
41+
}
42+
43+
objs = LoadChart(t).MustRender(t, func(cv *CoderValues) { cv.Postgres = pgval })
44+
coderd = MustFindDeployment(t, objs, "coderd")
45+
)
46+
47+
for _, vol := range []string{"pgcert", "pgkey", "pgrootcert"} {
48+
AssertVolume(t, coderd.Spec.Template.Spec.Volumes, vol, func(t testing.TB, v corev1.Volume) {
49+
require.NotNilf(t, v.Secret, "secret nil for %q", vol)
50+
assert.Equalf(t, "pg-certs", v.Secret.SecretName, "secret name incorrect for %q", vol)
51+
})
52+
}
53+
54+
for _, cnt := range []string{"migrations", "coderd"} {
55+
// Combine both init and regular containers.
56+
cnts := append(coderd.Spec.Template.Spec.InitContainers, coderd.Spec.Template.Spec.Containers...)
57+
58+
AssertContainer(t, cnts, cnt, func(t testing.TB, c corev1.Container) {
59+
for _, vol := range []string{"pgcert", "pgkey", "pgrootcert"} {
60+
AssertVolumeMount(t, c.VolumeMounts, vol, func(t testing.TB, v corev1.VolumeMount) {
61+
assert.Equalf(t, vol, v.Name, "volume mount name incorrect for %q", vol)
62+
assert.Truef(t, v.ReadOnly, "readonly incorrect for %q", vol)
63+
assert.Equalf(t, path.Join("/etc/ssl/certs/pg/", strings.TrimPrefix(v.Name, "pg")), v.MountPath, "mount path incorrect for %q", vol)
64+
})
65+
}
66+
})
67+
}
68+
}

0 commit comments

Comments
 (0)