Skip to content

Commit 83964c8

Browse files
Nikolas De Giorgisirajdeep
Nikolas De Giorgis
andauthored
CLOUDP-95679: Added support for pem entry in tls secret (#646)
Co-authored-by: Rajdeep Das <[email protected]>
1 parent 65db6a0 commit 83964c8

File tree

14 files changed

+301
-56
lines changed

14 files changed

+301
-56
lines changed

.action_templates/jobs/tests.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ tests:
2525
distro: ubuntu
2626
- test-name: replica_set_tls
2727
distro: ubuntu
28+
- test-name: replica_set_tls_pem_file
29+
distro: ubuntu
2830
- test-name: replica_set_tls_upgrade
2931
distro: ubuntu
3032
- test-name: statefulset_arbitrary_config
@@ -65,6 +67,8 @@ tests:
6567
distro: ubi
6668
- test-name: replica_set_tls_upgrade
6769
distro: ubi
70+
- test-name: replica_set_tls_pem_file
71+
distro: ubi
6872
- test-name: statefulset_arbitrary_config
6973
distro: ubi
7074
- test-name: statefulset_arbitrary_config_update

.github/workflows/e2e-fork.yml

+4
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ jobs:
107107
distro: ubuntu
108108
- test-name: replica_set_tls
109109
distro: ubuntu
110+
- test-name: replica_set_tls_pem_file
111+
distro: ubuntu
110112
- test-name: replica_set_tls_upgrade
111113
distro: ubuntu
112114
- test-name: statefulset_arbitrary_config
@@ -147,6 +149,8 @@ jobs:
147149
distro: ubi
148150
- test-name: replica_set_tls_upgrade
149151
distro: ubi
152+
- test-name: replica_set_tls_pem_file
153+
distro: ubi
150154
- test-name: statefulset_arbitrary_config
151155
distro: ubi
152156
- test-name: statefulset_arbitrary_config_update

.github/workflows/e2e.yml

+4
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ jobs:
112112
distro: ubuntu
113113
- test-name: replica_set_tls
114114
distro: ubuntu
115+
- test-name: replica_set_tls_pem_file
116+
distro: ubuntu
115117
- test-name: replica_set_tls_upgrade
116118
distro: ubuntu
117119
- test-name: statefulset_arbitrary_config
@@ -152,6 +154,8 @@ jobs:
152154
distro: ubi
153155
- test-name: replica_set_tls_upgrade
154156
distro: ubi
157+
- test-name: replica_set_tls_pem_file
158+
distro: ubi
155159
- test-name: statefulset_arbitrary_config
156160
distro: ubi
157161
- test-name: statefulset_arbitrary_config_update

api/v1/mongodbcommunity_types.go

+2
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,8 @@ type TLS struct {
321321
// CertificateKeySecret is a reference to a Secret containing a private key and certificate to use for TLS.
322322
// The key and cert are expected to be PEM encoded and available at "tls.key" and "tls.crt".
323323
// This is the same format used for the standard "kubernetes.io/tls" Secret type, but no specific type is required.
324+
// Alternatively, an entry tls.pem, containing the concatenation of cert and key, can be provided.
325+
// If all of tls.pem, tls.crt and tls.key are present, the tls.pem one needs to be equal to the concatenation of tls.crt and tls.key
324326
// +optional
325327
CertificateKeySecret LocalObjectReference `json:"certificateKeySecretRef"`
326328

config/crd/bases/mongodbcommunity.mongodb.com_mongodbcommunity.yaml

+5-1
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,11 @@ spec:
207207
The key and cert are expected to be PEM encoded and available
208208
at "tls.key" and "tls.crt". This is the same format used
209209
for the standard "kubernetes.io/tls" Secret type, but no
210-
specific type is required.
210+
specific type is required. Alternatively, an entry tls.pem,
211+
containing the concatenation of cert and key, can be provided.
212+
If all of tls.pem, tls.crt and tls.key are present, the
213+
tls.pem one needs to be equal to the concatenation of tls.crt
214+
and tls.key
211215
properties:
212216
name:
213217
type: string

controllers/mongodb_tls.go

+45-17
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import (
55
"fmt"
66
"strings"
77

8-
"github.com/pkg/errors"
9-
108
"github.com/mongodb/mongodb-kubernetes-operator/controllers/construct"
119
"github.com/mongodb/mongodb-kubernetes-operator/pkg/automationconfig"
1210

@@ -28,6 +26,7 @@ const (
2826
tlsOperatorSecretMountPath = "/var/lib/tls/server/" //nolint
2927
tlsSecretCertName = "tls.crt" //nolint
3028
tlsSecretKeyName = "tls.key"
29+
tlsSecretPemName = "tls.pem"
3130
)
3231

3332
// validateTLSConfig will check that the configured ConfigMap and Secret exist and that they have the correct fields.
@@ -56,7 +55,7 @@ func (r *ReplicaSetReconciler) validateTLSConfig(mdb mdbv1.MongoDBCommunity) (bo
5655
}
5756

5857
// Ensure Secret exists
59-
secretData, err := secret.ReadStringData(r.client, mdb.TLSSecretNamespacedName())
58+
_, err = secret.ReadStringData(r.client, mdb.TLSSecretNamespacedName())
6059
if err != nil {
6160
if apiErrors.IsNotFound(err) {
6261
r.log.Warnf(`Secret "%s" not found`, mdb.TLSSecretNamespacedName())
@@ -66,13 +65,11 @@ func (r *ReplicaSetReconciler) validateTLSConfig(mdb mdbv1.MongoDBCommunity) (bo
6665
return false, err
6766
}
6867

69-
// Ensure Secret has "tls.crt" and "tls.key" fields
70-
if key, ok := secretData[tlsSecretKeyName]; !ok || key == "" {
71-
r.log.Warnf(`Secret "%s" should have a key in field "%s"`, mdb.TLSSecretNamespacedName(), tlsSecretKeyName)
72-
return false, nil
73-
}
74-
if cert, ok := secretData[tlsSecretCertName]; !ok || cert == "" {
75-
r.log.Warnf(`Secret "%s" should have a certificate in field "%s"`, mdb.TLSSecretNamespacedName(), tlsSecretKeyName)
68+
// validate whether the secret contains "tls.crt" and "tls.key", or it contains "tls.pem"
69+
// if it contains all three, then the pem entry should be equal to the concatenation of crt and key
70+
_, err = getPemOrConcatenatedCrtAndKey(r.client, mdb)
71+
if err != nil {
72+
r.log.Warnf(err.Error())
7673
return false, nil
7774
}
7875

@@ -90,7 +87,7 @@ func getTLSConfigModification(getUpdateCreator secret.GetUpdateCreator, mdb mdbv
9087
return automationconfig.NOOP(), nil
9188
}
9289

93-
certKey, err := getCertAndKey(getUpdateCreator, mdb)
90+
certKey, err := getPemOrConcatenatedCrtAndKey(getUpdateCreator, mdb)
9491
if err != nil {
9592
return automationconfig.NOOP(), err
9693
}
@@ -99,18 +96,27 @@ func getTLSConfigModification(getUpdateCreator secret.GetUpdateCreator, mdb mdbv
9996
}
10097

10198
// getCertAndKey will fetch the certificate and key from the user-provided Secret.
102-
func getCertAndKey(getter secret.Getter, mdb mdbv1.MongoDBCommunity) (string, error) {
99+
func getCertAndKey(getter secret.Getter, mdb mdbv1.MongoDBCommunity) string {
103100
cert, err := secret.ReadKey(getter, tlsSecretCertName, mdb.TLSSecretNamespacedName())
104101
if err != nil {
105-
return "", err
102+
return ""
106103
}
107104

108105
key, err := secret.ReadKey(getter, tlsSecretKeyName, mdb.TLSSecretNamespacedName())
109106
if err != nil {
110-
return "", err
107+
return ""
111108
}
112109

113-
return combineCertificateAndKey(cert, key), nil
110+
return combineCertificateAndKey(cert, key)
111+
}
112+
113+
// getPem will fetch the pem from the user-provided secret
114+
func getPem(getter secret.Getter, mdb mdbv1.MongoDBCommunity) string {
115+
pem, err := secret.ReadKey(getter, tlsSecretPemName, mdb.TLSSecretNamespacedName())
116+
if err != nil {
117+
return ""
118+
}
119+
return pem
114120
}
115121

116122
func combineCertificateAndKey(cert, key string) string {
@@ -119,12 +125,34 @@ func combineCertificateAndKey(cert, key string) string {
119125
return fmt.Sprintf("%s\n%s", trimmedCert, trimmedKey)
120126
}
121127

128+
// getPemOrConcatenatedCrtAndKey will get the final PEM to write to the secret.
129+
// This is either the tls.pem entry in the given secret, or the concatenation
130+
// of tls.crt and tls.key
131+
// It performs a basic validation on the entries.
132+
func getPemOrConcatenatedCrtAndKey(getter secret.Getter, mdb mdbv1.MongoDBCommunity) (string, error) {
133+
certKey := getCertAndKey(getter, mdb)
134+
pem := getPem(getter, mdb)
135+
if certKey == "" && pem == "" {
136+
return "", fmt.Errorf(`Neither "%s" nor the pair "%s"/"%s" were present in the TLS secret`, tlsSecretPemName, tlsSecretCertName, tlsSecretKeyName)
137+
}
138+
if certKey == "" {
139+
return pem, nil
140+
}
141+
if pem == "" {
142+
return certKey, nil
143+
}
144+
if certKey != pem {
145+
return "", fmt.Errorf(`If all of "%s", "%s" and "%s" are present in the secret, the entry for "%s" must be equal to the concatenation of "%s" with "%s"`, tlsSecretCertName, tlsSecretKeyName, tlsSecretPemName, tlsSecretPemName, tlsSecretCertName, tlsSecretKeyName)
146+
}
147+
return certKey, nil
148+
}
149+
122150
// ensureTLSSecret will create or update the operator-managed Secret containing
123151
// the concatenated certificate and key from the user-provided Secret.
124152
func ensureTLSSecret(getUpdateCreator secret.GetUpdateCreator, mdb mdbv1.MongoDBCommunity) error {
125-
certKey, err := getCertAndKey(getUpdateCreator, mdb)
153+
certKey, err := getPemOrConcatenatedCrtAndKey(getUpdateCreator, mdb)
126154
if err != nil {
127-
return errors.Errorf("could not get cert and key: %s", err)
155+
return err
128156
}
129157
// Calculate file name from certificate and key
130158
fileName := tlsOperatorSecretFileName(certKey)

controllers/mongodb_tls_test.go

+89-20
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ func TestStatefulSet_IsCorrectlyConfiguredWithTLS(t *testing.T) {
2323
mdb := newTestReplicaSetWithTLS()
2424
mgr := client.NewManager(&mdb)
2525

26-
err := createTLSSecretAndConfigMap(mgr.GetClient(), mdb)
26+
client := mdbClient.NewClient(mgr.GetClient())
27+
err := createTLSSecret(client, mdb, "CERT", "KEY", "")
28+
assert.NoError(t, err)
29+
err = createTLSConfigMap(client, mdb)
2730
assert.NoError(t, err)
2831

2932
r := NewReconciler(mgr)
@@ -82,7 +85,9 @@ func TestStatefulSet_IsCorrectlyConfiguredWithTLS(t *testing.T) {
8285
func TestAutomationConfig_IsCorrectlyConfiguredWithTLS(t *testing.T) {
8386
createAC := func(mdb mdbv1.MongoDBCommunity) automationconfig.AutomationConfig {
8487
client := mdbClient.NewClient(client.NewManager(&mdb).GetClient())
85-
err := createTLSSecretAndConfigMap(client, mdb)
88+
err := createTLSSecret(client, mdb, "CERT", "KEY", "")
89+
assert.NoError(t, err)
90+
err = createTLSConfigMap(client, mdb)
8691
assert.NoError(t, err)
8792

8893
tlsModification, err := getTLSConfigModification(client, mdb)
@@ -151,15 +156,17 @@ func TestTLSOperatorSecret(t *testing.T) {
151156
t.Run("Secret is created if it doesn't exist", func(t *testing.T) {
152157
mdb := newTestReplicaSetWithTLS()
153158
c := mdbClient.NewClient(client.NewManager(&mdb).GetClient())
154-
err := createTLSSecretAndConfigMap(c, mdb)
159+
err := createTLSSecret(c, mdb, "CERT", "KEY", "")
160+
assert.NoError(t, err)
161+
err = createTLSConfigMap(c, mdb)
155162
assert.NoError(t, err)
156163

157164
r := NewReconciler(client.NewManagerWithClient(c))
158165

159166
err = r.ensureTLSResources(mdb)
160167
assert.NoError(t, err)
161168

162-
// Operator-managed secret should have been created and contain the
169+
// Operator-managed secret should have been created and contains the
163170
// concatenated certificate and key.
164171
expectedCertificateKey := "CERT\nKEY"
165172
certificateKey, err := secret.ReadKey(c, tlsOperatorSecretFileName(expectedCertificateKey), mdb.TLSOperatorSecretNamespacedName())
@@ -170,7 +177,9 @@ func TestTLSOperatorSecret(t *testing.T) {
170177
t.Run("Secret is updated if it already exists", func(t *testing.T) {
171178
mdb := newTestReplicaSetWithTLS()
172179
k8sclient := mdbClient.NewClient(client.NewManager(&mdb).GetClient())
173-
err := createTLSSecretAndConfigMap(k8sclient, mdb)
180+
err := createTLSSecret(k8sclient, mdb, "CERT", "KEY", "")
181+
assert.NoError(t, err)
182+
err = createTLSConfigMap(k8sclient, mdb)
174183
assert.NoError(t, err)
175184

176185
// Create operator-managed secret
@@ -215,29 +224,89 @@ func TestCombineCertificateAndKey(t *testing.T) {
215224
}
216225
}
217226

218-
func createTLSSecretAndConfigMap(c k8sClient.Client, mdb mdbv1.MongoDBCommunity) error {
219-
s := secret.Builder().
220-
SetName(mdb.Spec.Security.TLS.CertificateKeySecret.Name).
221-
SetNamespace(mdb.Namespace).
222-
SetField("tls.crt", "CERT").
223-
SetField("tls.key", "KEY").
224-
Build()
227+
func TestPemSupport(t *testing.T) {
228+
t.Run("Success if only pem is provided", func(t *testing.T) {
229+
mdb := newTestReplicaSetWithTLS()
230+
c := mdbClient.NewClient(client.NewManager(&mdb).GetClient())
231+
err := createTLSSecret(c, mdb, "", "", "CERT\nKEY")
232+
assert.NoError(t, err)
233+
err = createTLSConfigMap(c, mdb)
234+
assert.NoError(t, err)
225235

226-
err := c.Create(context.TODO(), &s)
227-
if err != nil {
228-
return err
229-
}
236+
r := NewReconciler(client.NewManagerWithClient(c))
237+
238+
err = r.ensureTLSResources(mdb)
239+
assert.NoError(t, err)
240+
241+
// Operator-managed secret should have been created and contains the
242+
// concatenated certificate and key.
243+
expectedCertificateKey := "CERT\nKEY"
244+
certificateKey, err := secret.ReadKey(c, tlsOperatorSecretFileName(expectedCertificateKey), mdb.TLSOperatorSecretNamespacedName())
245+
assert.NoError(t, err)
246+
assert.Equal(t, expectedCertificateKey, certificateKey)
247+
})
248+
t.Run("Success if pem is equal to cert+key", func(t *testing.T) {
249+
mdb := newTestReplicaSetWithTLS()
250+
c := mdbClient.NewClient(client.NewManager(&mdb).GetClient())
251+
err := createTLSSecret(c, mdb, "CERT", "KEY", "CERT\nKEY")
252+
assert.NoError(t, err)
253+
err = createTLSConfigMap(c, mdb)
254+
assert.NoError(t, err)
255+
256+
r := NewReconciler(client.NewManagerWithClient(c))
257+
258+
err = r.ensureTLSResources(mdb)
259+
assert.NoError(t, err)
260+
261+
// Operator-managed secret should have been created and contains the
262+
// concatenated certificate and key.
263+
expectedCertificateKey := "CERT\nKEY"
264+
certificateKey, err := secret.ReadKey(c, tlsOperatorSecretFileName(expectedCertificateKey), mdb.TLSOperatorSecretNamespacedName())
265+
assert.NoError(t, err)
266+
assert.Equal(t, expectedCertificateKey, certificateKey)
267+
})
268+
t.Run("Failure if pem is different from cert+key", func(t *testing.T) {
269+
mdb := newTestReplicaSetWithTLS()
270+
c := mdbClient.NewClient(client.NewManager(&mdb).GetClient())
271+
err := createTLSSecret(c, mdb, "CERT1", "KEY1", "CERT\nKEY")
272+
assert.NoError(t, err)
273+
err = createTLSConfigMap(c, mdb)
274+
assert.NoError(t, err)
275+
276+
r := NewReconciler(client.NewManagerWithClient(c))
277+
278+
err = r.ensureTLSResources(mdb)
279+
assert.Error(t, err)
280+
assert.Contains(t, err.Error(), `If all of "tls.crt", "tls.key" and "tls.pem" are present in the secret, the entry for "tls.pem" must be equal to the concatenation of "tls.crt" with "tls.key"`)
281+
282+
})
283+
}
230284

285+
func createTLSConfigMap(c k8sClient.Client, mdb mdbv1.MongoDBCommunity) error {
231286
configMap := configmap.Builder().
232287
SetName(mdb.Spec.Security.TLS.CaConfigMap.Name).
233288
SetNamespace(mdb.Namespace).
234289
SetField("ca.crt", "CERT").
235290
Build()
236291

237-
err = c.Create(context.TODO(), &configMap)
238-
if err != nil {
239-
return err
292+
return c.Create(context.TODO(), &configMap)
293+
}
294+
295+
func createTLSSecret(c k8sClient.Client, mdb mdbv1.MongoDBCommunity, crt string, key string, pem string) error {
296+
sBuilder := secret.Builder().
297+
SetName(mdb.Spec.Security.TLS.CertificateKeySecret.Name).
298+
SetNamespace(mdb.Namespace)
299+
300+
if crt != "" {
301+
sBuilder.SetField(tlsSecretCertName, crt)
302+
}
303+
if key != "" {
304+
sBuilder.SetField(tlsSecretKeyName, key)
305+
}
306+
if pem != "" {
307+
sBuilder.SetField(tlsSecretPemName, pem)
240308
}
241309

242-
return nil
310+
s := sBuilder.Build()
311+
return c.Create(context.TODO(), &s)
243312
}

docs/RELEASE_NOTES.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
- Changes
66
- MongoDB database of the statefulSet is managed using distinct Role, ServiceAccount and RoleBinding.
7-
7+
- TLS Secret can also contain a single "tls.pem" entry, containing the concatenation of the certificate and key
8+
- If a TLS secret contains all of "tls.key", "tls.crt" and "tls.pem" entries, the operator will raise an error if the "tls.pem" one is not equal to the concatenation of "tls.crt" with "tls.key"
89
## Updated Image Tags
910

1011
- mongodb-kubernetes-operator:0.7.1

test/e2e/replica_set_tls/replica_set_tls_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func TestReplicaSetTLS(t *testing.T) {
3333
t.Fatal(err)
3434
}
3535

36-
if err := setup.CreateTLSResources(mdb.Namespace, ctx); err != nil {
36+
if err := setup.CreateTLSResources(mdb.Namespace, ctx, setup.CertKeyPair); err != nil {
3737
t.Fatalf("Failed to set up TLS resources: %s", err)
3838
}
3939

0 commit comments

Comments
 (0)