From 328536f693b6d2aba58180cd8a29c788f49cf622 Mon Sep 17 00:00:00 2001 From: Alex Sherman Date: Tue, 28 Jul 2020 12:22:54 +0500 Subject: [PATCH] Add separate monitor for epp_tls_acceptor reloading --- README.md | 3 +- apps/epp_proxy/src/epp_proxy_sup.erl | 5 +- apps/epp_proxy/src/epp_tls_acceptor.erl | 44 ++----------- apps/epp_proxy/src/epp_tls_monitor.erl | 78 ++++++++++++++++++++++++ apps/epp_proxy/src/memory_monitor.erl | 13 ++-- apps/epp_proxy/test/tls_client_SUITE.erl | 6 +- config/test.config | 2 +- rebar.config | 2 +- 8 files changed, 103 insertions(+), 50 deletions(-) create mode 100644 apps/epp_proxy/src/epp_tls_monitor.erl diff --git a/README.md b/README.md index 65db7f1..2db5b03 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,8 @@ of Erlang property list. | `cacertfile_path` | `/opt/ca/ca.crt.pem` | SSLCACertificateFile | Where is the client root CA located. Can be inside apps/epp_proxy/priv or absolute path. | `certfile_path` | `/opt/ca/server.crt.pem` | SSLCertificateFile | Where is the server certificate located. Can be inside apps/epp_proxy/priv or absolute path. | `keyfile_path` | `/opt/ca/server.key.pem` | SSLCertificateKeyFile | Where is the server key located. Can be inside apps/epp_proxy/priv or absolute path. -| `crlfile_path` | `/opt/ca/crl.pem` | SSLCARevocationFile | Where is the CRL file located. Can be inside apps/epp_proxy/priv or absolute path. When not set, not CRL check is performed. +| `crlfile_path` | `/opt/ca/crl` | SSLCARevocationFile | Where is the CRL file located. Can be inside apps/epp_proxy/priv or absolute path. When not set, not CRL check is performed. CLRs in this directory must be rehashed by `c_rehash` command as per this solution (https://stackoverflow.com/posts/51480191/revisions) + Migrating from mod_epp diff --git a/apps/epp_proxy/src/epp_proxy_sup.erl b/apps/epp_proxy/src/epp_proxy_sup.erl index 9814d67..65ffa4c 100644 --- a/apps/epp_proxy/src/epp_proxy_sup.erl +++ b/apps/epp_proxy/src/epp_proxy_sup.erl @@ -60,8 +60,11 @@ init([]) -> MemoryMonitor = #{id => memory_monitor, type => worker, modules => [memory_monitor], start => {memory_monitor, start_link, []}}, + TLSMonitor = #{id => epp_tls_monitor, type => worker, + modules => [epp_tls_monitor], + start => {epp_tls_monitor, start_link, []}}, SharedSpecs = [TLSAcceptor, PoolSupervisor, - MemoryMonitor], + MemoryMonitor, TLSMonitor], ChildrenSpec = case ?DevMode of {ok, true} -> [TCPAcceptor | SharedSpecs]; _ -> SharedSpecs diff --git a/apps/epp_proxy/src/epp_tls_acceptor.erl b/apps/epp_proxy/src/epp_tls_acceptor.erl index 96bac53..3fa2526 100644 --- a/apps/epp_proxy/src/epp_tls_acceptor.erl +++ b/apps/epp_proxy/src/epp_tls_acceptor.erl @@ -6,17 +6,15 @@ -define(POOL_SUPERVISOR, epp_pool_supervisor). --define(THIRTY_MINUTES_IN_MS, 30 * 30 * 1000). - -define(WORKER, epp_tls_worker). %% gen_server callbacks -export([handle_call/3, handle_cast/2, init/1, - start_link/1, terminate/2, handle_info/1, handle_info/2]). + start_link/1, terminate/2]). --export([crl_file/0, crl_file/1]). +-export([crl_file/0]). --record(state, {socket, port, options, timer}). +-record(state, {socket, port, options}). start_link(Port) -> gen_server:start_link({local, ?SERVER}, ?MODULE, Port, @@ -29,12 +27,11 @@ init(Port) -> {cacertfile, ca_cert_file()}, {certfile, cert_file()}, {keyfile, key_file()}], Options = handle_crl_check_options(DefaultOptions), - TimerReference = erlang:send_after(?THIRTY_MINUTES_IN_MS, self(), reload_clr_file), {ok, ListenSocket} = ssl:listen(Port, Options), gen_server:cast(self(), accept), {ok, #state{socket = ListenSocket, port = Port, - options = Options, timer = TimerReference}}. + options = Options}}. %% Acceptor has only one state that goes in a loop: %% 1. Listen for a connection from anyone. @@ -55,35 +52,7 @@ handle_cast(accept, State#state{socket = ListenSocket, port = Port, options = Options}}. -handle_info(reload_crl_file) -> - case crl_file() of - undefined -> {noreply}; - {ok, File} -> - ssl_crl_cache:insert({file, File}), - {noreply} - end. - -handle_info(reload_crl_file, State = #state{socket = ListenSocket, port = Port, - options = _Options, timer = TimerReference}) -> - _ = erlang:cancel_timer(TimerReference, [{async, true}, {info, false}]), - TRef = erlang:send_after(?THIRTY_MINUTES_IN_MS, self(), reload_clr_file), - DefaultOptions = [binary, {packet, raw}, - {active, false}, {reuseaddr, true}, - {verify, verify_peer}, {depth, 1}, - {cacertfile, ca_cert_file()}, {certfile, cert_file()}, - {keyfile, key_file()}], - NewOptions = handle_crl_check_options(DefaultOptions), - ok = ssl:close(ListenSocket), - {ok, NewSocket} = ssl:listen(Port, NewOptions), - gen_server:cast(self(), accept), - {noreply, State#state{socket = NewSocket, port = Port, - options = NewOptions, timer = TRef}}; -handle_info(_Info, State) -> - {noreply, State}. - -terminate(_Reason, State) -> - Timer = State#state.timer, - _ = erlang:cancel_timer(Timer, [{async, true}, {info, false}]), +terminate(_Reason, _State) -> ok. handle_call(_E, _From, State) -> {noreply, State}. @@ -122,9 +91,6 @@ crl_file() -> {ok, CrlFile} -> epp_util:path_for_file(CrlFile) end. -crl_file(path) -> - epp_util:path_for_file(path). - %% In some environments, we do not perform a CRL check. Therefore, we need %% different options proplist. diff --git a/apps/epp_proxy/src/epp_tls_monitor.erl b/apps/epp_proxy/src/epp_tls_monitor.erl new file mode 100644 index 0000000..c6c0710 --- /dev/null +++ b/apps/epp_proxy/src/epp_tls_monitor.erl @@ -0,0 +1,78 @@ +%%%------------------------------------------------------------------- +%%% @doc +%%% +%%% Monitor module for reloading epp_tls_acceptor on runtime +%%% Used to renew CRLs once in 30 minutes +%%% @end +%%% Created: 20 Feb 2020 +%%%------------------------------------------------------------------- +-module(epp_tls_monitor). + +-behaviour(gen_server). + +-define(THIRTY_MINUTES_IN_MS, 30 * 60 * 1000). + +-export([init/1, start_link/0]). + +-export([code_change/3, handle_call/3, handle_cast/2, + handle_info/2, terminate/2]). + +-export([reload_acceptor/0]). + +-record(state, {timer_ref :: timer:tref()}). + +-type state() :: #state{}. + +-spec start_link() -> ignore | {error, _} | {ok, pid()}. + +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], + []). + +-spec init([]) -> {ok, state()}. + +init([]) -> + TimerReference = erlang:send_after(?THIRTY_MINUTES_IN_MS, self(), reload_acceptor), + erlang:send(self(), reload_acceptor), + {ok, #state{timer_ref = TimerReference}}. + +%%%------------------------------------------------------------------- +%%% GenServer callbacks +%%%------------------------------------------------------------------- +-spec handle_call(_, _, State) -> {stop, + not_implemented, State}. + +handle_call(_M, _F, State) -> + {stop, not_implemented, State}. + +-spec handle_cast(_, State) -> {stop, not_implemented, + State}. + +handle_cast(_M, State) -> + {stop, not_implemented, State}. + +-spec handle_info(reload_acceptor, _) -> {noreply, _}. + +handle_info(reload_acceptor, State = #state{timer_ref = TimerReference}) -> + _ = erlang:cancel_timer(TimerReference, [{async, true}, {info, false}]), + TRef = erlang:send_after(?THIRTY_MINUTES_IN_MS, self(), reload_clr_file), + ok = reload_acceptor(), + {noreply, State#state{timer_ref = TRef}}. + +-spec terminate(_, state()) -> ok. + +terminate(_Reason, State) -> + _ = erlang:cancel_timer(State#state.timer_ref, [{async, true}, {info, false}]), + ok. + +-spec code_change(_, _, _) -> {ok, _}. + +code_change(_OldVersion, State, _Extra) -> {ok, State}. + +%%%------------------------------------------------------------------- +%%% Internal functions +%%%------------------------------------------------------------------- +reload_acceptor() -> + supervisor:terminate_child(epp_proxy_sup, epp_tls_acceptor), + supervisor:restart_child(epp_proxy_sup, epp_tls_acceptor), + ok. diff --git a/apps/epp_proxy/src/memory_monitor.erl b/apps/epp_proxy/src/memory_monitor.erl index 58b2c3b..b2d0612 100644 --- a/apps/epp_proxy/src/memory_monitor.erl +++ b/apps/epp_proxy/src/memory_monitor.erl @@ -34,8 +34,7 @@ start_link() -> -spec init([]) -> {ok, state()}. init([]) -> - {ok, TimerReference} = - timer:send_interval(?THIRTY_MINUTES_IN_MS, log_usage), + TimerReference = erlang:send_after(?THIRTY_MINUTES_IN_MS, self(), log_usage), erlang:send(self(), log_usage), {ok, #state{timer_ref = TimerReference}}. @@ -56,13 +55,17 @@ handle_cast(_M, State) -> -spec handle_info(log_usage, _) -> {noreply, _}. -handle_info(log_usage, State) -> - ok = log_memory(), {noreply, State}. +handle_info(log_usage, State = #state{timer_ref = TimerReference}) -> + _ = erlang:cancel_timer(TimerReference, [{async, true}, {info, false}]), + TRef = erlang:send_after(?THIRTY_MINUTES_IN_MS, self(), reload_clr_file), + ok = log_memory(), + {noreply, State#state{timer_ref = TRef}}. -spec terminate(_, state()) -> ok. terminate(_Reason, State) -> - {ok, cancel} = timer:cancel(State#state.timer_ref), ok. + _ = erlang:cancel_timer(State#state.timer_ref, [{async, true}, {info, false}]), + ok. -spec code_change(_, _, _) -> {ok, _}. diff --git a/apps/epp_proxy/test/tls_client_SUITE.erl b/apps/epp_proxy/test/tls_client_SUITE.erl index 8fd7255..b7a596f 100644 --- a/apps/epp_proxy/test/tls_client_SUITE.erl +++ b/apps/epp_proxy/test/tls_client_SUITE.erl @@ -33,6 +33,7 @@ all() -> init_per_suite(Config) -> application:ensure_all_started(epp_proxy), application:ensure_all_started(hackney), + ok = application:set_env(epp_proxy, crlfile_path, "test_ca/crl/first"), CWD = code:priv_dir(epp_proxy), Options = [binary, {certfile, filename:join(CWD, "test_ca/certs/client.crt.pem")}, @@ -121,14 +122,15 @@ session_test_case(Config) -> second_revoked_session_test_case(Config) -> ok = application:set_env(epp_proxy, crlfile_path, "test_ca/crl/second"), - epp_tls_acceptor ! reload_crl_file, - + epp_tls_monitor ! reload_acceptor, + ct:sleep({seconds, 5}), Options = proplists:get_value(second_revoked_options, Config), {error, Error} = ssl:connect("localhost", 1443, Options, 2000), {tls_alert, {certificate_revoked, "received CLIENT ALERT: Fatal - Certificate Revoked"}} = Error, +%% "TLS client: In state cipher received SERVER ALERT: Fatal - Certificate Revoked\n "}} = Error, ok. valid_command_test_case(Config) -> diff --git a/config/test.config b/config/test.config index a2c21ac..cbfd20f 100644 --- a/config/test.config +++ b/config/test.config @@ -12,7 +12,7 @@ {cacertfile_path, "test_ca/certs/ca.crt.pem"}, {certfile_path, "test_ca/certs/apache.crt"}, {keyfile_path, "test_ca/private/apache.key"}, - {crlfile_path, "test_ca/crl"}]}, + {crlfile_path, "test_ca/crl/first"}]}, {lager, [ diff --git a/rebar.config b/rebar.config index 52406ac..23f7594 100644 --- a/rebar.config +++ b/rebar.config @@ -57,4 +57,4 @@ {plugins, [rebar3_auto, {erl_tidy_prv_fmt, ".*", {git, "git://github.com/tsloughter/erl_tidy.git", {branch, "master"}}}, - rebar3_appup_plugin]}. \ No newline at end of file + rebar3_appup_plugin]}.