Skip to content

Commit 1e9b551

Browse files
MB-61292: Fix drop DEKs behavior when force encryption...
... has been used before This commit fixes the following encr at rest scenario: 1. Create a key 2. Create an encrypted bucket 3. Call POST /controller/forceEncryptionAtRest/bucket/<B> 4. Disable encryption on the bucket 5. Call POST /controller/dropEncryptionAtRestDeks/bucket/<B> 6. Enable encryption on the bucket again 7. Call POST /controller/dropEncryptionAtRestDeks/bucket/<B> Step #7 should trigger DEK drop and re-encryption of the data, but the reencryption doesn't happen. This happens because of two reasons: - drop_deks (via compaction) is not needed in this case, because old DEK becomes "not in use" without compaction (it doesn't really encrypt anything); - force_encryption_datetime is set to some old value, so it is ignored when the list of expired keys is checked In this case we should not really ignore force_encryption_datetime, because dek_drop_datetime is actually set, so this time should be used instead. In other words, currently we only use dek_drop_datetime as force_encryption_datetime if force_encryption_datetime is not set. While we should actually take the maximum of these two values. Change-Id: I7328b721aa82d64026561b4448813158417bc9b8 Reviewed-on: https://review.couchbase.org/c/ns_server/+/232670 Reviewed-by: Navdeep S Boparai <[email protected]> Well-Formed: Build Bot <[email protected]> Tested-by: Timofey Barmin <[email protected]>
1 parent 0d96588 commit 1e9b551

File tree

2 files changed

+182
-18
lines changed

2 files changed

+182
-18
lines changed

apps/ns_server/src/cb_crypto.erl

Lines changed: 87 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -881,26 +881,100 @@ get_drop_keys_timestamp(Type, Snapshot) ->
881881
get_force_encryption_timestamp(Type, Snapshot) ->
882882
maybe
883883
Settings = menelaus_web_encr_at_rest:get_settings(Snapshot),
884-
TypeSettings = maps:get(Type, Settings, undefined),
885-
{ok, ForceDT} ?=
886-
case TypeSettings of
887-
undefined ->
888-
{error, not_found};
889-
#{force_encryption_datetime := {set, DT}} ->
890-
{ok, DT};
891-
#{force_encryption_datetime := {not_set, _}} ->
892-
get_drop_keys_timestamp(Type, Snapshot)
884+
{ok, TypeSettings} ?= case maps:find(Type, Settings) of
885+
error -> {error, not_found};
886+
{ok, S} -> {ok, S}
887+
end,
888+
889+
ForceDT = maps:get(force_encryption_datetime, TypeSettings,
890+
{not_set, []}),
891+
DropDT = maps:get(dek_drop_datetime, TypeSettings,
892+
{not_set, []}),
893+
EffectiveForceDT =
894+
case {ForceDT, DropDT} of
895+
{{not_set, _}, {not_set, _}} -> undefined;
896+
{{not_set, []}, {set, DT}} -> DT;
897+
{{set, DT}, {not_set, []}} -> DT;
898+
{{set, DT1}, {set, DT2}} -> max(DT1, DT2)
893899
end,
894900
LastToggleDT = maps:get(encryption_last_toggle_datetime, TypeSettings,
895901
undefined),
896902
if
897-
ForceDT =:= undefined -> {ok, undefined};
898-
LastToggleDT =:= undefined -> {ok, ForceDT};
899-
LastToggleDT > ForceDT -> {ok, undefined};
900-
true -> {ok, ForceDT}
903+
EffectiveForceDT =:= undefined -> {ok, undefined};
904+
LastToggleDT =:= undefined -> {ok, EffectiveForceDT};
905+
LastToggleDT > EffectiveForceDT -> {ok, undefined};
906+
true -> {ok, EffectiveForceDT}
901907
end
902908
end.
903909

910+
-ifdef(TEST).
911+
912+
get_force_encryption_timestamp_test() ->
913+
NewDate = {{2025,8,21},{23,28,56}},
914+
OldDate = {{2025,8,21},{23,27,29}},
915+
SuperOldDate = {{2025,8,21},{23,26,29}},
916+
meck:new(cluster_compat_mode, [passthrough]),
917+
try
918+
meck:expect(cluster_compat_mode, is_cluster_79,
919+
fun () -> true end),
920+
meck:expect(cluster_compat_mode, is_enterprise,
921+
fun () -> true end),
922+
Mock = fun (ForceDT, DropDT, LastToggleDT) ->
923+
meck:expect(
924+
menelaus_web_encr_at_rest, get_settings,
925+
fun (_Snapshot) ->
926+
C1 = case LastToggleDT of
927+
undefined -> #{};
928+
_ ->
929+
#{encryption_last_toggle_datetime =>
930+
LastToggleDT}
931+
end,
932+
C2 = #{force_encryption_datetime => ForceDT,
933+
dek_drop_datetime => DropDT},
934+
#{config_encryption => maps:merge(C1, C2)}
935+
end)
936+
end,
937+
938+
Assert = fun (Expected, Force, Drop, LastToggle) ->
939+
Val = fun (undefined) -> {not_set, []};
940+
(V) -> {set, V}
941+
end,
942+
Mock(Val(Force), Val(Drop), LastToggle),
943+
?assertEqual({error, not_found},
944+
get_force_encryption_timestamp(unknown, #{})),
945+
?assertEqual({ok, Expected},
946+
get_force_encryption_timestamp(
947+
config_encryption, #{}))
948+
end,
949+
950+
Assert(NewDate, NewDate, OldDate, SuperOldDate),
951+
Assert(NewDate, OldDate, NewDate, SuperOldDate),
952+
Assert(NewDate, undefined, NewDate, SuperOldDate),
953+
Assert(NewDate, NewDate, undefined, SuperOldDate),
954+
Assert(undefined, undefined, undefined, SuperOldDate),
955+
956+
Assert(undefined, OldDate, SuperOldDate, NewDate),
957+
Assert(undefined, SuperOldDate, OldDate, NewDate),
958+
Assert(undefined, undefined, OldDate, NewDate),
959+
Assert(undefined, SuperOldDate, undefined, NewDate),
960+
Assert(undefined, undefined, undefined, NewDate),
961+
962+
Assert(NewDate, SuperOldDate, NewDate, OldDate),
963+
Assert(NewDate, NewDate, SuperOldDate, OldDate),
964+
Assert(undefined, undefined, SuperOldDate, OldDate),
965+
Assert(NewDate, NewDate, undefined, OldDate),
966+
967+
Assert(NewDate, NewDate, OldDate, undefined),
968+
Assert(NewDate, OldDate, NewDate, undefined),
969+
Assert(NewDate, undefined, NewDate, undefined),
970+
Assert(NewDate, NewDate, undefined, undefined),
971+
Assert(undefined, undefined, undefined, undefined)
972+
973+
after
974+
meck:unload(cluster_compat_mode)
975+
end.
976+
-endif.
977+
904978
%%%===================================================================
905979
%%% Internal functions
906980
%%%===================================================================

apps/ns_server/src/ns_bucket.erl

Lines changed: 95 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3280,20 +3280,110 @@ get_force_encryption_timestamp(BucketUUID, Snapshot) ->
32803280
Settings = chronicle_compat:get(Snapshot, sub_key(Bucket, encr_at_rest),
32813281
#{default => #{}}),
32823282
DropDT = maps:get(dek_drop_datetime, Settings, undefined),
3283-
ForceDT = maps:get(force_encryption_datetime, Settings, DropDT),
3283+
ForceDT = maps:get(force_encryption_datetime, Settings, undefined),
3284+
3285+
EffectiveForceDT = case {DropDT, ForceDT} of
3286+
{undefined, undefined} -> undefined;
3287+
{undefined, DT} -> DT;
3288+
{DT, undefined} -> DT;
3289+
{DT1, DT2} -> max(DT1, DT2)
3290+
end,
3291+
32843292
LastToggleDT = proplists:get_value(encryption_last_toggle_datetime,
32853293
Config),
32863294
if
3287-
ForceDT =:= undefined -> {ok, undefined};
3288-
LastToggleDT =:= undefined -> {ok, ForceDT};
3289-
LastToggleDT > ForceDT -> {ok, undefined};
3290-
true -> {ok, ForceDT}
3295+
EffectiveForceDT =:= undefined -> {ok, undefined};
3296+
LastToggleDT =:= undefined -> {ok, EffectiveForceDT};
3297+
LastToggleDT > EffectiveForceDT -> {ok, undefined};
3298+
true -> {ok, EffectiveForceDT}
32913299
end
32923300
else
32933301
not_present -> {error, not_found};
32943302
{error, R} -> {error, R}
32953303
end.
32963304

3305+
-ifdef(TEST).
3306+
get_force_encryption_timestamp_test() ->
3307+
meck:new(cluster_compat_mode, [passthrough]),
3308+
try
3309+
meck:expect(cluster_compat_mode, is_cluster_79,
3310+
fun () -> true end),
3311+
meck:expect(cluster_compat_mode, is_enterprise,
3312+
fun () -> true end),
3313+
3314+
NewDate = {{2025,8,21},{23,28,56}},
3315+
OldDate = {{2025,8,21},{23,27,29}},
3316+
SuperOldDate = {{2025,8,21},{23,26,29}},
3317+
B = <<"test_bucket">>,
3318+
BUUID = <<"123">>,
3319+
3320+
BucketCommon = #{root => {[B], 0},
3321+
uuid2bucket_key(BUUID) => {B, 0},
3322+
uuid_key(B) => {BUUID, 0}},
3323+
3324+
SetToggleDT =
3325+
fun (S, undefined) ->
3326+
S#{sub_key(B, props) => {[], 0}};
3327+
(S, DT) ->
3328+
S#{sub_key(B, props) =>
3329+
{[{encryption_last_toggle_datetime, DT}], 0}}
3330+
end,
3331+
3332+
SetOtherDT =
3333+
fun (S, undefined, undefined) -> S;
3334+
(S, ForceDT, undefined) ->
3335+
S#{sub_key(B, encr_at_rest) =>
3336+
{#{force_encryption_datetime => ForceDT}, 0}};
3337+
(S, undefined, DropDT) ->
3338+
S#{sub_key(B, encr_at_rest) =>
3339+
{#{dek_drop_datetime => DropDT}, 0}};
3340+
(S, ForceDT, DropDT) ->
3341+
S#{sub_key(B, encr_at_rest) =>
3342+
{#{dek_drop_datetime => DropDT,
3343+
force_encryption_datetime => ForceDT}, 0}}
3344+
end,
3345+
3346+
Snapshot = fun (ForceDT, DropDT, LastToggleDT) ->
3347+
functools:chain(
3348+
BucketCommon,
3349+
[SetToggleDT(_, LastToggleDT),
3350+
SetOtherDT(_, ForceDT, DropDT)])
3351+
end,
3352+
3353+
Assert = fun (Expected, ForceDT, DropDT, LastToggleDT) ->
3354+
?assertEqual({ok, Expected},
3355+
get_force_encryption_timestamp(
3356+
BUUID, Snapshot(ForceDT, DropDT, LastToggleDT)))
3357+
end,
3358+
3359+
Assert(NewDate, NewDate, OldDate, SuperOldDate),
3360+
Assert(NewDate, OldDate, NewDate, SuperOldDate),
3361+
Assert(NewDate, undefined, NewDate, SuperOldDate),
3362+
Assert(NewDate, NewDate, undefined, SuperOldDate),
3363+
Assert(undefined, undefined, undefined, SuperOldDate),
3364+
3365+
Assert(undefined, OldDate, SuperOldDate, NewDate),
3366+
Assert(undefined, SuperOldDate, OldDate, NewDate),
3367+
Assert(undefined, undefined, OldDate, NewDate),
3368+
Assert(undefined, SuperOldDate, undefined, NewDate),
3369+
Assert(undefined, undefined, undefined, NewDate),
3370+
3371+
Assert(NewDate, SuperOldDate, NewDate, OldDate),
3372+
Assert(NewDate, NewDate, SuperOldDate, OldDate),
3373+
Assert(undefined, undefined, SuperOldDate, OldDate),
3374+
Assert(NewDate, NewDate, undefined, OldDate),
3375+
3376+
Assert(NewDate, NewDate, OldDate, undefined),
3377+
Assert(NewDate, OldDate, NewDate, undefined),
3378+
Assert(NewDate, undefined, NewDate, undefined),
3379+
Assert(NewDate, NewDate, undefined, undefined),
3380+
Assert(undefined, undefined, undefined, undefined)
3381+
after
3382+
meck:unload(cluster_compat_mode)
3383+
end.
3384+
3385+
-endif.
3386+
32973387
%% fusion
32983388

32993389
-spec is_fusion(proplists:proplist()) -> boolean().

0 commit comments

Comments
 (0)