From 77a96b0ec6c793ce0f5741d4683edd2ee9b3b877 Mon Sep 17 00:00:00 2001 From: Konstantinos Kallas Date: Wed, 12 Jul 2017 19:23:52 +0300 Subject: [PATCH] Solve acme challenges using built in http server --- ejabberd.yml.example | 4 ++- src/acme_challenge.erl | 62 ++++++++++++++++++++++++++++++++------ src/ejabberd_acme.erl | 13 ++++---- src/ejabberd_acme_comm.erl | 6 ++++ 4 files changed, 68 insertions(+), 17 deletions(-) diff --git a/ejabberd.yml.example b/ejabberd.yml.example index 5e6f9b631..7a75c5533 100644 --- a/ejabberd.yml.example +++ b/ejabberd.yml.example @@ -160,6 +160,7 @@ listen: request_handlers: "/websocket": ejabberd_http_ws "/api": mod_http_api + "/.well-known": acme_challenge ## "/pub/archive": mod_http_fileserver web_admin: true http_bind: true @@ -668,7 +669,8 @@ language: "en" acme: contact: "mailto:cert-admin-ejabberd@example.com" 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 diff --git a/src/acme_challenge.erl b/src/acme_challenge.erl index 433de8143..2638e0ddf 100644 --- a/src/acme_challenge.erl +++ b/src/acme_challenge.erl @@ -1,7 +1,9 @@ -module(acme_challenge). --export ([ key_authorization/2, - solve_challenge/3 +-export ([key_authorization/2, + solve_challenge/3, + + process/2 ]). %% Challenge Types %% ================ @@ -13,9 +15,19 @@ -include("ejabberd.hrl"). -include("logger.hrl"). -include("xmpp.hrl"). - +-include("ejabberd_http.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(). key_authorization(Token, Key) -> Thumbprint = jose_jwk:thumbprint(Key), @@ -68,17 +80,49 @@ solve_challenge(ChallengeType, Challenges, Options) -> {ok, url(), bitstring()} | {error, _}. solve_challenge1(Chal = #challenge{type = <<"http-01">>, token=Tkn}, {Key, HttpDir}) -> 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), case file:write_file(FileLocation, KeyAuthz) of ok -> {ok, Chal#challenge.uri, KeyAuthz}; - {error, _} = Err -> - ?ERROR_MSG("Error writing to file: ~s with reason: ~p~n", [FileLocation, Err]), + {error, Reason} = Err -> + ?ERROR_MSG("Error writing to file: ~s with reason: ~p~n", [FileLocation, Reason]), Err - end; -%% TODO: Fill stub -solve_challenge1(Challenge, _Key) -> - ?INFO_MSG("Challenge: ~p~n", [Challenge]). + end. + +-spec ets_put_key_authorization(bitstring(), bitstring()) -> ok. +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 diff --git a/src/ejabberd_acme.erl b/src/ejabberd_acme.erl index 60bb31bec..209ac46c5 100644 --- a/src/ejabberd_acme.erl +++ b/src/ejabberd_acme.erl @@ -513,13 +513,12 @@ get_config_hosts() -> end. get_config_cert_dir() -> - {ok, Acme} = get_config_acme(), - case lists:keyfind(cert_dir, 1, Acme) of - {cert_dir, CertDir} -> - {ok, CertDir}; - false -> - ?ERROR_MSG("No certificate directory has been specified", []), - {error, configuration_cert_dir} + case ejabberd_config:get_option(cert_dir, undefined) of + undefined -> + ?ERROR_MSG("No cert_dir configuration has been specified", []), + throw({error, configuration}); + CertDir -> + {ok, CertDir} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/src/ejabberd_acme_comm.erl b/src/ejabberd_acme_comm.erl index 804d46531..b66d5f610 100644 --- a/src/ejabberd_acme_comm.erl +++ b/src/ejabberd_acme_comm.erl @@ -32,6 +32,12 @@ -define(MAX_POLL_REQUESTS, 20). -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. +%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%