Skip to content

Commit 790eac1

Browse files
K8SPSMDB-1363 | Fix encryption enabled check during snapshot restore (#2293)
Signed-off-by: Mayank Shah <mayank.shah@percona.com>
1 parent 81fe812 commit 790eac1

3 files changed

Lines changed: 236 additions & 13 deletions

File tree

pkg/apis/psmdb/v1/psmdb_types.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,8 @@ func (conf MongoConfiguration) GetTLSMode() (string, error) {
670670
return mode, nil
671671
}
672672

673-
// IsEncryptionEnabled returns nil if "enableEncryption" field is not specified or the pointer to the value of this field
673+
// IsEncryptionEnabled returns nil if "enableEncryption" field is not specified or the pointer to the value of this field.
674+
// Nil value means that encryption was not specified and is enabled by default.
674675
func (conf MongoConfiguration) IsEncryptionEnabled() (*bool, error) {
675676
m, err := conf.GetOptions("security")
676677
if err != nil || m == nil {

pkg/controller/perconaservermongodbrestore/snapshots.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,11 +289,11 @@ func (r *ReconcilePerconaServerMongoDBRestore) scaleDownStatefulSetsForSnapshotR
289289
if rs.Configuration.VaultEnabled() {
290290
return true, nil
291291
}
292-
enc, err := rs.Configuration.IsEncryptionEnabled()
292+
enc, err := rs.IsEncryptionEnabled()
293293
if err != nil {
294294
return false, errors.Wrapf(err, "failed to check if encryption is enabled")
295295
}
296-
return enc != nil && *enc, nil
296+
return enc, nil
297297
}
298298

299299
// Scale down all statefulsets of each replset.
@@ -879,11 +879,11 @@ func (r *ReconcilePerconaServerMongoDBRestore) createOrUpdateDBConfigSecret(
879879
securityConf = sec
880880
}
881881
} else {
882-
enabled, err := rs.Configuration.IsEncryptionEnabled()
882+
enabled, err := rs.IsEncryptionEnabled()
883883
if err != nil {
884884
return errors.Wrapf(err, "check encryption for replset %s", rs.Name)
885885
}
886-
if enabled != nil && *enabled {
886+
if enabled {
887887
securityConf = map[string]any{
888888
"enableEncryption": true,
889889
"encryptionKeyFile": fmt.Sprintf("/tmp/%s", psmdbv1.EncryptionKeyName),

pkg/controller/perconaservermongodbrestore/snapshots_test.go

Lines changed: 230 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,22 @@ func TestScaleDownStatefulSetsForSnapshotRestore(t *testing.T) {
102102
ctx := context.Background()
103103
const ns = "default"
104104

105+
encryptionDisabledConf := psmdbv1.MongoConfiguration(`security:
106+
enableEncryption: false`)
107+
105108
cluster := &psmdbv1.PerconaServerMongoDB{
106109
ObjectMeta: metav1.ObjectMeta{
107110
Name: "my-cluster",
108111
Namespace: ns,
109112
},
110113
Spec: psmdbv1.PerconaServerMongoDBSpec{
111114
Replsets: []*psmdbv1.ReplsetSpec{
112-
{Name: "rs0", Size: 3},
115+
{
116+
Name: "rs0",
117+
Size: 3,
118+
Storage: &psmdbv1.MongodSpecStorage{},
119+
Configuration: encryptionDisabledConf,
120+
},
113121
},
114122
},
115123
}
@@ -210,8 +218,9 @@ func TestScaleDownStatefulSetsForSnapshotRestore(t *testing.T) {
210218
Spec: psmdbv1.PerconaServerMongoDBSpec{
211219
Replsets: []*psmdbv1.ReplsetSpec{
212220
{
213-
Name: "rs0",
214-
Size: 1,
221+
Name: "rs0",
222+
Size: 1,
223+
Configuration: encryptionDisabledConf,
215224
NonVoting: psmdbv1.NonVotingSpec{
216225
Enabled: true,
217226
Size: 1,
@@ -256,6 +265,209 @@ func TestScaleDownStatefulSetsForSnapshotRestore(t *testing.T) {
256265
assert.True(t, done)
257266
assert.True(t, apimeta.IsStatusConditionTrue(status.Conditions, psmdbv1.ConditionPBMAgentConfiguredForSnapshot))
258267
})
268+
269+
t.Run("encryption explicitly enabled adds db-config volume and bash wrapper command", func(t *testing.T) {
270+
encCluster := &psmdbv1.PerconaServerMongoDB{
271+
ObjectMeta: metav1.ObjectMeta{
272+
Name: "enc-cluster",
273+
Namespace: ns,
274+
},
275+
Spec: psmdbv1.PerconaServerMongoDBSpec{
276+
Replsets: []*psmdbv1.ReplsetSpec{
277+
{
278+
Name: "rs0",
279+
Size: 1,
280+
Storage: &psmdbv1.MongodSpecStorage{},
281+
Configuration: psmdbv1.MongoConfiguration(`security:
282+
enableEncryption: true`),
283+
},
284+
},
285+
},
286+
}
287+
encRS := encCluster.Spec.Replsets[0]
288+
289+
sfs := &appsv1.StatefulSet{
290+
ObjectMeta: metav1.ObjectMeta{
291+
Name: naming.MongodStatefulSetName(encCluster, encRS),
292+
Namespace: ns,
293+
},
294+
Spec: appsv1.StatefulSetSpec{
295+
Replicas: ptr.To(int32(1)),
296+
Template: corev1.PodTemplateSpec{
297+
Spec: corev1.PodSpec{
298+
Containers: []corev1.Container{{Name: "mongod"}},
299+
},
300+
},
301+
},
302+
Status: appsv1.StatefulSetStatus{ReadyReplicas: 1},
303+
}
304+
305+
r := fakeReconciler(encCluster, sfs)
306+
pbmName := "enc-pbm-restore"
307+
status := &psmdbv1.PerconaServerMongoDBRestoreStatus{PBMname: pbmName}
308+
309+
done, err := r.scaleDownStatefulSetsForSnapshotRestore(ctx, encCluster, status)
310+
assert.NoError(t, err)
311+
assert.False(t, done)
312+
313+
updated := &appsv1.StatefulSet{}
314+
err = r.client.Get(ctx, types.NamespacedName{Name: sfs.Name, Namespace: ns}, updated)
315+
require.NoError(t, err)
316+
317+
container := updated.Spec.Template.Spec.Containers[0]
318+
assert.Equal(t, "bash", container.Command[0])
319+
assert.Contains(t, container.Command[2], "exec /opt/percona/pbm-agent")
320+
assert.Contains(t, container.Command[2], psmdbv1.EncryptionKeyName)
321+
assert.Contains(t, container.Args, "--db-config")
322+
assert.Contains(t, container.Args, "/etc/pbm-db-config/db_config.yaml")
323+
324+
foundVolume := false
325+
for _, v := range updated.Spec.Template.Spec.Volumes {
326+
if v.Name == "pbm-db-config" {
327+
foundVolume = true
328+
require.NotNil(t, v.Secret)
329+
assert.Equal(t, r.dbConfigSecretName(encCluster, encRS), v.Secret.SecretName)
330+
}
331+
}
332+
assert.True(t, foundVolume, "expected pbm-db-config volume to be added")
333+
334+
foundMount := false
335+
for _, m := range container.VolumeMounts {
336+
if m.Name == "pbm-db-config" {
337+
foundMount = true
338+
assert.Equal(t, "/etc/pbm-db-config/", m.MountPath)
339+
assert.True(t, m.ReadOnly)
340+
}
341+
}
342+
assert.True(t, foundMount, "expected pbm-db-config volume mount to be added")
343+
})
344+
345+
t.Run("encryption explicitly disabled uses plain pbm-agent command", func(t *testing.T) {
346+
noEncCluster := &psmdbv1.PerconaServerMongoDB{
347+
ObjectMeta: metav1.ObjectMeta{
348+
Name: "noenc-cluster",
349+
Namespace: ns,
350+
},
351+
Spec: psmdbv1.PerconaServerMongoDBSpec{
352+
Replsets: []*psmdbv1.ReplsetSpec{
353+
{
354+
Name: "rs0",
355+
Size: 1,
356+
Storage: &psmdbv1.MongodSpecStorage{},
357+
Configuration: psmdbv1.MongoConfiguration(`security:
358+
enableEncryption: false`),
359+
},
360+
},
361+
},
362+
}
363+
noEncRS := noEncCluster.Spec.Replsets[0]
364+
365+
sfs := &appsv1.StatefulSet{
366+
ObjectMeta: metav1.ObjectMeta{
367+
Name: naming.MongodStatefulSetName(noEncCluster, noEncRS),
368+
Namespace: ns,
369+
},
370+
Spec: appsv1.StatefulSetSpec{
371+
Replicas: ptr.To(int32(1)),
372+
Template: corev1.PodTemplateSpec{
373+
Spec: corev1.PodSpec{
374+
Containers: []corev1.Container{
375+
{
376+
Name: "mongod",
377+
LivenessProbe: &corev1.Probe{},
378+
ReadinessProbe: &corev1.Probe{},
379+
},
380+
},
381+
},
382+
},
383+
},
384+
Status: appsv1.StatefulSetStatus{ReadyReplicas: 1},
385+
}
386+
387+
r := fakeReconciler(noEncCluster, sfs)
388+
pbmName := "noenc-pbm-restore"
389+
status := &psmdbv1.PerconaServerMongoDBRestoreStatus{PBMname: pbmName}
390+
391+
done, err := r.scaleDownStatefulSetsForSnapshotRestore(ctx, noEncCluster, status)
392+
assert.NoError(t, err)
393+
assert.False(t, done)
394+
395+
updated := &appsv1.StatefulSet{}
396+
err = r.client.Get(ctx, types.NamespacedName{Name: sfs.Name, Namespace: ns}, updated)
397+
require.NoError(t, err)
398+
399+
container := updated.Spec.Template.Spec.Containers[0]
400+
assert.Equal(t, []string{"/opt/percona/pbm-agent"}, container.Command)
401+
assert.Equal(t, "restore-finish", container.Args[0])
402+
assert.Equal(t, pbmName, container.Args[1])
403+
assert.NotContains(t, container.Args, "--db-config")
404+
assert.Nil(t, container.LivenessProbe)
405+
assert.Nil(t, container.ReadinessProbe)
406+
407+
for _, v := range updated.Spec.Template.Spec.Volumes {
408+
assert.NotEqual(t, "pbm-db-config", v.Name, "expected no pbm-db-config volume")
409+
}
410+
})
411+
412+
t.Run("encryption not specified defaults to encrypted for non-InMemory storage", func(t *testing.T) {
413+
defaultCluster := &psmdbv1.PerconaServerMongoDB{
414+
ObjectMeta: metav1.ObjectMeta{
415+
Name: "default-cluster",
416+
Namespace: ns,
417+
},
418+
Spec: psmdbv1.PerconaServerMongoDBSpec{
419+
Replsets: []*psmdbv1.ReplsetSpec{
420+
{
421+
Name: "rs0",
422+
Size: 1,
423+
Storage: &psmdbv1.MongodSpecStorage{},
424+
},
425+
},
426+
},
427+
}
428+
defaultRS := defaultCluster.Spec.Replsets[0]
429+
430+
sfs := &appsv1.StatefulSet{
431+
ObjectMeta: metav1.ObjectMeta{
432+
Name: naming.MongodStatefulSetName(defaultCluster, defaultRS),
433+
Namespace: ns,
434+
},
435+
Spec: appsv1.StatefulSetSpec{
436+
Replicas: ptr.To(int32(1)),
437+
Template: corev1.PodTemplateSpec{
438+
Spec: corev1.PodSpec{
439+
Containers: []corev1.Container{{Name: "mongod"}},
440+
},
441+
},
442+
},
443+
Status: appsv1.StatefulSetStatus{ReadyReplicas: 1},
444+
}
445+
446+
r := fakeReconciler(defaultCluster, sfs)
447+
pbmName := "default-pbm-restore"
448+
status := &psmdbv1.PerconaServerMongoDBRestoreStatus{PBMname: pbmName}
449+
450+
done, err := r.scaleDownStatefulSetsForSnapshotRestore(ctx, defaultCluster, status)
451+
assert.NoError(t, err)
452+
assert.False(t, done)
453+
454+
updated := &appsv1.StatefulSet{}
455+
err = r.client.Get(ctx, types.NamespacedName{Name: sfs.Name, Namespace: ns}, updated)
456+
require.NoError(t, err)
457+
458+
container := updated.Spec.Template.Spec.Containers[0]
459+
assert.Equal(t, "bash", container.Command[0], "encryption enabled by default should use bash wrapper")
460+
assert.Contains(t, container.Command[2], "exec /opt/percona/pbm-agent")
461+
assert.Contains(t, container.Args, "--db-config")
462+
463+
foundVolume := false
464+
for _, v := range updated.Spec.Template.Spec.Volumes {
465+
if v.Name == "pbm-db-config" {
466+
foundVolume = true
467+
}
468+
}
469+
assert.True(t, foundVolume, "expected pbm-db-config volume when encryption defaults to enabled")
470+
})
259471
}
260472

261473
func TestScaleUpStatefulSetsForSnapshotRestore(t *testing.T) {
@@ -729,7 +941,11 @@ func TestScaleDownStatefulSetsNodeAddressArg(t *testing.T) {
729941
cluster := &psmdbv1.PerconaServerMongoDB{
730942
ObjectMeta: metav1.ObjectMeta{Name: "cl", Namespace: ns},
731943
Spec: psmdbv1.PerconaServerMongoDBSpec{
732-
Replsets: []*psmdbv1.ReplsetSpec{{Name: "rs0", Size: 1}},
944+
Replsets: []*psmdbv1.ReplsetSpec{{
945+
Name: "rs0",
946+
Size: 1,
947+
Storage: &psmdbv1.MongodSpecStorage{},
948+
}},
733949
},
734950
}
735951
rs := cluster.Spec.Replsets[0]
@@ -835,7 +1051,7 @@ func TestCreateOrUpdateDBConfigSecret(t *testing.T) {
8351051
},
8361052
Spec: psmdbv1.PerconaServerMongoDBSpec{
8371053
Replsets: []*psmdbv1.ReplsetSpec{
838-
{Name: "rs0", Size: 1, Configuration: conf},
1054+
{Name: "rs0", Size: 1, Configuration: conf, Storage: &psmdbv1.MongodSpecStorage{}},
8391055
},
8401056
},
8411057
}
@@ -844,16 +1060,22 @@ func TestCreateOrUpdateDBConfigSecret(t *testing.T) {
8441060
expectedSecretName := clusterName + "-rs0-pbm-db-config"
8451061
expectedKeyFile := "/tmp/" + psmdbv1.EncryptionKeyName
8461062

847-
t.Run("no secret created when encryption is not configured", func(t *testing.T) {
1063+
t.Run("secret created when encryption is not configured because it defaults to enabled", func(t *testing.T) {
8481064
cluster := makeCluster("")
8491065
r := fakeReconciler(cluster)
8501066

8511067
err := r.createOrUpdateDBConfigSecret(t.Context(), cluster)
852-
assert.NoError(t, err)
1068+
require.NoError(t, err)
8531069

8541070
secret := &corev1.Secret{}
8551071
err = r.client.Get(t.Context(), types.NamespacedName{Name: expectedSecretName, Namespace: ns}, secret)
856-
assert.True(t, k8sErrors.IsNotFound(err))
1072+
require.NoError(t, err)
1073+
1074+
data, ok := secret.Data["db_config.yaml"]
1075+
require.True(t, ok, "expected db_config.yaml key in secret")
1076+
content := string(data)
1077+
assert.True(t, strings.Contains(content, "enableEncryption: true"), "expected enableEncryption: true in content: %s", content)
1078+
assert.True(t, strings.Contains(content, expectedKeyFile), "expected key file path in content: %s", content)
8571079
})
8581080

8591081
t.Run("no secret created when encryption is explicitly false", func(t *testing.T) {

0 commit comments

Comments
 (0)