Skip to content

Commit f8960ab

Browse files
committed
MB-61292: Support for encrypting logs upon enable log encryption
This patch will enable it for debug log Change-Id: I7eca83f3677a49ce055be0123f93a9bff6c56c44 Reviewed-on: https://review.couchbase.org/c/ns_server/+/216151 Tested-by: Navdeep S Boparai <[email protected]> Reviewed-by: Timofey Barmin <[email protected]> Well-Formed: Build Bot <[email protected]>
1 parent 1c3937a commit f8960ab

File tree

8 files changed

+222
-27
lines changed

8 files changed

+222
-27
lines changed

apps/ale/src/ale.erl

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,16 @@
2121
sync_sink/1,
2222
sync_all_sinks/0,
2323
init_log_encryption_ds/1,
24+
get_sink_ds/1,
2425
set_global_log_deks_snapshot/1,
2526
get_global_log_deks_snapshot/0,
27+
set_log_deks_snapshot/1,
28+
29+
%% Callbacks for encryption
30+
create_no_deks_snapshot/0,
31+
file_encrypt_state_match/2,
32+
file_encrypt_init/2,
33+
file_encrypt_chunk/2,
2634

2735
with_configuration_batching/1,
2836

@@ -122,28 +130,95 @@ set_sink_loglevel(LoggerName, SinkName, LogLevel) ->
122130
get_sink_loglevel(LoggerName, SinkName) ->
123131
gen_server:call(?MODULE, {get_sink_loglevel, LoggerName, SinkName}).
124132

125-
sync_sink(SinkName) ->
133+
call_disk_sink(SinkName, Call, Timeout) ->
126134
try
127-
gen_server:call(ale_utils:sink_id(SinkName), sync, infinity)
135+
gen_server:call(ale_utils:sink_id(SinkName), Call, Timeout)
128136
catch
129137
exit:{noproc, _} ->
130138
{error, unknown_sink}
131139
end.
132140

141+
sync_sink(SinkName) ->
142+
call_disk_sink(SinkName, sync, infinity).
143+
133144
sync_all_sinks() ->
134145
Sinks = gen_server:call(?MODULE, get_sink_names, infinity),
135146
[sync_sink(SinkName) || SinkName <- Sinks],
136147
ok.
137148

149+
get_encr_enabled_disk_sinks() ->
150+
#{ale_utils:sink_id(disk_debug) => true}.
151+
152+
is_encr_enabled_sink(SinkId) ->
153+
EncrEnabledSinks = get_encr_enabled_disk_sinks(),
154+
case maps:find(SinkId, EncrEnabledSinks) of
155+
{ok, true} ->
156+
true;
157+
_ ->
158+
false
159+
end.
160+
161+
set_log_deks_snapshot(LogsDS) ->
162+
set_global_log_deks_snapshot(LogsDS),
163+
Sinks = gen_server:call(?MODULE, get_sink_names, infinity),
164+
EncryptedSinks =
165+
lists:filter(
166+
fun(SinkName) ->
167+
is_encr_enabled_sink(ale_utils:sink_id(SinkName))
168+
end, Sinks),
169+
RVs =
170+
misc:parallel_map(
171+
fun(SinkName) ->
172+
{SinkName, call_disk_sink(SinkName,
173+
notify_active_key_updt, infinity)}
174+
end, EncryptedSinks, infinity),
175+
176+
Failures = [Result || {_Sink, R} = Result <- RVs, R =/= ok],
177+
case Failures of
178+
[] ->
179+
ok;
180+
_ ->
181+
{error, Failures}
182+
end.
183+
138184
init_log_encryption_ds(LogDS) ->
139185
set_global_log_deks_snapshot(LogDS).
140186

187+
get_sink_ds(SinkId) ->
188+
case is_encr_enabled_sink(SinkId) of
189+
true ->
190+
get_global_log_deks_snapshot();
191+
false ->
192+
create_no_deks_snapshot()
193+
end.
194+
141195
set_global_log_deks_snapshot(LogsDs) ->
142196
persistent_term:put(log_deks_snapshot, LogsDs).
143197

144198
get_global_log_deks_snapshot() ->
145199
persistent_term:get(log_deks_snapshot, undefined).
146200

201+
get_encryption_cb(CbType) ->
202+
CBs = persistent_term:get(ale_encryption_callbacks, undefined),
203+
#{CbType := Callback} = CBs,
204+
Callback.
205+
206+
create_no_deks_snapshot() ->
207+
Callback = get_encryption_cb(create_no_deks_snapshot),
208+
Callback().
209+
210+
file_encrypt_state_match(DS, EncrState) ->
211+
Callback = get_encryption_cb(file_encrypt_state_match),
212+
Callback(DS, EncrState).
213+
214+
file_encrypt_init(FileName, DS) ->
215+
Callback = get_encryption_cb(file_encrypt_init),
216+
Callback(FileName, DS).
217+
218+
file_encrypt_chunk(Data, EncrState) ->
219+
Callback = get_encryption_cb(file_encrypt_chunk),
220+
Callback(Data, EncrState).
221+
147222
get_effective_loglevel(LoggerName) ->
148223
call_logger_impl(LoggerName, get_effective_loglevel, []).
149224

@@ -334,10 +409,37 @@ init([]) ->
334409
%% Erlang starts this for us when we disable default handler.
335410
_ = logger:remove_handler(simple),
336411

412+
case application:get_env(ale, encryption_callbacks) of
413+
{ok, EncrCBs} ->
414+
persistent_term:put(ale_encryption_callbacks,
415+
EncrCBs);
416+
_ ->
417+
persistent_term:put(ale_encryption_callbacks,
418+
default_encr_disabled_cbs())
419+
end,
420+
337421
ok = set_error_logger_handler(),
338422
ok = set_noisy_progress_reports_handler(),
339423
{ok, State3}.
340424

425+
default_encr_disabled_cbs() ->
426+
#{create_no_deks_snapshot =>
427+
fun() ->
428+
#{}
429+
end,
430+
file_encrypt_state_match =>
431+
fun(_DS, _EncrState) ->
432+
true
433+
end,
434+
file_encrypt_init =>
435+
fun(_FileName, _DS) ->
436+
{<<>>, #{}}
437+
end,
438+
file_encrypt_chunk =>
439+
fun(Data, EncrState) ->
440+
{Data, EncrState}
441+
end}.
442+
341443
handle_call(get_state, _From, State) ->
342444
{reply, State, State};
343445

apps/ale/src/ale_disk_sink.erl

Lines changed: 65 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@
4646
rotation_size :: non_neg_integer(),
4747
rotation_num_files :: pos_integer(),
4848
rotation_compress :: boolean(),
49-
rotation_check_interval :: non_neg_integer()
49+
rotation_check_interval :: non_neg_integer(),
50+
encr_state :: any()
5051
}).
5152

5253
start_link(Name, Path) ->
@@ -96,17 +97,22 @@ init([Name, Path, Opts]) ->
9697

9798
{ok, State}.
9899

99-
handle_call(sync, From, #state{worker = Worker} = State) ->
100-
NewState = flush_buffer(State),
101-
100+
do_work(Worker, Call, From, Timeout) ->
102101
Parent = self(),
103102
proc_lib:spawn_link(
104103
fun () ->
105-
gen_server:reply(From, gen_server:call(Worker, sync, infinity)),
104+
gen_server:reply(From, gen_server:call(Worker, Call, Timeout)),
106105
erlang:unlink(Parent)
107-
end),
106+
end).
108107

108+
handle_call(sync, From, #state{worker = Worker} = State) ->
109+
NewState = flush_buffer(State),
110+
do_work(Worker, sync, From, infinity),
109111
{noreply, NewState};
112+
handle_call(notify_active_key_updt, From,
113+
#state{worker = Worker} = State) ->
114+
do_work(Worker, notify_active_key_updt, From, infinity),
115+
{noreply, State};
110116
handle_call(Request, _From, State) ->
111117
{stop, {unexpected_call, Request}, State}.
112118

@@ -287,16 +293,18 @@ do_rotate_file(From0, To0, Compress) ->
287293
Error
288294
end.
289295

290-
maybe_rotate_files(#worker_state{sink_name = Name,
291-
file_size = FileSize,
292-
rotation_size = RotSize} = State)
293-
when RotSize =/= 0, FileSize >= RotSize ->
296+
do_rotate_files(#worker_state{sink_name = Name} = State) ->
294297
time_stat(Name, rotation_time,
295298
fun () ->
296299
ok = rotate_files(State),
297300
ok = maybe_compress_post_rotate(State),
298301
open_log_file(State)
299-
end);
302+
end).
303+
304+
maybe_rotate_files(#worker_state{file_size = FileSize,
305+
rotation_size = RotSize} = State)
306+
when RotSize =/= 0, FileSize >= RotSize ->
307+
do_rotate_files(State);
300308
maybe_rotate_files(State) ->
301309
State.
302310

@@ -428,16 +436,21 @@ spawn_worker(WorkerState) ->
428436
worker_init(WorkerState)
429437
end).
430438

431-
worker_init(#worker_state{rotation_check_interval = RotCheckInterval} = State0) ->
439+
worker_init(#worker_state{rotation_check_interval = RotCheckInterval,
440+
path = Path} = State0) ->
432441
case RotCheckInterval > 0 of
433442
true ->
434443
erlang:send_after(RotCheckInterval, self(), check_file);
435444
false ->
436445
ok
437446
end,
438-
worker_loop(open_log_file(State0)).
439447

440-
worker_loop(State) ->
448+
DS = ale:create_no_deks_snapshot(),
449+
{<<>>, EncrState} = ale:file_encrypt_init(filename:basename(Path), DS),
450+
worker_loop(
451+
open_log_file(State0#worker_state{encr_state = EncrState})).
452+
453+
worker_loop(#worker_state{sink_name = SinkName} = State) ->
441454
NewState =
442455
receive
443456
{write, Data0, DataSize0} ->
@@ -450,6 +463,13 @@ worker_loop(State) ->
450463
{'$gen_call', From, sync} ->
451464
gen_server:reply(From, ok),
452465
State;
466+
{'$gen_call', From, notify_active_key_updt} ->
467+
#worker_state{encr_state = EncrState} = State,
468+
DS = ale:get_sink_ds(SinkName),
469+
UpdtReq = not ale:file_encrypt_state_match(DS, EncrState),
470+
UpdatedState = process_key_update_work(UpdtReq, DS, State),
471+
gen_server:reply(From, ok),
472+
UpdatedState;
453473
Msg ->
454474
exit({unexpected_msg, Msg})
455475
end,
@@ -466,22 +486,46 @@ receive_more_writes(Data, DataSize) ->
466486
{Data, DataSize}
467487
end.
468488

469-
write_data(Data, DataSize,
489+
maybe_encrypt_data(InputData, EncrState) ->
490+
ale:file_encrypt_chunk(InputData, EncrState).
491+
492+
write_data(InputData, InputDataSize,
470493
#worker_state{sink_name = Name,
471494
file = File,
472495
file_size = FileSize,
473-
parent = Parent} = State) ->
474-
broadcast_stat(Name, write_size, DataSize),
475-
496+
parent = Parent,
497+
encr_state = EncrState} = State) ->
498+
{WriteData, NewEncrState} = maybe_encrypt_data(InputData, EncrState),
499+
WriteDataSize = byte_size(WriteData),
500+
broadcast_stat(Name, write_size, WriteDataSize),
476501
time_stat(Name, write_time,
477502
fun () ->
478-
ok = file:write(File, Data)
503+
ok = file:write(File, WriteData)
479504
end),
480505

481-
Parent ! {written, DataSize},
482-
NewState = State#worker_state{file_size = FileSize + DataSize},
506+
Parent ! {written, InputDataSize},
507+
NewState = State#worker_state{file_size = FileSize + WriteDataSize,
508+
encr_state = NewEncrState},
483509
maybe_rotate_files(NewState).
484510

511+
process_key_update_work(false = _UpdtReq, _DS, State) ->
512+
State;
513+
process_key_update_work(true = _UpdtReq, DS,
514+
#worker_state{sink_name = Name,
515+
path = Path} = State) ->
516+
{Header, EncryptState} =
517+
ale:file_encrypt_init(filename:basename(Path), DS),
518+
NewState = do_rotate_files(State),
519+
#worker_state{file = File,
520+
file_size = 0,
521+
path = Path} = NewState,
522+
time_stat(Name, write_time,
523+
fun () ->
524+
ok = file:write(File, Header)
525+
end),
526+
NewState#worker_state{file_size = byte_size(Header),
527+
encr_state = EncryptState}.
528+
485529
remove_unnecessary_log_files(LogFilePath, NumFiles) ->
486530
Dir = filename:dirname(LogFilePath),
487531
Name = filename:basename(LogFilePath),

apps/ns_babysitter/src/ns_babysitter_bootstrap.erl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ start() ->
1818

1919
start({nocookie, nologdek}) ->
2020
try
21+
application:set_env(ale, encryption_callbacks, ?ALE_ENCR_CALLBACKS),
22+
2123
ok = application:start(ale),
2224
ok = application:start(sasl),
2325
ok = application:start(ns_babysitter, permanent),

apps/ns_couchdb/src/ns_couchdb.erl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
start({Cookie, LogDeks}) ->
3131
application:set_env(ns_server, babysitter_cookie, Cookie),
3232
application:set_env(ns_server, initial_log_deks, LogDeks),
33+
application:set_env(ale, encryption_callbacks, ?ALE_ENCR_CALLBACKS),
3334
application:start(ns_common, permanent),
3435
application:start(ns_couchdb, permanent).
3536

apps/ns_server/include/ns_common.hrl

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,4 +461,22 @@
461461
?FUNCTION_NAME, Args)
462462
end).
463463

464-
-endif.
464+
-define(ALE_ENCR_CALLBACKS,
465+
#{create_no_deks_snapshot =>
466+
fun() ->
467+
cb_crypto:create_deks_snapshot(undefined, [], undefined)
468+
end,
469+
file_encrypt_state_match =>
470+
fun(DS, EncrState) ->
471+
cb_crypto:file_encrypt_state_match(DS, EncrState)
472+
end,
473+
file_encrypt_init =>
474+
fun(FileName, DS) ->
475+
cb_crypto:file_encrypt_init(FileName, DS)
476+
end,
477+
file_encrypt_chunk =>
478+
fun(Data, EncrState) ->
479+
cb_crypto:file_encrypt_chunk(Data, EncrState)
480+
end}).
481+
482+
-endif.

apps/ns_server/src/cb_crypto.erl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
file_encrypt_chunk/2,
3333

3434
read_file/2,
35+
file_encrypt_state_match/2,
3536
file_decrypt_init/3,
3637
file_decrypt_next_chunk/2,
3738

@@ -113,6 +114,18 @@ atomic_write_file(Path, Bytes, DekSnapshot, Opts) when is_binary(Bytes) ->
113114
encrypt_to_file(IODevice, Filename, Bytes, MaxChunkSize, DekSnapshot)
114115
end).
115116

117+
get_key_id(undefined) ->
118+
undefined;
119+
get_key_id(#{id := KeyId} = _Key) ->
120+
KeyId.
121+
122+
-spec file_encrypt_state_match(#dek_snapshot{},
123+
#file_encr_state{}) -> boolean().
124+
file_encrypt_state_match(#dek_snapshot{active_key = ActiveKey},
125+
#file_encr_state{key = FileActiveKey}) ->
126+
127+
get_key_id(ActiveKey) =:= get_key_id(FileActiveKey).
128+
116129
-spec file_encrypt_init(string(), #dek_snapshot{}) ->
117130
{binary(), #file_encr_state{}}.
118131
file_encrypt_init(Filename,

0 commit comments

Comments
 (0)