Solve acme challenges using built in http server

This commit is contained in:
Konstantinos Kallas 2017-07-12 19:23:52 +03:00
parent 5199ede4a2
commit 77a96b0ec6
4 changed files with 68 additions and 17 deletions

View File

@ -160,6 +160,7 @@ listen:
request_handlers: request_handlers:
"/websocket": ejabberd_http_ws "/websocket": ejabberd_http_ws
"/api": mod_http_api "/api": mod_http_api
"/.well-known": acme_challenge
## "/pub/archive": mod_http_fileserver ## "/pub/archive": mod_http_fileserver
web_admin: true web_admin: true
http_bind: true http_bind: true
@ -668,7 +669,8 @@ language: "en"
acme: acme:
contact: "mailto:cert-admin-ejabberd@example.com" contact: "mailto:cert-admin-ejabberd@example.com"
http_dir: "/home/konstantinos/Desktop/Programming/test-server-for-acme/" http_dir: "/home/konstantinos/Desktop/Programming/test-server-for-acme/"
cert_dir: "/usr/local/var/lib/ejabberd/"
cert_dir: "/usr/local/var/lib/ejabberd/"
###. ======= ###. =======
###' MODULES ###' MODULES

View File

@ -1,7 +1,9 @@
-module(acme_challenge). -module(acme_challenge).
-export ([ key_authorization/2, -export ([key_authorization/2,
solve_challenge/3 solve_challenge/3,
process/2
]). ]).
%% Challenge Types %% Challenge Types
%% ================ %% ================
@ -13,9 +15,19 @@
-include("ejabberd.hrl"). -include("ejabberd.hrl").
-include("logger.hrl"). -include("logger.hrl").
-include("xmpp.hrl"). -include("xmpp.hrl").
-include("ejabberd_http.hrl").
-include("ejabberd_acme.hrl"). -include("ejabberd_acme.hrl").
%% TODO: Maybe validate request here??
process(LocalPath, Request) ->
Result = ets_get_key_authorization(LocalPath),
?INFO_MSG("Trying to serve: ~p at: ~p", [Request, LocalPath]),
?INFO_MSG("Http Response: ~p", [Result]),
{200,
[{<<"Content-Type">>, <<"text/plain">>}],
Result}.
-spec key_authorization(bitstring(), jose_jwk:key()) -> bitstring(). -spec key_authorization(bitstring(), jose_jwk:key()) -> bitstring().
key_authorization(Token, Key) -> key_authorization(Token, Key) ->
Thumbprint = jose_jwk:thumbprint(Key), Thumbprint = jose_jwk:thumbprint(Key),
@ -68,17 +80,49 @@ solve_challenge(ChallengeType, Challenges, Options) ->
{ok, url(), bitstring()} | {error, _}. {ok, url(), bitstring()} | {error, _}.
solve_challenge1(Chal = #challenge{type = <<"http-01">>, token=Tkn}, {Key, HttpDir}) -> solve_challenge1(Chal = #challenge{type = <<"http-01">>, token=Tkn}, {Key, HttpDir}) ->
KeyAuthz = key_authorization(Tkn, Key), KeyAuthz = key_authorization(Tkn, Key),
%% save_key_authorization(Chal, Tkn, KeyAuthz, HttpDir);
ets_put_key_authorization(Tkn, KeyAuthz),
{ok, Chal#challenge.uri, KeyAuthz};
solve_challenge1(Challenge, _Key) ->
?INFO_MSG("Challenge: ~p~n", [Challenge]).
save_key_authorization(Chal, Tkn, KeyAuthz, HttpDir) ->
FileLocation = HttpDir ++ "/.well-known/acme-challenge/" ++ bitstring_to_list(Tkn), FileLocation = HttpDir ++ "/.well-known/acme-challenge/" ++ bitstring_to_list(Tkn),
case file:write_file(FileLocation, KeyAuthz) of case file:write_file(FileLocation, KeyAuthz) of
ok -> ok ->
{ok, Chal#challenge.uri, KeyAuthz}; {ok, Chal#challenge.uri, KeyAuthz};
{error, _} = Err -> {error, Reason} = Err ->
?ERROR_MSG("Error writing to file: ~s with reason: ~p~n", [FileLocation, Err]), ?ERROR_MSG("Error writing to file: ~s with reason: ~p~n", [FileLocation, Reason]),
Err Err
end; end.
%% TODO: Fill stub
solve_challenge1(Challenge, _Key) -> -spec ets_put_key_authorization(bitstring(), bitstring()) -> ok.
?INFO_MSG("Challenge: ~p~n", [Challenge]). ets_put_key_authorization(Tkn, KeyAuthz) ->
Tab = ets_get_acme_table(),
Key = [<<"acme-challenge">>, Tkn],
ets:insert(Tab, {Key, KeyAuthz}),
ok.
-spec ets_get_key_authorization([bitstring()]) -> bitstring().
ets_get_key_authorization(Key) ->
Tab = ets_get_acme_table(),
case ets:take(Tab, Key) of
[{Key, KeyAuthz}] ->
KeyAuthz;
_ ->
?ERROR_MSG("Unable to serve key authorization in: ~p", [Key]),
<<"">>
end.
-spec ets_get_acme_table() -> atom().
ets_get_acme_table() ->
case ets:info(acme) of
undefined ->
ets:new(acme, [named_table, public]);
_ ->
acme
end.
%% Useful functions %% Useful functions

View File

@ -513,13 +513,12 @@ get_config_hosts() ->
end. end.
get_config_cert_dir() -> get_config_cert_dir() ->
{ok, Acme} = get_config_acme(), case ejabberd_config:get_option(cert_dir, undefined) of
case lists:keyfind(cert_dir, 1, Acme) of undefined ->
{cert_dir, CertDir} -> ?ERROR_MSG("No cert_dir configuration has been specified", []),
{ok, CertDir}; throw({error, configuration});
false -> CertDir ->
?ERROR_MSG("No certificate directory has been specified", []), {ok, CertDir}
{error, configuration_cert_dir}
end. end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

View File

@ -32,6 +32,12 @@
-define(MAX_POLL_REQUESTS, 20). -define(MAX_POLL_REQUESTS, 20).
-define(POLL_WAIT_TIME, 500). % 500 ms. -define(POLL_WAIT_TIME, 500). % 500 ms.
%%%
%%% This module contains functions that implement all necessary http
%%% requests to the ACME Certificate Authority. Its purpose is to
%%% facilitate the acme client implementation by separating the
%%% handling/validating/parsing of all the needed http requests.
%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%