From c2c91667e779f24745df97bbbe84d4f93e5da462 Mon Sep 17 00:00:00 2001 From: Maciej Szlosarczyk Date: Mon, 27 May 2019 16:29:02 +0300 Subject: [PATCH] Working base loop --- apps/epp_proxy/src/epp_certs.erl | 5 +- apps/epp_proxy/src/epp_proxy_sup.erl | 12 +- apps/epp_proxy/src/epp_tcp.erl | 20 --- apps/epp_proxy/src/epp_tcp_acceptor.erl | 3 +- apps/epp_proxy/src/epp_tcp_worker.erl | 123 +++++++++++++------ apps/epp_proxy/src/epp_tls.erl | 51 -------- apps/epp_proxy/src/epp_tls_acceptor.erl | 67 ++++++++++ apps/epp_proxy/src/epp_tls_worker.erl | 155 ++++++++++++++++++++++++ apps/epp_proxy/src/epp_util.erl | 18 ++- apps/epp_proxy/test/epp_certs_tests.erl | 2 +- apps/epp_proxy/test/epp_util_tests.erl | 14 +++ config/sys.config | 5 +- 12 files changed, 355 insertions(+), 120 deletions(-) delete mode 100644 apps/epp_proxy/src/epp_tcp.erl delete mode 100644 apps/epp_proxy/src/epp_tls.erl create mode 100644 apps/epp_proxy/src/epp_tls_acceptor.erl create mode 100644 apps/epp_proxy/src/epp_tls_worker.erl diff --git a/apps/epp_proxy/src/epp_certs.erl b/apps/epp_proxy/src/epp_certs.erl index a8dfcb0..6133525 100644 --- a/apps/epp_proxy/src/epp_certs.erl +++ b/apps/epp_proxy/src/epp_certs.erl @@ -6,12 +6,13 @@ common_name_from_subject/1, certificate_to_pem/1, der_certificate/1, headers_from_cert/1]). -% Returns a tuple of headers {SSL_CLIENT_S_DN_CN, SLL_CLIENT_CERT} +% Returns a tuple of headers {SSL_CLIENT_S_DN_CN, SSL_CLIENT_CERT} headers_from_cert(Der) -> OTPCertificate = der_certificate(Der), Subject = subject_from_otp_certificate(OTPCertificate), CommonName = common_name_from_subject(Subject), - PEM = certificate_to_pem(OTPCertificate), + logger:info("~p~n", [CommonName]), + PEM = certificate_to_pem(Der), {CommonName, PEM}. %% Read certificate from the wire and return back an otp type record diff --git a/apps/epp_proxy/src/epp_proxy_sup.erl b/apps/epp_proxy/src/epp_proxy_sup.erl index 67545c5..21fe075 100644 --- a/apps/epp_proxy/src/epp_proxy_sup.erl +++ b/apps/epp_proxy/src/epp_proxy_sup.erl @@ -36,11 +36,15 @@ init([]) -> type => worker, modules => [epp_tcp_acceptor], start => {epp_tcp_acceptor, start_link, [3333]}}, - PoolSupervisor = #{id => pool_supervisor, + TLSAcceptor = #{id => epp_tls_acceptor, + type => worker, + modules => [epp_tls_acceptor], + start => {epp_tls_acceptor, start_link, [4444]}}, + PoolSupervisor = #{id => epp_pool_supervisor, type => supervisor, - modules => [pool_supervisor], - start => {pool_supervisor, start_link, []}}, - {ok, {SupFlags, [TCPAcceptor, PoolSupervisor]}}. + modules => [epp_pool_supervisor], + start => {epp_pool_supervisor, start_link, []}}, + {ok, {SupFlags, [TCPAcceptor, TLSAcceptor, PoolSupervisor]}}. %%==================================================================== %% Internal functions diff --git a/apps/epp_proxy/src/epp_tcp.erl b/apps/epp_proxy/src/epp_tcp.erl deleted file mode 100644 index 4c9d4b0..0000000 --- a/apps/epp_proxy/src/epp_tcp.erl +++ /dev/null @@ -1,20 +0,0 @@ --module(epp_tcp). - --behaviour(supervisor). --define(SERVER, ?MODULE). - --export([start_link/1, init/1]). - -start_link(Port) -> - supervisor:start_link({local, ?SERVER}, ?MODULE, [Port]). - -init([Port]) -> - process_flag(trap_exit, true), - SupFlags = #{strategy => one_for_one, intensity => 3, period => 60}, - ChildSpecs = [#{id => epp_tcp_acceptor, - start => {epp_tcp_acceptor, start_link, [Port]}}], - log_message(Port), - {ok, {SupFlags, ChildSpecs}}. - -log_message(Port) -> - logger:info("TCP listening on Port: ~p", [Port]). diff --git a/apps/epp_proxy/src/epp_tcp_acceptor.erl b/apps/epp_proxy/src/epp_tcp_acceptor.erl index 757106e..35a0ee1 100644 --- a/apps/epp_proxy/src/epp_tcp_acceptor.erl +++ b/apps/epp_proxy/src/epp_tcp_acceptor.erl @@ -2,10 +2,9 @@ -behaviour(gen_server). -define(SERVER, ?MODULE). --define(POOL_SUPERVISOR, pool_supervisor). +-define(POOL_SUPERVISOR, epp_pool_supervisor). -define(WORKER, epp_tcp_worker). - %% gen_server callbacks -export([init/1, handle_cast/2, handle_call/3, start_link/1]). diff --git a/apps/epp_proxy/src/epp_tcp_worker.erl b/apps/epp_proxy/src/epp_tcp_worker.erl index 75190a8..350134e 100644 --- a/apps/epp_proxy/src/epp_tcp_worker.erl +++ b/apps/epp_proxy/src/epp_tcp_worker.erl @@ -5,76 +5,93 @@ %% gen_server callbacks -export([init/1, handle_cast/2, handle_call/3, start_link/1]). --export([terminate/2, code_change/3]). +-export([code_change/3]). + +-export([request/3]). -record(state,{socket, length, session_id}). +-record(request,{method, url, body, cookies, headers}). init(Socket) -> logger:info("Created a worker process"), - {ok, #state{socket=Socket}}. + SessionId = session_id(), + {ok, #state{socket=Socket, session_id=SessionId}}. start_link(Socket) -> gen_server:start_link(?MODULE, Socket, []). handle_cast(serve, State = #state{socket=Socket}) -> {noreply, State#state{socket=Socket}}; -handle_cast(greeting, State = #state{socket=Socket}) -> - SessionId = session_id(), - Cookie = hackney_cookie:setcookie("session", SessionId, []), +handle_cast(greeting, State = #state{socket=Socket, session_id=SessionId}) -> + Request = request("hello", SessionId, ""), + logger:info("Request: ~p~n", [Request]), {_Status, _StatusCode, _Headers, ClientRef} = - hackney:request(get, epp_router:route_request("hello"), [], "", - [{cookie, Cookie}, insecure]), + hackney:request(Request#request.method, Request#request.url, + Request#request.headers, Request#request.body, + [{cookie, Request#request.cookies}, insecure]), {ok, Body} = hackney:body(ClientRef), - Length = byte_size(Body) + 4, - ByteSize = << Length:32/big >>, - io:format("State: ~p~n", [State]), - write_line(Socket, ByteSize), - write_line(Socket, Body), - gen_server:cast(self(), read_length), + frame_to_socket(Body, Socket), + gen_server:cast(self(), process_command), {noreply, State#state{socket=Socket, session_id=SessionId}}; -handle_cast(read_length, State = #state{socket=Socket}) -> - case read_length(Socket) of + +handle_cast(process_command, State = #state{socket=Socket, session_id=SessionId}) -> + Length = case read_length(Socket) of {ok, Data} -> - NewState = State#state{length=Data}, - gen_server:cast(self(), read_frame), - {noreply, NewState}; + Data; {error, _Details} -> {stop, normal, State} - end; -handle_cast(read_frame, State = #state{socket=Socket, length=Length}) -> - case read_frame(Socket, Length) of - {ok, Data} -> - ByteLength = byte_size(Data) + 4, - ByteSize = << ByteLength:32/big >>, - write_line(Socket, ByteSize), - write_line(Socket, Data), - gen_server:cast(self(), read_length), - {noreply, State}; - {error, _Details} -> + end, + + Frame = case read_frame(Socket, Length) of + {ok, FrameData} -> + io:format("~p~n", [FrameData]), + FrameData; + {error, _FrameDetails} -> {stop, normal, State} - end; -handle_cast(Message, State) -> - logger:info("Received message ~p.~n", [Message]), - {noreply, State}. + end, + + {ok, XMLRecord} = epp_xml:parse(Frame), + Command = epp_xml:get_command(XMLRecord), + + Request = request(Command, SessionId, Frame), + logger:info("Request: ~p~n", [Request]), + + {_Status, _StatusCode, _Headers, ClientRef} = + hackney:request(Request#request.method, Request#request.url, + Request#request.headers, Request#request.body, + [{cookie, Request#request.cookies}, insecure]), + + {ok, Body} = hackney:body(ClientRef), + + frame_to_socket(Body, Socket), + + %% On logout, close the socket. + %% Else, go back to the beginning of the loop. + if + Command =:= "logout" -> + ok = gen_tcp:shutdown(Socket, read_write), + {stop, normal, State}; + true -> + gen_server:cast(self(), process_command), + {noreply, State#state{socket=Socket, session_id=SessionId}} + end. handle_call(_E, _From, State) -> {noreply, State}. -terminate(_Reason, _State) -> - ok. code_change(_OldVersion, State, _Extra) -> {ok, State}. %% Private function write_line(Socket, Line) -> - gen_tcp:send(Socket, Line). + ok = gen_tcp:send(Socket, Line). read_length(Socket) -> case gen_tcp:recv(Socket, 4) of {ok, Data} -> Length = binary:decode_unsigned(Data, big), - io:format("Preparing to receive: ~p~n", [Length]), - {ok, Length - 4}; + LengthToReceive = epp_util:frame_length_to_receive(Length), + {ok, LengthToReceive}; {error, Reason} -> io:format("Error: ~p~n", [Reason]), {error, Reason} @@ -94,3 +111,33 @@ session_id() -> UniqueMap = epp_util:create_map(self()), BinaryHash = epp_util:create_session_id(UniqueMap), BinaryHash. + +%% Map request and return values +request(Command, SessionId, RawFrame) -> + URL = epp_router:route_request(Command), + RequestMethod = request_method(Command), + Cookie = hackney_cookie:setcookie("session", SessionId, []), + case Command of + "hello" -> + Body = ""; + _ -> + Body = {multipart, [{<<"raw_frame">>, RawFrame}]} + end, + Headers = [], + #request{url=URL, method=RequestMethod, body=Body, cookies=[Cookie], + headers=Headers}. + +%% request method: GET for greeting, POST for everything else. +request_method("hello") -> + get; +request_method(<<"hello">>) -> + get; +request_method(_) -> + post. + +%% Wrap a message in EPP frame, and then send it to socket. +frame_to_socket(Message, Socket) -> + Length = epp_util:frame_length_to_send(Message), + ByteSize = << Length:32/big >>, + write_line(Socket, ByteSize), + write_line(Socket, Message). diff --git a/apps/epp_proxy/src/epp_tls.erl b/apps/epp_proxy/src/epp_tls.erl deleted file mode 100644 index 49f42b0..0000000 --- a/apps/epp_proxy/src/epp_tls.erl +++ /dev/null @@ -1,51 +0,0 @@ --module(epp_tls). - --behaviour(supervisor). --define(SERVER, ?MODULE). - --export([start_link/1, init/1, serve/1]). - -start_link(Port) -> - supervisor:start_link({local, ?SERVER}, ?MODULE, [Port]). - -init([Port]) -> - process_flag(trap_exit, true), - SupFlags = #{strategy => one_for_one, intensity => 3, period => 60}, - ChildSpecs = [], - log_message(Port), - accept(Port), - {ok, {SupFlags, ChildSpecs}}. - -accept(Port) -> - Options = [binary, - {packet, raw}, - {active, false}, - {reuseaddr, true}, - {verify, verify_peer}, - {depth, 1}, - {cacertfile, "/Users/maciej/Development/internetee/docker-images/shared/ca/certs/ca.crt.pem"}, - {certfile, "/Users/maciej/Development/internetee/docker-images/shared/ca/certs/apache.crt"}, - {keyfile, "/Users/maciej/Development/internetee/docker-images/shared/ca/private/apache.key"}], - - {ok, ListenSocket} = ssl:listen(Port, Options), - spawn_monitor(fun () -> loop_acceptor(ListenSocket) end). - -loop_acceptor(ListenSocket) -> - case ssl:transport_accept(ListenSocket) of - {error, closed} -> exit(shutdown); - {ok, Client} -> - Pid = spawn_link(?MODULE, serve, [Client]), - logger:info("PID: ~p", [Pid]), - ok = ssl:controlling_process(Client, Pid) - end, - loop_acceptor(ListenSocket). - -serve(Client) -> - {ok, TSocket} = ssl:handshake(Client), - {ok, ClientCert} = ssl:peercert(TSocket), - logger:info("Client cert ~s", [ClientCert]), - timer:sleep(100000), - exit(shutdown). - -log_message(Port) -> - logger:info("SSL Listening on Port: ~p", [Port]). diff --git a/apps/epp_proxy/src/epp_tls_acceptor.erl b/apps/epp_proxy/src/epp_tls_acceptor.erl new file mode 100644 index 0000000..dbe1efe --- /dev/null +++ b/apps/epp_proxy/src/epp_tls_acceptor.erl @@ -0,0 +1,67 @@ +-module(epp_tls_acceptor). + +-behaviour(gen_server). + +-define(SERVER, ?MODULE). +-define(POOL_SUPERVISOR, epp_pool_supervisor). +-define(WORKER, epp_tls_worker). + +-define(CaCertFile, + case application:get_env(epp_proxy, epp_cacertfile_path) of + undefined -> undefined; + {ok, Value} -> Value + end). + +-define(CertFile, + case application:get_env(epp_proxy, epp_certfile_path) of + undefined -> undefined; + {ok, Value} -> Value + end). +-define(KeyFile, + case application:get_env(epp_proxy, epp_keyfile_path) of + undefined -> undefined; + {ok, Value} -> Value + end). + +%% gen_server callbacks +-export([init/1, handle_cast/2, handle_call/3, start_link/1]). + +-record(state, {socket, port, options}). + +start_link(Port) -> + gen_server:start_link({local, ?SERVER}, ?MODULE, Port, []). + +init(Port) -> + Options = [binary, + {packet, raw}, + {active, false}, + {reuseaddr, true}, + {verify, verify_peer}, + {depth, 1}, + {cacertfile, ?CaCertFile}, + {certfile, ?CertFile}, + {keyfile, ?KeyFile}], + + {ok, ListenSocket} = ssl:listen(Port, Options), + gen_server:cast(self(), accept), + + {ok, #state{socket=ListenSocket, port=Port, options=Options}}. + +handle_cast(accept, State = #state{socket=ListenSocket, port=Port, options=Options}) -> + {ok, AcceptSocket} = ssl:transport_accept(ListenSocket), + {ok, NewOwner} = create_worker(AcceptSocket), + ok = ssl:controlling_process(AcceptSocket, NewOwner), + gen_server:cast(NewOwner, serve), + gen_server:cast(NewOwner, greeting), + gen_server:cast(self(), accept), + {noreply, State#state{socket=ListenSocket, port=Port, options=Options}}. + +handle_call(_E, _From, State) -> {noreply, State}. + +create_worker(Socket) -> + ChildSpec = #{id => rand:uniform(), + type => worker, + modules => [?WORKER], + restart => temporary, + start => {?WORKER, start_link, [Socket]}}, + supervisor:start_child(?POOL_SUPERVISOR, ChildSpec). diff --git a/apps/epp_proxy/src/epp_tls_worker.erl b/apps/epp_proxy/src/epp_tls_worker.erl new file mode 100644 index 0000000..43f2108 --- /dev/null +++ b/apps/epp_proxy/src/epp_tls_worker.erl @@ -0,0 +1,155 @@ +-module(epp_tls_worker). + +-behaviour(gen_server). +-define(SERVER, ?MODULE). + +%% gen_server callbacks +-export([init/1, handle_cast/2, handle_call/3, start_link/1]). +-export([code_change/3]). + +-export([request/5]). + +-record(state,{socket, length, session_id, common_name, client_cert}). +-record(request,{method, url, body, cookies, headers}). + +init(Socket) -> + logger:info("Created a worker process"), + SessionId = session_id(), + {ok, #state{socket=Socket, session_id=SessionId}}. + +start_link(Socket) -> + gen_server:start_link(?MODULE, Socket, []). + +handle_cast(serve, State = #state{socket=Socket}) -> + {ok, SecureSocket} = ssl:handshake(Socket), + {ok, PeerCert} = ssl:peercert(SecureSocket), + {SSL_CLIENT_S_DN_CN, SSL_CLIENT_CERT} = + epp_certs:headers_from_cert(PeerCert), + + {noreply, State#state{socket=SecureSocket, common_name=SSL_CLIENT_S_DN_CN, + client_cert=SSL_CLIENT_CERT}}; +handle_cast(greeting, State = #state{socket=Socket, common_name=SSL_CLIENT_S_DN_CN, + client_cert=SSL_CLIENT_CERT, + session_id=SessionId}) -> + Request = request("hello", SessionId, "", SSL_CLIENT_S_DN_CN, + SSL_CLIENT_CERT), + logger:info("Request: ~p~n", [Request]), + + {_Status, _StatusCode, _Headers, ClientRef} = + hackney:request(Request#request.method, Request#request.url, + Request#request.headers, Request#request.body, + [{cookie, Request#request.cookies}, insecure]), + + {ok, Body} = hackney:body(ClientRef), + + frame_to_socket(Body, Socket), + gen_server:cast(self(), process_command), + {noreply, State#state{socket=Socket, session_id=SessionId}}; +handle_cast(process_command, State = #state{socket=Socket, + common_name=SSL_CLIENT_S_DN_CN, + client_cert=SSL_CLIENT_CERT, + session_id=SessionId}) -> + Length = case read_length(Socket) of + {ok, Data} -> + Data; + {error, _Details} -> + {stop, normal, State} + end, + + Frame = case read_frame(Socket, Length) of + {ok, FrameData} -> + io:format("~p~n", [FrameData]), + FrameData; + {error, _FrameDetails} -> + {stop, normal, State} + end, + + {ok, XMLRecord} = epp_xml:parse(Frame), + Command = epp_xml:get_command(XMLRecord), + + Request = request(Command, SessionId, Frame, SSL_CLIENT_S_DN_CN, + SSL_CLIENT_CERT), + logger:info("Request: ~p~n", [Request]), + + {_Status, _StatusCode, _Headers, ClientRef} = + hackney:request(Request#request.method, Request#request.url, + Request#request.headers, Request#request.body, + [{cookie, Request#request.cookies}, insecure]), + + {ok, Body} = hackney:body(ClientRef), + + frame_to_socket(Body, Socket), + + %% On logout, close the socket. + %% Else, go back to the beginning of the loop. + if + Command =:= "logout" -> + ok = ssl:shutdown(Socket, read_write), + {stop, normal, State}; + true -> + gen_server:cast(self(), process_command), + {noreply, State#state{socket=Socket, session_id=SessionId}} + end. +handle_call(_E, _From, State) -> {noreply, State}. +code_change(_OldVersion, State, _Extra) -> {ok, State}. + +%% Private function +write_line(Socket, Line) -> + ok = ssl:send(Socket, Line). + +read_length(Socket) -> + case ssl:recv(Socket, 4) of + {ok, Data} -> + Length = binary:decode_unsigned(Data, big), + LengthToReceive = epp_util:frame_length_to_receive(Length), + {ok, LengthToReceive}; + {error, Reason} -> + io:format("Error: ~p~n", [Reason]), + {error, Reason} + end. + +read_frame(Socket, FrameLength) -> + case ssl:recv(Socket, FrameLength) of + {ok, Data} -> + io:format("Frame: ~p~n", [Data]), + {ok, Data}; + {error, Reason} -> + io:format("Error: ~p~n", [Reason]), + {error, Reason} + end. + +session_id() -> + UniqueMap = epp_util:create_map(self()), + BinaryHash = epp_util:create_session_id(UniqueMap), + BinaryHash. + +%% Map request and return values +request(Command, SessionId, RawFrame, CommonName, ClientCert) -> + URL = epp_router:route_request(Command), + RequestMethod = request_method(Command), + Cookie = hackney_cookie:setcookie("session", SessionId, []), + case Command of + "hello" -> + Body = ""; + _ -> + Body = {multipart, [{<<"raw_frame">>, RawFrame}]} + end, + Headers = [{"SSL_CLIENT_CERT", ClientCert}, + {"SSL_CLIENT_S_DN_CN", CommonName}], + #request{url=URL, method=RequestMethod, body=Body, cookies=[Cookie], + headers=Headers}. + +%% request method: GET for greeting, POST for everything else. +request_method("hello") -> + get; +request_method(<<"hello">>) -> + get; +request_method(_) -> + post. + +%% Wrap a message in EPP frame, and then send it to socket. +frame_to_socket(Message, Socket) -> + Length = epp_util:frame_length_to_send(Message), + ByteSize = << Length:32/big >>, + write_line(Socket, ByteSize), + write_line(Socket, Message). diff --git a/apps/epp_proxy/src/epp_util.erl b/apps/epp_proxy/src/epp_util.erl index b0b8652..73666a2 100644 --- a/apps/epp_proxy/src/epp_util.erl +++ b/apps/epp_proxy/src/epp_util.erl @@ -1,6 +1,9 @@ -module(epp_util). --export([create_map/1, create_session_id/1]). +-export([create_map/1, create_session_id/1, frame_length/1, + frame_length_to_receive/1, frame_length_to_send/1]). + +-define(OFFSET, 4). % Give me a process id, I'll create a random map for you. -spec create_map(pid()) -> #{string() => pid(), string() => float(), @@ -24,3 +27,16 @@ create_session_id(#{"pid" := Pid, "random" := Random, "timestamp" := Timestamp}) BinaryHash = crypto:hash(sha512, ListOfGlyphs), String = lists:flatten([integer_to_list(X,16) || <> <= BinaryHash]), String. + +frame_length_to_receive(Size) when Size >= 0 -> + Size - ?OFFSET. + +frame_length_to_send(Frame) -> + Length = frame_length(Frame), + Length + ?OFFSET. + +frame_length(Frame) when is_binary(Frame) -> + byte_size(Frame); +frame_length(Frame) when is_list(Frame) -> + Bin = unicode:characters_to_binary(Frame), + byte_size(Bin). diff --git a/apps/epp_proxy/test/epp_certs_tests.erl b/apps/epp_proxy/test/epp_certs_tests.erl index 9993e5f..2a3eefd 100644 --- a/apps/epp_proxy/test/epp_certs_tests.erl +++ b/apps/epp_proxy/test/epp_certs_tests.erl @@ -15,7 +15,7 @@ wder_certificate_test() -> subject_from_otp_certificate_test() -> Certificate = test_certificate(), Subject = epp_certs:subject_from_otp_certificate(Certificate), - {rdnSequence, ListOfAttributes} = Subject. + {rdnSequence, _ListOfAttributes} = Subject. common_name_from_subject_test() -> Certificate = test_certificate(), diff --git a/apps/epp_proxy/test/epp_util_tests.erl b/apps/epp_proxy/test/epp_util_tests.erl index a34d0f6..d4ea680 100644 --- a/apps/epp_proxy/test/epp_util_tests.erl +++ b/apps/epp_proxy/test/epp_util_tests.erl @@ -17,3 +17,17 @@ create_session_id_test() -> Hash = epp_util:create_session_id(Map), ?assert(is_list(Hash)), ?assertEqual("88F49C2B1BDD6F2355BF4424A67C928DA7C5616B30F7C5B35F17747348AF61EF8723ED6BE6012D879CB3D6A7EC7A187660A56910ED896AE67DE533C212D", Hash). + +frame_length_test() -> + ?assertEqual(2, epp_util:frame_length("aa")), + ?assertEqual(2, epp_util:frame_length(<<"aa">>)), + ?assertEqual(2, epp_util:frame_length(<<"OÜ">>)). + +frame_length_to_receive_test() -> + ?assertEqual(2, epp_util:frame_length_to_receive(6)), + ?assertEqual(0, epp_util:frame_length_to_receive(4)), + ?assertError(function_clause, epp_util:frame_length_to_receive(-22)). + +frame_length_to_send_test() -> + ?assertEqual(18, epp_util:frame_length_to_send("")), + ?assertEqual(4, epp_util:frame_length_to_send("")). diff --git a/config/sys.config b/config/sys.config index cd748b1..6860f3b 100644 --- a/config/sys.config +++ b/config/sys.config @@ -2,5 +2,8 @@ {epp_proxy, [{tcp_port, 3333}, {tls_port, 4444}, {epp_session_url, "https://registry.test/epp/session/"}, - {epp_command_url, "https://registry.test/epp/command/"}]} + {epp_command_url, "https://registry.test/epp/command/"}, + {epp_cacertfile_path, "/Users/maciej/Development/internetee/docker-images/shared/ca/certs/ca.crt.pem"}, + {epp_certfile_path, "/Users/maciej/Development/internetee/docker-images/shared/ca/certs/apache.crt"}, + {epp_keyfile_path, "/Users/maciej/Development/internetee/docker-images/shared/ca/private/apache.key"}]} ].