Implement mTLS authentication between client and server
This commit is contained in:
@@ -4,15 +4,15 @@
|
||||
-module(proto_sup).
|
||||
-behaviour(supervisor).
|
||||
|
||||
-export([start_link/1]).
|
||||
-export([start_link/2]).
|
||||
-export([init/1]).
|
||||
|
||||
start_link(Port) ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, [Port]).
|
||||
start_link(Port, CertDir) ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, [Port, CertDir]).
|
||||
|
||||
init([Port]) ->
|
||||
init([Port, CertDir]) ->
|
||||
SupFlags = #{stragegy => one_for_all,
|
||||
intensity => 1,
|
||||
period => 5},
|
||||
ChildSpecs = [tcp_server:child_spec(Port)],
|
||||
ChildSpecs = [tcp_server:child_spec(Port, CertDir)],
|
||||
{ok, {SupFlags, ChildSpecs}}.
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
{vsn, "0.1.0"},
|
||||
{registered, []},
|
||||
{mod, {study_system_server_app, []}},
|
||||
{applications, [kernel, stdlib]},
|
||||
{env, []},
|
||||
{applications, [kernel, stdlib, ssl]},
|
||||
{env, [{cert_dir, "../test"}]},
|
||||
{modules, []},
|
||||
{licenses, ["AGPL-3.0-only"]},
|
||||
{links, []}]}.
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
|
||||
start(_StartType, _StartArgs) ->
|
||||
Port = application:get_env(study_system_server, port, 12888),
|
||||
proto_sup:start_link(Port).
|
||||
{ok, CertDir} = application:get_env(study_system_server, cert_dir),
|
||||
proto_sup:start_link(Port, CertDir).
|
||||
|
||||
stop(_State) ->
|
||||
ok.
|
||||
|
||||
@@ -4,25 +4,31 @@
|
||||
-module(tcp_server).
|
||||
-behaviour(gen_server).
|
||||
|
||||
-export([start_link/1, child_spec/1]).
|
||||
-export([start_link/2, child_spec/2]).
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
|
||||
-record(state, {socket, acceptor}).
|
||||
|
||||
start_link(Port) ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [Port], []).
|
||||
start_link(Port, CertDir) ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [Port, CertDir], []).
|
||||
|
||||
child_spec(Port) ->
|
||||
child_spec(Port, CertDir) ->
|
||||
#{id => ?MODULE,
|
||||
start => {?MODULE, start_link, [Port]},
|
||||
start => {?MODULE, start_link, [Port, CertDir]},
|
||||
restart => permanent,
|
||||
shutdown => 5000,
|
||||
type => worker,
|
||||
modules => [?MODULE]}.
|
||||
|
||||
init([Port]) ->
|
||||
{ok, Socket} = gen_tcp:listen(Port, [binary, inet6, {active, true}]),
|
||||
init([Port, CertDir]) ->
|
||||
io:format("cert dir: ~p~n", [CertDir]),
|
||||
SslOpts = [{certfile, filename:join([CertDir, "server.pem"])},
|
||||
{cacertfile, filename:join([CertDir, "ca.pem"])},
|
||||
{verify, verify_peer},
|
||||
{fail_if_no_peer_cert, true}],
|
||||
{ok, Socket} =
|
||||
ssl:listen(Port, [binary, inet6, {active, true} | SslOpts]),
|
||||
Pid = spawn_link(fun() -> acceptor_loop(Socket) end),
|
||||
{ok, #state{socket = Socket, acceptor = Pid}}.
|
||||
|
||||
@@ -45,21 +51,27 @@ code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
acceptor_loop(Socket) ->
|
||||
case gen_tcp:accept(Socket) of
|
||||
{ok, ClientSocket} ->
|
||||
gen_tcp:controlling_process(
|
||||
ClientSocket,
|
||||
spawn(fun() -> handle_connection(ClientSocket) end)),
|
||||
acceptor_loop(Socket);
|
||||
case ssl:transport_accept(Socket) of
|
||||
{ok, TlsSocket} ->
|
||||
case ssl:handshake(TlsSocket) of
|
||||
{ok, ClientSocket} ->
|
||||
Pid = spawn(
|
||||
fun() -> handle_connection(ClientSocket) end),
|
||||
ok = ssl:controlling_process(ClientSocket, Pid),
|
||||
ssl:setopts(ClientSocket, [{active, true}]),
|
||||
acceptor_loop(Socket);
|
||||
{error, _Reason} ->
|
||||
acceptor_loop(Socket)
|
||||
end;
|
||||
{error, closed} ->
|
||||
ok
|
||||
end.
|
||||
|
||||
handle_connection(Socket) ->
|
||||
receive
|
||||
{tcp, Socket, Data} ->
|
||||
gen_tcp:send(Socket, Data),
|
||||
{ssl, Socket, Data} ->
|
||||
ssl:send(Socket, Data),
|
||||
handle_connection(Socket);
|
||||
{tcp_closed, Socket} ->
|
||||
{ssl_closed, Socket} ->
|
||||
ok
|
||||
end.
|
||||
|
||||
Reference in New Issue
Block a user