chore(oscp_crl): port OCSP and CRL features to CE repo (re4.4)
This commit is contained in:
parent
5bba281c2b
commit
af4141bef4
|
@ -3,7 +3,7 @@ version: '3.9'
|
|||
services:
|
||||
erlang:
|
||||
container_name: erlang
|
||||
image: ghcr.io/emqx/emqx-builder/4.4-19:24.1.5-3-ubuntu20.04
|
||||
image: ghcr.io/emqx/emqx-builder/4.4-20:24.3.4.2-1-ubuntu20.04
|
||||
env_file:
|
||||
- conf.env
|
||||
environment:
|
||||
|
|
|
@ -15,8 +15,8 @@ PROFILE="$1"
|
|||
VSN="$2"
|
||||
OLD_VSN="$3"
|
||||
PACKAGE_PATH="$4"
|
||||
FROM_OTP_VSN="${5:-24.1.5-3}"
|
||||
TO_OTP_VSN="${6:-24.1.5-3}"
|
||||
FROM_OTP_VSN="${5:-24.3.4.2-1}"
|
||||
TO_OTP_VSN="${6:-24.3.4.2-1}"
|
||||
|
||||
TEMPDIR=$(mktemp -d)
|
||||
trap '{ rm -rf -- "$TEMPDIR"; }' EXIT
|
||||
|
|
|
@ -29,7 +29,7 @@ jobs:
|
|||
- uses: actions/checkout@v2
|
||||
- uses: erlef/setup-beam@v1
|
||||
with:
|
||||
otp-version: "24.1.5"
|
||||
otp-version: "24.3.4.2"
|
||||
- name: prepare
|
||||
id: prepare
|
||||
run: |
|
||||
|
|
|
@ -1 +1 @@
|
|||
erlang 24.1.5-3
|
||||
erlang 24.3.4.2-1
|
||||
|
|
2
Makefile
2
Makefile
|
@ -3,7 +3,7 @@ REBAR = $(CURDIR)/rebar3
|
|||
BUILD = $(CURDIR)/build
|
||||
SCRIPTS = $(CURDIR)/scripts
|
||||
export EMQX_RELUP ?= true
|
||||
export EMQX_DEFAULT_BUILDER = ghcr.io/emqx/emqx-builder/4.4-19:24.1.5-3-alpine3.15.1
|
||||
export EMQX_DEFAULT_BUILDER = ghcr.io/emqx/emqx-builder/4.4-20:24.3.4.2-1-alpine3.15.1
|
||||
export EMQX_DEFAULT_RUNNER = alpine:3.15.1
|
||||
export OTP_VSN ?= $(shell $(CURDIR)/scripts/get-otp-vsn.sh)
|
||||
export PKG_VSN ?= $(shell $(CURDIR)/pkg-vsn.sh)
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
-include("emqx_auth_ldap.hrl").
|
||||
|
||||
-include_lib("emqx/include/emqx.hrl").
|
||||
-include_lib("eldap/include/eldap.hrl").
|
||||
-include_lib("eldap2/include/eldap.hrl").
|
||||
-include_lib("emqx/include/logger.hrl").
|
||||
|
||||
-export([ check_acl/5
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
-include("emqx_auth_ldap.hrl").
|
||||
|
||||
-include_lib("emqx/include/emqx.hrl").
|
||||
-include_lib("eldap/include/eldap.hrl").
|
||||
-include_lib("eldap2/include/eldap.hrl").
|
||||
-include_lib("emqx/include/logger.hrl").
|
||||
|
||||
-import(proplists, [get_value/2]).
|
||||
|
|
2
build
2
build
|
@ -268,7 +268,7 @@ make_docker() {
|
|||
## Name Default Example
|
||||
## ---------------------------------------------------------------------
|
||||
## EMQX_BASE_IMAGE current os centos:7
|
||||
## EMQX_ZIP_PACKAGE _packages/<current-zip-target> /tmp/emqx-4.4.0-otp24.1.5-3-el7-amd64.zip
|
||||
## EMQX_ZIP_PACKAGE _packages/<current-zip-target> /tmp/emqx-4.4.0-otp24.3.4.2-1-el7-amd64.zip
|
||||
## EMQX_IMAGE_TAG emqx/emqx:<current-vns-rel> emqx/emqx:testing-tag
|
||||
##
|
||||
make_docker_testing() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
ARG BUILD_FROM=ghcr.io/emqx/emqx-builder/4.4-19:24.1.5-3-alpine3.15.1
|
||||
ARG BUILD_FROM=ghcr.io/emqx/emqx-builder/4.4-20:24.3.4.2-1-alpine3.15.1
|
||||
ARG RUN_FROM=alpine:3.15.1
|
||||
FROM ${BUILD_FROM} AS builder
|
||||
|
||||
|
|
|
@ -1521,6 +1521,27 @@ listener.ssl.external.certfile = {{ platform_etc_dir }}/certs/cert.pem
|
|||
## Value: File
|
||||
listener.ssl.external.cacertfile = {{ platform_etc_dir }}/certs/cacert.pem
|
||||
|
||||
## URL for the OCSP responder to check the server certificate against.
|
||||
##
|
||||
## Value: String
|
||||
## listener.ssl.external.ocsp_responder_url = http://my.ocsp.responder.com
|
||||
|
||||
## Path to the file containing PEM-encoded certificate of the OCSP
|
||||
## issuer for the server certificate.
|
||||
##
|
||||
## Value: File
|
||||
## listener.ssl.external.ocsp_issuer_pem = {{ platform_etc_dir }}/certs/ocsp-issuer.pem
|
||||
|
||||
## The period to refresh the OCSP response for the server.
|
||||
##
|
||||
## Value: Duration
|
||||
## listener.ssl.external.ocsp_refresh_interval = 5m
|
||||
|
||||
## The timeout for the HTTP request when checking OCSP responses.
|
||||
##
|
||||
## Value: Duration
|
||||
## listener.ssl.external.ocsp_refresh_http_timeout = 15s
|
||||
|
||||
## The Ephemeral Diffie-Helman key exchange is a very effective way of
|
||||
## ensuring Forward Secrecy by exchanging a set of keys that never hit
|
||||
## the wire. Since the DH key is effectively signed by the private key,
|
||||
|
|
|
@ -852,6 +852,14 @@ end}.
|
|||
{datatype, string}
|
||||
]}.
|
||||
|
||||
{mapping, "clientid_enrichment_module", "emqx.clientid_enrichment_module", [
|
||||
{datatype, atom}
|
||||
]}.
|
||||
|
||||
{mapping, "special_auth_module", "emqx.special_auth_module", [
|
||||
{datatype, atom}
|
||||
]}.
|
||||
|
||||
%% @doc Allow anonymous authentication.
|
||||
{mapping, "allow_anonymous", "emqx.allow_anonymous", [
|
||||
{default, false},
|
||||
|
@ -1664,6 +1672,49 @@ end}.
|
|||
{datatype, {duration, ms}}
|
||||
]}.
|
||||
|
||||
{mapping, "listener.ssl.$name.ocsp_responder_url", "emqx.listeners", [
|
||||
{datatype, string}
|
||||
]}.
|
||||
|
||||
{mapping, "listener.ssl.$name.ocsp_issuer_pem", "emqx.listeners", [
|
||||
{datatype, file}
|
||||
]}.
|
||||
|
||||
{mapping, "listener.ssl.$name.ocsp_refresh_interval", "emqx.listeners", [
|
||||
{default, "5m"},
|
||||
{datatype, {duration, ms}}
|
||||
]}.
|
||||
|
||||
{mapping, "listener.ssl.$name.ocsp_refresh_http_timeout", "emqx.listeners", [
|
||||
{default, "15000ms"},
|
||||
{datatype, {duration, ms}}
|
||||
]}.
|
||||
|
||||
{mapping, "listener.ssl.$name.enable_crl_cache", "emqx.listeners", [
|
||||
{default, false},
|
||||
{datatype, {enum, [true, false]}}
|
||||
]}.
|
||||
|
||||
{mapping, "listener.ssl.$name.crl_cache_http_timeout", "emqx.listeners", [
|
||||
{default, "15s"},
|
||||
{datatype, {duration, ms}}
|
||||
]}.
|
||||
|
||||
{mapping, "crl_cache.refresh_interval", "emqx.crl_cache_refresh_interval", [
|
||||
{default, "15m"},
|
||||
{datatype, {duration, ms}}
|
||||
]}.
|
||||
|
||||
{mapping, "crl_cache.urls", "emqx.crl_cache_urls", [
|
||||
{default, ""},
|
||||
{datatype, string}
|
||||
]}.
|
||||
|
||||
{translation, "emqx.crl_cache_urls", fun(Conf) ->
|
||||
Val = cuttlefish:conf_get("crl_cache.urls", Conf),
|
||||
string:tokens(Val, ", ")
|
||||
end}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% MQTT/WebSocket Listeners
|
||||
|
||||
|
@ -2184,6 +2235,10 @@ end}.
|
|||
{supported_subprotocols, string:tokens(cuttlefish:conf_get(Prefix ++ ".supported_subprotocols", Conf, ""), ", ")},
|
||||
{peer_cert_as_username, cuttlefish:conf_get(Prefix ++ ".peer_cert_as_username", Conf, undefined)},
|
||||
{peer_cert_as_clientid, cuttlefish:conf_get(Prefix ++ ".peer_cert_as_clientid", Conf, undefined)},
|
||||
{ocsp_responder_url, cuttlefish:conf_get(Prefix ++ ".ocsp_responder_url", Conf, undefined)},
|
||||
{ocsp_issuer_pem, cuttlefish:conf_get(Prefix ++ ".ocsp_issuer_pem", Conf, undefined)},
|
||||
{ocsp_refresh_interval, cuttlefish:conf_get(Prefix ++ ".ocsp_refresh_interval", Conf, undefined)},
|
||||
{ocsp_refresh_http_timeout, cuttlefish:conf_get(Prefix ++ ".ocsp_refresh_http_timeout", Conf, undefined)},
|
||||
{compress, cuttlefish:conf_get(Prefix ++ ".compress", Conf, undefined)},
|
||||
{idle_timeout, cuttlefish:conf_get(Prefix ++ ".idle_timeout", Conf, undefined)},
|
||||
{max_frame_size, cuttlefish:conf_get(Prefix ++ ".max_frame_size", Conf, undefined)},
|
||||
|
@ -2271,7 +2326,18 @@ end}.
|
|||
undefined -> undefined;
|
||||
_ -> {fun emqx_psk:lookup/3, <<>>}
|
||||
end,
|
||||
Filter([{versions, Versions},
|
||||
CRLCheck = case cuttlefish:conf_get(Prefix ++ ".enable_crl_cache", Conf, false) of
|
||||
true ->
|
||||
HTTPTimeout = cuttlefish:conf_get(Prefix ++ ".crl_cache_http_timeout", Conf, timer:seconds(15)),
|
||||
%% {crl_check, true} doesn't work
|
||||
[ {crl_check, peer}
|
||||
, {crl_cache, {ssl_crl_cache, {internal, [{http, HTTPTimeout}]}}}
|
||||
];
|
||||
false ->
|
||||
[]
|
||||
end,
|
||||
Filter(CRLCheck ++
|
||||
[{versions, Versions},
|
||||
{ciphers, Ciphers},
|
||||
{user_lookup_fun, UserLookupFun},
|
||||
{handshake_timeout, cuttlefish:conf_get(Prefix ++ ".handshake_timeout", Conf, undefined)},
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
## i.e. will not work if docker command has to be executed with sudo
|
||||
|
||||
## example:
|
||||
## ./scripts/buildx.sh --profile emqx --pkgtype zip --builder ghcr.io/emqx/emqx-builder/4.4-19:24.1.5-3-debian10 --arch arm64
|
||||
## ./scripts/buildx.sh --profile emqx --pkgtype zip --builder ghcr.io/emqx/emqx-builder/4.4-20:24.3.4.2-1-debian10 --arch arm64
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
|
@ -20,7 +20,7 @@ help() {
|
|||
echo "--arch amd64|arm64: Target arch to build the EMQ X package for"
|
||||
echo "--src_dir <SRC_DIR>: EMQ X source ode in this dir, default to PWD"
|
||||
echo "--builder <BUILDER>: Builder image to pull"
|
||||
echo " E.g. ghcr.io/emqx/emqx-builder/4.4-19:24.1.5-3-debian11"
|
||||
echo " E.g. ghcr.io/emqx/emqx-builder/4.4-20:24.3.4.2-1-debian11"
|
||||
echo "--ssh: Pass ssh agent to the builder."
|
||||
echo " Also configures git in container to use ssh instead of https to clone deps"
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ set -euo pipefail
|
|||
## i.e. if we are to release a new enterprise without cutting a new opensource release
|
||||
## we should tag rel-e4.4.X in the opensource repo, and merge this tag to enterprise
|
||||
## then cut a release from the enterprise repo.
|
||||
latest_release="$(git describe --abbrev=0 --tags --match 'rel-e4.4.*' --match '[v|e]4.4*' --exclude '*beta*' --exclude '*alpha*' --exclude '*rc*')"
|
||||
latest_release="$(git describe --abbrev=0 --tags --match 'rel-e4.4.*' --match '[v|e]4.4*' --exclude '*beta*' --exclude '*alpha*' --exclude '*rc*' --exclude '*gocsp*')"
|
||||
echo "Compare base: $latest_release"
|
||||
|
||||
bad_app_count=0
|
||||
|
|
|
@ -18,7 +18,7 @@ case "${VSN_MATCH}" in
|
|||
PKG_VSN='*'
|
||||
;;
|
||||
*)
|
||||
echo "$0 ERROR: second arg must "
|
||||
echo "$0 ERROR: second arg must be either 'vsn_exact' or 'vsn_matcher'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
|
|
@ -48,7 +48,7 @@ esac
|
|||
## e4.3.11
|
||||
## rel-v4.4.3
|
||||
## rel-e4.4.3
|
||||
PREV_TAG="${PREV_TAG:-$(git describe --tag --abbrev=0 --match "[${TAG_PREFIX}|rel-]*" --exclude '*rc*' --exclude '*alpha*' --exclude '*beta*')}"
|
||||
PREV_TAG="${PREV_TAG:-$(git describe --tag --abbrev=0 --match "[${TAG_PREFIX}|rel-]*" --exclude '*rc*' --exclude '*alpha*' --exclude '*beta*' --exclude '*gocsp*')}"
|
||||
|
||||
shift 1
|
||||
# bash 3.2 treat empty array as unbound, so we can't use 'ESCRIPT_ARGS=()' here,
|
||||
|
|
|
@ -79,7 +79,10 @@ compile(topic, Topic) ->
|
|||
end.
|
||||
|
||||
pattern(Words) ->
|
||||
lists:member(<<"%u">>, Words) orelse lists:member(<<"%c">>, Words).
|
||||
lists:member(<<"%u">>, Words)
|
||||
orelse lists:member(<<"%c">>, Words)
|
||||
orelse lists:member(<<"%cida">>, Words)
|
||||
orelse lists:member(<<"%cna">>, Words).
|
||||
|
||||
bin(L) when is_list(L) ->
|
||||
list_to_binary(L);
|
||||
|
@ -165,5 +168,13 @@ feed_var(#{username := undefined}, [<<"%u">>|_Words], _Acc) ->
|
|||
nomatch;
|
||||
feed_var(ClientInfo = #{username := Username}, [<<"%u">>|Words], Acc) ->
|
||||
feed_var(ClientInfo, Words, [Username|Acc]);
|
||||
feed_var(#{clientid_alias := undefined}, [<<"%cida">>|_Words], _Acc) ->
|
||||
nomatch;
|
||||
feed_var(ClientInfo = #{clientid_alias := Alias}, [<<"%cida">>|Words], Acc) ->
|
||||
feed_var(ClientInfo, Words, [Alias|Acc]);
|
||||
feed_var(#{common_name_alias := undefined}, [<<"%cna">>|_Words], _Acc) ->
|
||||
nomatch;
|
||||
feed_var(ClientInfo = #{common_name_alias := Alias}, [<<"%cna">>|Words], Acc) ->
|
||||
feed_var(ClientInfo, Words, [Alias|Acc]);
|
||||
feed_var(ClientInfo, [W|Words], Acc) ->
|
||||
feed_var(ClientInfo, Words, [W|Acc]).
|
||||
|
|
|
@ -49,6 +49,8 @@ start(_Type, _Args) ->
|
|||
ok = emqx_plugins:init(),
|
||||
_ = emqx_plugins:load(),
|
||||
_ = start_ce_modules(),
|
||||
set_clientid_enrichment_module(),
|
||||
_ = set_special_auth_module(),
|
||||
register(emqx, self()),
|
||||
print_vsn(),
|
||||
{ok, Sup}.
|
||||
|
@ -78,6 +80,33 @@ start_ce_modules() ->
|
|||
ok.
|
||||
-endif.
|
||||
|
||||
set_clientid_enrichment_module() ->
|
||||
case emqx:get_env(clientid_enrichment_module) of
|
||||
undefined ->
|
||||
ok;
|
||||
Mod ->
|
||||
case erlang:function_exported(Mod, enrich_clientid_alias, 2) of
|
||||
true ->
|
||||
persistent_term:put(clientid_enrichment_module, Mod);
|
||||
false ->
|
||||
ok
|
||||
end
|
||||
end.
|
||||
|
||||
set_special_auth_module() ->
|
||||
case emqx:get_env(special_auth_module) of
|
||||
undefined ->
|
||||
ok;
|
||||
Mod ->
|
||||
case erlang:function_exported(Mod, check_authn, 2) of
|
||||
true ->
|
||||
persistent_term:put(special_auth_module, Mod),
|
||||
emqx:hook('client.authenticate', fun Mod:check_authn/2, []);
|
||||
false ->
|
||||
ok
|
||||
end
|
||||
end.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Print Banner
|
||||
%%--------------------------------------------------------------------
|
||||
|
|
|
@ -313,6 +313,7 @@ handle_in(?CONNECT_PACKET(ConnPkt) = Packet, Channel) ->
|
|||
fun set_log_meta/2,
|
||||
fun check_banned/2,
|
||||
fun count_flapping_event/2,
|
||||
fun enrich_clientid_alias/2,
|
||||
fun auth_connect/2
|
||||
], ConnPkt, Channel#channel{conn_state = connecting}) of
|
||||
{ok, NConnPkt, NChannel = #channel{clientinfo = ClientInfo}} ->
|
||||
|
@ -1353,6 +1354,17 @@ check_banned(_ConnPkt, #channel{clientinfo = ClientInfo = #{zone := Zone}}) ->
|
|||
false -> ok
|
||||
end.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Enrich ClientID Alias
|
||||
|
||||
enrich_clientid_alias(Packet, Channel) ->
|
||||
case persistent_term:get(clientid_enrichment_module, undefined) of
|
||||
undefined ->
|
||||
{ok, Channel};
|
||||
Mod ->
|
||||
Mod:enrich_clientid_alias(Packet, Channel)
|
||||
end.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Auth Connect
|
||||
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2022 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||
%%
|
||||
%% @doc EMQX CRL cache.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-module(emqx_crl_cache).
|
||||
|
||||
%% API
|
||||
-export([ start_link/0
|
||||
, start_link/1
|
||||
, refresh/1
|
||||
, evict/1
|
||||
]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([ init/1
|
||||
, handle_call/3
|
||||
, handle_cast/2
|
||||
, handle_info/2
|
||||
]).
|
||||
|
||||
%% internal exports
|
||||
-export([http_get/2]).
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
-include_lib("snabbkaffe/include/snabbkaffe.hrl").
|
||||
|
||||
-define(LOG(Level, Format, Args),
|
||||
logger:log(Level, "[~p] " ++ Format, [?MODULE | Args])).
|
||||
-define(HTTP_TIMEOUT, timer:seconds(10)).
|
||||
-define(RETRY_TIMEOUT, 5_000).
|
||||
|
||||
-record(state,
|
||||
{ refresh_timers = #{} :: #{binary() => timer:tref()}
|
||||
, refresh_interval = timer:minutes(15) :: timer:time()
|
||||
}).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% API
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
start_link() ->
|
||||
URLs = emqx:get_env(crl_cache_urls, []),
|
||||
RefreshIntervalMS = emqx:get_env(crl_cache_refresh_interval,
|
||||
timer:minutes(15)),
|
||||
start_link(#{urls => URLs, refresh_interval => RefreshIntervalMS}).
|
||||
|
||||
start_link(Opts = #{urls := _, refresh_interval := _}) ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, Opts, []).
|
||||
|
||||
refresh(URL) ->
|
||||
gen_server:call(?MODULE, {refresh, URL}, ?HTTP_TIMEOUT + 2_000).
|
||||
|
||||
evict(URL) ->
|
||||
gen_server:cast(?MODULE, {evict, URL}).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_server behaviour
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
init(#{urls := URLs, refresh_interval := RefreshIntervalMS}) ->
|
||||
State = lists:foldl(fun(URL, Acc) -> ensure_timer(URL, Acc, 0) end,
|
||||
#state{refresh_interval = RefreshIntervalMS},
|
||||
URLs),
|
||||
{ok, State}.
|
||||
|
||||
handle_call({refresh, URL}, _From, State0) ->
|
||||
case do_http_fetch_and_cache(URL) of
|
||||
{error, Error} ->
|
||||
?LOG(error, "failed to fetch crl response for ~p; error: ~p",
|
||||
[URL, Error]),
|
||||
{reply, error, ensure_timer(URL, State0, ?RETRY_TIMEOUT)};
|
||||
{ok, CRLs} ->
|
||||
?LOG(debug, "fetched crl response for ~p", [URL]),
|
||||
{reply, {ok, CRLs}, ensure_timer(URL, State0)}
|
||||
end;
|
||||
handle_call(Call, _From, State) ->
|
||||
{reply, {error, {bad_call, Call}}, State}.
|
||||
|
||||
handle_cast({evict, URL}, State0 = #state{refresh_timers = RefreshTimers0}) ->
|
||||
ssl_crl_cache:delete(URL),
|
||||
MTimer = maps:get(URL, RefreshTimers0, undefined),
|
||||
emqx_misc:cancel_timer(MTimer),
|
||||
RefreshTimers = maps:without([URL], RefreshTimers0),
|
||||
State = State0#state{refresh_timers = RefreshTimers},
|
||||
?tp(crl_cache_evict,
|
||||
#{ url => URL
|
||||
}),
|
||||
{noreply, State};
|
||||
handle_cast(_Cast, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info({timeout, TRef, {refresh, URL}},
|
||||
State = #state{refresh_timers = RefreshTimers}) ->
|
||||
case maps:get(URL, RefreshTimers, undefined) of
|
||||
TRef ->
|
||||
?tp(crl_refresh_timer, #{url => URL}),
|
||||
?LOG(debug, "refreshing crl response for ~p", [URL]),
|
||||
case do_http_fetch_and_cache(URL) of
|
||||
{error, Error} ->
|
||||
?LOG(error, "failed to fetch crl response for ~p; error: ~p",
|
||||
[URL, Error]),
|
||||
{noreply, ensure_timer(URL, State, ?RETRY_TIMEOUT)};
|
||||
{ok, _CRLs} ->
|
||||
?LOG(debug, "fetched crl response for ~p", [URL]),
|
||||
{noreply, ensure_timer(URL, State)}
|
||||
end;
|
||||
_ ->
|
||||
{noreply, State}
|
||||
end;
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
http_get(URL, HTTPTimeout) ->
|
||||
httpc:request(
|
||||
get,
|
||||
{URL,
|
||||
[{"connection", "close"}]},
|
||||
[{timeout, HTTPTimeout}],
|
||||
[{body_format, binary}]
|
||||
).
|
||||
|
||||
do_http_fetch_and_cache(URL) ->
|
||||
%% FIXME
|
||||
Resp = ?MODULE:http_get(URL, ?HTTP_TIMEOUT),
|
||||
case Resp of
|
||||
{ok, {{_, 200, _}, _, Body}} ->
|
||||
case parse_crls(Body) of
|
||||
error ->
|
||||
{error, invalid_crl};
|
||||
CRLs ->
|
||||
ssl_crl_cache:insert(URL, {der, CRLs}),
|
||||
?tp(crl_cache_insert, #{url => URL}),
|
||||
{ok, CRLs}
|
||||
end;
|
||||
{ok, {{_, Code, _}, _, Body}} ->
|
||||
{error, {bad_response, #{code => Code, body => Body}}};
|
||||
{error, Error} ->
|
||||
{error, {http_error, Error}}
|
||||
end.
|
||||
|
||||
parse_crls(Bin) ->
|
||||
try
|
||||
[CRL || {'CertificateList', CRL, not_encrypted} <- public_key:pem_decode(Bin)]
|
||||
catch
|
||||
_:_ ->
|
||||
error
|
||||
end.
|
||||
|
||||
ensure_timer(URL, State = #state{refresh_interval = Timeout}) ->
|
||||
ensure_timer(URL, State, Timeout).
|
||||
|
||||
ensure_timer(URL, State = #state{refresh_timers = RefreshTimers0}, Timeout) ->
|
||||
MTimer = maps:get(URL, RefreshTimers0, undefined),
|
||||
emqx_misc:cancel_timer(MTimer),
|
||||
RefreshTimers = RefreshTimers0#{URL => emqx_misc:start_timer(
|
||||
Timeout,
|
||||
{refresh, URL})},
|
||||
State#state{refresh_timers = RefreshTimers}.
|
|
@ -33,7 +33,9 @@ init([]) ->
|
|||
child_spec(emqx_stats, worker),
|
||||
child_spec(emqx_metrics, worker),
|
||||
child_spec(emqx_ctl, worker),
|
||||
child_spec(emqx_zone, worker)]}}.
|
||||
child_spec(emqx_zone, worker),
|
||||
child_spec(emqx_ocsp_cache, worker),
|
||||
child_spec(emqx_crl_cache, worker)]}}.
|
||||
|
||||
child_spec(M, Type) ->
|
||||
child_spec(M, Type, []).
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
, proto := esockd:proto()
|
||||
, listen_on := esockd:listen_on()
|
||||
, opts := [esockd:option()]
|
||||
, any() => term()
|
||||
}).
|
||||
|
||||
%% @doc Find listener identifier by listen-on.
|
||||
|
@ -104,8 +105,9 @@ ensure_all_started([L | Rest], Results) ->
|
|||
format_listen_on(ListenOn) -> format(ListenOn).
|
||||
|
||||
-spec(start_listener(listener()) -> ok).
|
||||
start_listener(#{proto := Proto, name := Name, listen_on := ListenOn, opts := Options}) ->
|
||||
start_listener(Listener = #{proto := Proto, name := Name, listen_on := ListenOn}) ->
|
||||
ID = identifier(Proto, Name),
|
||||
Options = emqx_ocsp_cache:inject_sni_fun(Listener),
|
||||
case start_listener(Proto, ListenOn, Options) of
|
||||
{ok, _} ->
|
||||
console_print("Start ~s listener on ~s successfully.~n", [ID, format(ListenOn)]);
|
||||
|
|
|
@ -0,0 +1,313 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2022 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||
%%
|
||||
%% @doc EMQX OCSP cache.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-module(emqx_ocsp_cache).
|
||||
|
||||
-include_lib("public_key/include/public_key.hrl").
|
||||
-include_lib("ssl/src/ssl_handshake.hrl").
|
||||
-include_lib("snabbkaffe/include/snabbkaffe.hrl").
|
||||
|
||||
-behaviour(gen_server).
|
||||
|
||||
-export([ start_link/0
|
||||
, sni_fun/2
|
||||
, fetch_response/1
|
||||
, register_listener/1
|
||||
, inject_sni_fun/1
|
||||
]).
|
||||
|
||||
%% gen_server API
|
||||
-export([ init/1
|
||||
, handle_call/3
|
||||
, handle_cast/2
|
||||
, handle_info/2
|
||||
, code_change/3
|
||||
]).
|
||||
|
||||
%% internal export; only for mocking in tests
|
||||
-export([http_get/2]).
|
||||
|
||||
-define(LOG(Level, Format, Args),
|
||||
logger:log(Level, "[~p] " ++ Format, [?MODULE | Args])).
|
||||
-define(CACHE_TAB, ?MODULE).
|
||||
-define(CALL_TIMEOUT, 20_000).
|
||||
-define(RETRY_TIMEOUT, 5_000).
|
||||
-define(REFRESH_TIMER(LID), {refresh_timer, LID}).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% API
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
|
||||
|
||||
sni_fun(_ServerName, ListenerID) ->
|
||||
Res = try
|
||||
fetch_response(ListenerID)
|
||||
catch
|
||||
_:_ -> error
|
||||
end,
|
||||
case Res of
|
||||
{ok, Response} ->
|
||||
[{certificate_status, #certificate_status{
|
||||
status_type = ?CERTIFICATE_STATUS_TYPE_OCSP,
|
||||
response = Response
|
||||
}}];
|
||||
error ->
|
||||
[]
|
||||
end.
|
||||
|
||||
fetch_response(ListenerID) ->
|
||||
case ets:lookup(?CACHE_TAB, cache_key(ListenerID)) of
|
||||
[{_, DERResponse}] ->
|
||||
?tp(ocsp_cache_hit, #{listener_id => ListenerID}),
|
||||
?LOG(debug, "using cached ocsp response for ~p", [ListenerID]),
|
||||
{ok, DERResponse};
|
||||
[] ->
|
||||
?tp(ocsp_cache_miss, #{listener_id => ListenerID}),
|
||||
?LOG(debug, "fetching new ocsp response for ~p", [ListenerID]),
|
||||
http_fetch(ListenerID)
|
||||
end.
|
||||
|
||||
register_listener(ListenerID) ->
|
||||
gen_server:call(?MODULE, {register_listener, ListenerID}, ?CALL_TIMEOUT).
|
||||
|
||||
-spec inject_sni_fun(emqx_listeners:listener()) -> [esockd:option()].
|
||||
inject_sni_fun(Listener = #{proto := Proto, name := Name, opts := Options0}) ->
|
||||
%% We need to patch `sni_fun' here and not in `emqx.schema'
|
||||
%% because otherwise an anonymous function will end up in
|
||||
%% `app.*.config'...
|
||||
ListenerID = emqx_listeners:identifier(Listener),
|
||||
case proplists:get_value(ocsp_responder_url, Options0, undefined) of
|
||||
undefined ->
|
||||
Options0;
|
||||
_URL ->
|
||||
SSLOpts0 = proplists:get_value(ssl_options, Options0, []),
|
||||
SNIFun = fun(SN) -> emqx_ocsp_cache:sni_fun(SN, ListenerID) end,
|
||||
Options1 = proplists:delete(ssl_options, Options0),
|
||||
Options = [{ssl_options, [{sni_fun, SNIFun} | SSLOpts0]} | Options1],
|
||||
%% save to env
|
||||
{[ThisListener0], Listeners} =
|
||||
lists:partition(
|
||||
fun(#{name := N, proto := P}) ->
|
||||
N =:= Name andalso P =:= Proto
|
||||
end,
|
||||
emqx:get_env(listeners)),
|
||||
ThisListener = ThisListener0#{opts => Options},
|
||||
application:set_env(emqx, listeners, [ThisListener | Listeners]),
|
||||
ok = emqx_ocsp_cache:register_listener(ListenerID),
|
||||
Options
|
||||
end.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% gen_server behaviour
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
init(_Args) ->
|
||||
logger:set_process_metadata(#{domain => [emqx, ocsp, cache]}),
|
||||
_ = ets:new(?CACHE_TAB, [ named_table
|
||||
, protected
|
||||
, {read_concurrency, true}
|
||||
]),
|
||||
?tp(ocsp_cache_init, #{}),
|
||||
{ok, #{}}.
|
||||
|
||||
handle_call({http_fetch, ListenerID}, _From, State) ->
|
||||
%% Respond immediately if a concurrent call already fetched it.
|
||||
case ets:lookup(?CACHE_TAB, cache_key(ListenerID)) of
|
||||
[{_, Response}] ->
|
||||
{reply, {ok, Response}, State};
|
||||
[] ->
|
||||
case do_http_fetch_and_cache(ListenerID) of
|
||||
error -> {reply, error, ensure_timer(ListenerID, State, ?RETRY_TIMEOUT)};
|
||||
{ok, Response} -> {reply, {ok, Response}, ensure_timer(ListenerID, State)}
|
||||
end
|
||||
end;
|
||||
handle_call({register_listener, ListenerID}, _From, State0) ->
|
||||
?LOG(debug, "registering ocsp cache for ~p", [ListenerID]),
|
||||
#{opts := Opts} = emqx_listeners:find_by_id(ListenerID),
|
||||
RefreshInterval = proplists:get_value(ocsp_refresh_interval, Opts),
|
||||
State = State0#{{refresh_interval, ListenerID} => RefreshInterval},
|
||||
{reply, ok, ensure_timer(ListenerID, State, 0)};
|
||||
handle_call(Call, _From, State) ->
|
||||
{reply, {error, {unknown_call, Call}}, State}.
|
||||
|
||||
handle_cast(_Cast, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info({timeout, TRef, {refresh, ListenerID}}, State0) ->
|
||||
case maps:get(?REFRESH_TIMER(ListenerID), State0, undefined) of
|
||||
TRef ->
|
||||
?tp(ocsp_refresh_timer, #{listener_id => ListenerID}),
|
||||
?LOG(debug, "refreshing ocsp response for ~p", [ListenerID]),
|
||||
case do_http_fetch_and_cache(ListenerID) of
|
||||
error ->
|
||||
?LOG(debug, "failed to fetch ocsp response for ~p", [ListenerID]),
|
||||
{noreply, ensure_timer(ListenerID, State0, ?RETRY_TIMEOUT)};
|
||||
{ok, _Response} ->
|
||||
?LOG(debug, "fetched ocsp response for ~p", [ListenerID]),
|
||||
{noreply, ensure_timer(ListenerID, State0)}
|
||||
end;
|
||||
_ ->
|
||||
{noreply, State0}
|
||||
end;
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
code_change(_Vsn, State, _Extra) ->
|
||||
%% we need to re-create the `sni_fun' lambda that the SSL
|
||||
%% listeners are holding onto to avoid them becoming `badfun''s.
|
||||
ListenersToPatch =
|
||||
lists:filter(
|
||||
fun(#{opts := Opts}) ->
|
||||
undefined =/= proplists:get_value(ocsp_responder_url, Opts)
|
||||
end,
|
||||
emqx:get_env(listeners, [])),
|
||||
PatchedListeners = [L#{opts => ?MODULE:inject_sni_fun(L)} || L <- ListenersToPatch],
|
||||
lists:foreach(
|
||||
fun(L) ->
|
||||
emqx_listeners:update_listeners_env(update, L)
|
||||
end,
|
||||
PatchedListeners),
|
||||
{ok, State}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% internal functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
http_fetch(ListenerID) ->
|
||||
%% TODO: configurable call timeout?
|
||||
gen_server:call(?MODULE, {http_fetch, ListenerID}, ?CALL_TIMEOUT).
|
||||
|
||||
cache_key(ListenerID) ->
|
||||
#{opts := Options} = emqx_listeners:find_by_id(ListenerID),
|
||||
SSLOpts = proplists:get_value(ssl_options, Options, undefined),
|
||||
ServerCertPemPath = proplists:get_value(certfile, SSLOpts, undefined),
|
||||
#'Certificate'{
|
||||
tbsCertificate =
|
||||
#'TBSCertificate'{
|
||||
signature = Signature
|
||||
}} = read_server_cert(ServerCertPemPath),
|
||||
{ocsp_response, Signature}.
|
||||
|
||||
read_server_cert(ServerCertPemPath0) ->
|
||||
ServerCertPemPath = to_bin(ServerCertPemPath0),
|
||||
case ets:lookup(ssl_pem_cache, ServerCertPemPath) of
|
||||
[{_, [{'Certificate', ServerCertDer, _} | _]}] ->
|
||||
public_key:der_decode('Certificate', ServerCertDer);
|
||||
[] ->
|
||||
case file:read_file(ServerCertPemPath) of
|
||||
{ok, ServerCertPem} ->
|
||||
[{'Certificate', ServerCertDer, _} | _] =
|
||||
public_key:pem_decode(ServerCertPem),
|
||||
public_key:der_decode('Certificate', ServerCertDer);
|
||||
{error, Error1} -> error({bad_server_cert_file, Error1})
|
||||
end
|
||||
end.
|
||||
|
||||
do_http_fetch_and_cache(ListenerID) ->
|
||||
#{opts := Options} = emqx_listeners:find_by_id(ListenerID),
|
||||
ResponderURL0 = proplists:get_value(ocsp_responder_url, Options, undefined),
|
||||
ResponderURL = uri_string:normalize(ResponderURL0),
|
||||
IssuerPemPath = proplists:get_value(ocsp_issuer_pem, Options, undefined),
|
||||
SSLOpts = proplists:get_value(ssl_options, Options, undefined),
|
||||
ServerCertPemPath = proplists:get_value(certfile, SSLOpts, undefined),
|
||||
IssuerPem = case file:read_file(IssuerPemPath) of
|
||||
{ok, IssuerPem0} -> IssuerPem0;
|
||||
{error, Error0} -> error({bad_issuer_pem_file, Error0})
|
||||
end,
|
||||
ServerCert = read_server_cert(ServerCertPemPath),
|
||||
Request = build_ocsp_request(IssuerPem, ServerCert),
|
||||
HTTPTimeout = proplists:get_value(ocsp_refresh_http_timeout, Options),
|
||||
?tp(ocsp_http_fetch, #{ listener_id => ListenerID
|
||||
, responder_url => ResponderURL
|
||||
, timeout => HTTPTimeout
|
||||
}),
|
||||
Resp = ?MODULE:http_get(ResponderURL ++ Request, HTTPTimeout),
|
||||
case Resp of
|
||||
{ok, {{_, 200, _}, _, Body}} ->
|
||||
?LOG(debug, "caching ocsp response for ~p", [ListenerID]),
|
||||
true = ets:insert(?CACHE_TAB, {cache_key(ListenerID), Body}),
|
||||
?tp(ocsp_http_fetch_and_cache, #{listener_id => ListenerID}),
|
||||
{ok, Body};
|
||||
{ok, {{_, Code, _}, _, Body}} ->
|
||||
?tp(error, ocsp_http_fetch_bad_code,
|
||||
#{ listener_id => ListenerID
|
||||
, body => Body
|
||||
, code => Code
|
||||
}),
|
||||
?LOG(error, "error fetching ocsp response for ~p: code ~b, body: ~p", [ListenerID, Code, Body]),
|
||||
error;
|
||||
{error, Error} ->
|
||||
?tp(error, ocsp_http_fetch_error,
|
||||
#{ listener_id => ListenerID
|
||||
, error => Error
|
||||
}),
|
||||
?LOG(error, "error fetching ocsp response for ~p: ~p", [ListenerID, Error]),
|
||||
error
|
||||
end.
|
||||
|
||||
http_get(URL, HTTPTimeout) ->
|
||||
httpc:request(
|
||||
get,
|
||||
{URL,
|
||||
[{"connection", "close"}]},
|
||||
[{timeout, HTTPTimeout}],
|
||||
[{body_format, binary}]
|
||||
).
|
||||
|
||||
ensure_timer(ListenerID, State) ->
|
||||
Timeout = maps:get({refresh_interval, ListenerID}, State, timer:minutes(5)),
|
||||
ensure_timer(ListenerID, State, Timeout).
|
||||
|
||||
ensure_timer(ListenerID, State, Timeout) ->
|
||||
emqx_misc:cancel_timer(maps:get(?REFRESH_TIMER(ListenerID), State, undefined)),
|
||||
State#{?REFRESH_TIMER(ListenerID) => emqx_misc:start_timer(
|
||||
Timeout,
|
||||
{refresh, ListenerID})}.
|
||||
|
||||
build_ocsp_request(IssuerPem, ServerCert) ->
|
||||
[{'Certificate', IssuerDer, _} | _] = public_key:pem_decode(IssuerPem),
|
||||
#'Certificate'{
|
||||
tbsCertificate =
|
||||
#'TBSCertificate'{
|
||||
serialNumber = SerialNumber,
|
||||
issuer = Issuer
|
||||
}} = ServerCert,
|
||||
#'Certificate'{
|
||||
tbsCertificate =
|
||||
#'TBSCertificate'{
|
||||
subjectPublicKeyInfo =
|
||||
#'SubjectPublicKeyInfo'{subjectPublicKey = IssuerPublicKeyDer}
|
||||
}} = public_key:der_decode('Certificate', IssuerDer),
|
||||
IssuerDNHash = crypto:hash(sha, public_key:der_encode('Name', Issuer)),
|
||||
IssuerPKHash = crypto:hash(sha, IssuerPublicKeyDer),
|
||||
Req = #'OCSPRequest'{
|
||||
tbsRequest =
|
||||
#'TBSRequest'{
|
||||
version = 0,
|
||||
requestList =
|
||||
[#'Request'{
|
||||
reqCert =
|
||||
#'CertID'{
|
||||
hashAlgorithm =
|
||||
#'AlgorithmIdentifier'{
|
||||
algorithm = ?'id-sha1',
|
||||
%% ???
|
||||
parameters = <<5, 0>>
|
||||
},
|
||||
issuerNameHash = IssuerDNHash,
|
||||
issuerKeyHash = IssuerPKHash,
|
||||
serialNumber = SerialNumber
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
ReqDer = public_key:der_encode('OCSPRequest', Req),
|
||||
base64:encode_to_string(ReqDer).
|
||||
|
||||
to_bin(Str) when is_list(Str) -> list_to_binary(Str);
|
||||
to_bin(Bin) when is_binary(Bin) -> Bin.
|
|
@ -31,6 +31,84 @@ init_per_suite(Config) ->
|
|||
end_per_suite(_Config) ->
|
||||
emqx_ct_helpers:stop_apps([]).
|
||||
|
||||
t_compile_clientid_common_name_alias_placeholders(_Config) ->
|
||||
Rule1 = {allow, all, pubsub, <<"%cida">>},
|
||||
?assertEqual(
|
||||
{allow, all, pubsub, [{pattern,[<<"%cida">>]}]},
|
||||
emqx_access_rule:compile(Rule1)),
|
||||
|
||||
Rule2 = {allow, all, pubsub, <<"%cna">>},
|
||||
?assertEqual(
|
||||
{allow, all, pubsub, [{pattern,[<<"%cna">>]}]},
|
||||
emqx_access_rule:compile(Rule2)),
|
||||
|
||||
ok.
|
||||
|
||||
t_match_clientid_common_name_alias_placeholders(_Config) ->
|
||||
ClientInfo1 = #{clientid => <<"something-123456789">>,
|
||||
cn => <<"another-987654321">>,
|
||||
clientid_alias => <<"123456789">>,
|
||||
common_name_alias => <<"987654321">>
|
||||
},
|
||||
|
||||
Rule1 = {allow, all, pubsub, <<"t/%cida">>},
|
||||
Compiled1 = emqx_access_rule:compile(Rule1),
|
||||
?assertEqual({matched, allow},
|
||||
emqx_access_rule:match(
|
||||
ClientInfo1,
|
||||
<<"t/123456789">>,
|
||||
Compiled1)),
|
||||
?assertEqual(nomatch,
|
||||
emqx_access_rule:match(
|
||||
ClientInfo1,
|
||||
<<"t/987654321">>,
|
||||
Compiled1)),
|
||||
?assertEqual(nomatch,
|
||||
emqx_access_rule:match(
|
||||
ClientInfo1,
|
||||
<<"123456789">>,
|
||||
Compiled1)),
|
||||
?assertEqual(nomatch,
|
||||
emqx_access_rule:match(
|
||||
ClientInfo1,
|
||||
<<"t/something-123456789">>,
|
||||
Compiled1)),
|
||||
?assertEqual(nomatch,
|
||||
emqx_access_rule:match(
|
||||
ClientInfo1,
|
||||
<<"t/%cida">>,
|
||||
Compiled1)),
|
||||
|
||||
Rule2 = {allow, all, pubsub, <<"t/%cna">>},
|
||||
Compiled2 = emqx_access_rule:compile(Rule2),
|
||||
?assertEqual(nomatch,
|
||||
emqx_access_rule:match(
|
||||
ClientInfo1,
|
||||
<<"t/123456789">>,
|
||||
Compiled2)),
|
||||
?assertEqual({matched, allow},
|
||||
emqx_access_rule:match(
|
||||
ClientInfo1,
|
||||
<<"t/987654321">>,
|
||||
Compiled2)),
|
||||
?assertEqual(nomatch,
|
||||
emqx_access_rule:match(
|
||||
ClientInfo1,
|
||||
<<"987654321">>,
|
||||
Compiled2)),
|
||||
?assertEqual(nomatch,
|
||||
emqx_access_rule:match(
|
||||
ClientInfo1,
|
||||
<<"t/another-987654321">>,
|
||||
Compiled2)),
|
||||
?assertEqual(nomatch,
|
||||
emqx_access_rule:match(
|
||||
ClientInfo1,
|
||||
<<"t/%cida">>,
|
||||
Compiled2)),
|
||||
|
||||
ok.
|
||||
|
||||
t_compile(_) ->
|
||||
Rule1 = {allow, all, pubsub, <<"%u">>},
|
||||
Compile1 = {allow, all, pubsub, [{pattern,[<<"%u">>]}]},
|
||||
|
|
|
@ -0,0 +1,400 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2022 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-module(emqx_crl_cache_SUITE).
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
-include_lib("snabbkaffe/include/snabbkaffe.hrl").
|
||||
|
||||
%% from ssl_manager.erl
|
||||
-record(state, {
|
||||
session_cache_client,
|
||||
session_cache_client_cb,
|
||||
session_lifetime,
|
||||
certificate_db,
|
||||
session_validation_timer,
|
||||
session_cache_client_max,
|
||||
session_client_invalidator,
|
||||
options,
|
||||
client_session_order
|
||||
}).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% CT boilerplate
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
all() ->
|
||||
emqx_ct:all(?MODULE).
|
||||
|
||||
init_per_testcase(TestCase, Config)
|
||||
when TestCase =:= t_empty_cache;
|
||||
TestCase =:= t_filled_cache;
|
||||
TestCase =:= t_revoked ->
|
||||
DataDir = ?config(data_dir, Config),
|
||||
CRLFile = filename:join([DataDir, "crl.pem"]),
|
||||
{ok, CRLPem} = file:read_file(CRLFile),
|
||||
[{'CertificateList', CRLDer, not_encrypted}] = public_key:pem_decode(CRLPem),
|
||||
ServerPid = start_crl_server(CRLPem),
|
||||
IsCached = lists:member(TestCase, [t_filled_cache, t_revoked]),
|
||||
ok = setup_crl_options(Config, #{is_cached => IsCached}),
|
||||
[ {crl_pem, CRLPem}
|
||||
, {crl_der, CRLDer}
|
||||
, {http_server, ServerPid}
|
||||
| Config];
|
||||
init_per_testcase(t_not_cached_and_unreachable, Config) ->
|
||||
DataDir = ?config(data_dir, Config),
|
||||
CRLFile = filename:join([DataDir, "crl.pem"]),
|
||||
{ok, CRLPem} = file:read_file(CRLFile),
|
||||
[{'CertificateList', CRLDer, not_encrypted}] = public_key:pem_decode(CRLPem),
|
||||
application:stop(cowboy),
|
||||
ok = setup_crl_options(Config, #{is_cached => false}),
|
||||
[ {crl_pem, CRLPem}
|
||||
, {crl_der, CRLDer}
|
||||
| Config];
|
||||
init_per_testcase(_TestCase, Config) ->
|
||||
DataDir = ?config(data_dir, Config),
|
||||
CRLFile = filename:join([DataDir, "crl.pem"]),
|
||||
{ok, CRLPem} = file:read_file(CRLFile),
|
||||
[{'CertificateList', CRLDer, not_encrypted}] = public_key:pem_decode(CRLPem),
|
||||
TestPid = self(),
|
||||
ok = meck:new(emqx_crl_cache, [non_strict, passthrough, no_history, no_link]),
|
||||
meck:expect(emqx_crl_cache, http_get,
|
||||
fun(URL, _HTTPTimeout) ->
|
||||
TestPid ! {http_get, URL},
|
||||
{ok, {{"HTTP/1.0", 200, 'OK'}, [], CRLPem}}
|
||||
end),
|
||||
[ {crl_pem, CRLPem}
|
||||
, {crl_der, CRLDer}
|
||||
| Config].
|
||||
|
||||
end_per_testcase(TestCase, Config)
|
||||
when TestCase =:= t_empty_cache;
|
||||
TestCase =:= t_filled_cache;
|
||||
TestCase =:= t_revoked ->
|
||||
ServerPid = ?config(http_server, Config),
|
||||
emqx_crl_cache_http_server:stop(ServerPid),
|
||||
emqx_ct_helpers:stop_apps([]),
|
||||
application:set_env(emqx, crl_cache_urls, []),
|
||||
application:stop(cowboy),
|
||||
clear_crl_cache(),
|
||||
ok;
|
||||
end_per_testcase(t_not_cached_and_unreachable, _Config) ->
|
||||
emqx_ct_helpers:stop_apps([]),
|
||||
application:set_env(emqx, crl_cache_urls, []),
|
||||
clear_crl_cache(),
|
||||
ok;
|
||||
end_per_testcase(_TestCase, _Config) ->
|
||||
meck:unload([emqx_crl_cache]),
|
||||
clear_crl_cache(),
|
||||
ok.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Helper functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
assert_http_get(URL) ->
|
||||
receive
|
||||
{http_get, URL} ->
|
||||
ok
|
||||
after
|
||||
1000 ->
|
||||
error({should_have_requested, URL})
|
||||
end.
|
||||
|
||||
get_crl_cache_table() ->
|
||||
#state{certificate_db = [_, _, _, {Ref, _}]} = sys:get_state(ssl_manager),
|
||||
Ref.
|
||||
|
||||
start_crl_server(Port, CRLPem) ->
|
||||
{ok, LSock} = gen_tcp:listen(Port, [binary, {active, true}, reusedaddr]),
|
||||
spawn_link(fun() -> accept_loop(LSock, CRLPem) end),
|
||||
ok.
|
||||
|
||||
accept_loop(LSock, CRLPem) ->
|
||||
case gen_tcp:accept(LSock) of
|
||||
{ok, Sock} ->
|
||||
Worker = spawn_link(fun() -> crl_loop(Sock, CRLPem) end),
|
||||
gen_tcp:controlling_process(Sock, Worker),
|
||||
accept_loop(LSock, CRLPem);
|
||||
{error, Reason} ->
|
||||
error({accept_error, Reason})
|
||||
end.
|
||||
|
||||
crl_loop(Sock, CRLPem) ->
|
||||
receive
|
||||
{tcp, Sock, _Data} ->
|
||||
gen_tcp:send(Sock, CRLPem),
|
||||
crl_loop(Sock, CRLPem);
|
||||
_Msg ->
|
||||
ok
|
||||
end.
|
||||
|
||||
drain_msgs() ->
|
||||
receive
|
||||
_Msg ->
|
||||
drain_msgs()
|
||||
after
|
||||
0 ->
|
||||
ok
|
||||
end.
|
||||
|
||||
clear_crl_cache() ->
|
||||
%% reset the CRL cache
|
||||
exit(whereis(ssl_manager), kill),
|
||||
ok.
|
||||
|
||||
force_cacertfile(Cacertfile) ->
|
||||
{SSLListeners0, OtherListeners} = lists:partition(
|
||||
fun(#{proto := Proto}) -> Proto =:= ssl end,
|
||||
emqx:get_env(listeners)),
|
||||
SSLListeners =
|
||||
lists:map(
|
||||
fun(Listener = #{opts := Opts0}) ->
|
||||
SSLOpts0 = proplists:get_value(ssl_options, Opts0),
|
||||
%% it injects some garbage...
|
||||
SSLOpts1 = lists:keydelete(cacertfile, 1, lists:keydelete(cacertfile, 1, SSLOpts0)),
|
||||
SSLOpts2 = [{cacertfile, Cacertfile} | SSLOpts1],
|
||||
Opts1 = lists:keyreplace(ssl_options, 1, Opts0, {ssl_options, SSLOpts2}),
|
||||
Listener#{opts => Opts1}
|
||||
end,
|
||||
SSLListeners0),
|
||||
application:set_env(emqx, listeners, SSLListeners ++ OtherListeners),
|
||||
ok.
|
||||
|
||||
setup_crl_options(Config, #{is_cached := IsCached}) ->
|
||||
DataDir = ?config(data_dir, Config),
|
||||
Cacertfile = filename:join(DataDir, "ca-chain.cert.pem"),
|
||||
Certfile = filename:join(DataDir, "server.cert.pem"),
|
||||
Keyfile = filename:join(DataDir, "server.key.pem"),
|
||||
URLs = case IsCached of
|
||||
false -> [];
|
||||
true -> ["http://localhost:9878/intermediate.crl.pem"]
|
||||
end,
|
||||
Handler =
|
||||
fun(emqx) ->
|
||||
application:set_env(emqx, crl_cache_urls, URLs),
|
||||
emqx_ct_helpers:change_emqx_opts(
|
||||
ssl_twoway, [{ssl_options, [ {certfile, Certfile}
|
||||
, {keyfile, Keyfile}
|
||||
, {verify, verify_peer}
|
||||
%% {crl_check, true} does not work; probably bug in OTP
|
||||
, {crl_check, peer}
|
||||
, {crl_cache,
|
||||
{ssl_crl_cache, {internal, [{http, timer:seconds(15)}]}}}
|
||||
]}]),
|
||||
%% emqx_ct_helpers:change_emqx_opts has cacertfile hardcoded....
|
||||
ok = force_cacertfile(Cacertfile),
|
||||
ok;
|
||||
(_) ->
|
||||
ok
|
||||
end,
|
||||
emqx_ct_helpers:start_apps([], Handler),
|
||||
case IsCached of
|
||||
true ->
|
||||
%% wait the cache to be filled
|
||||
receive
|
||||
http_get -> ok
|
||||
after
|
||||
1_000 -> error(crl_cache_not_filled)
|
||||
end;
|
||||
false ->
|
||||
%% ensure cache is empty
|
||||
clear_crl_cache(),
|
||||
ct:sleep(200),
|
||||
ok
|
||||
end,
|
||||
drain_msgs(),
|
||||
ok.
|
||||
|
||||
start_crl_server(CRLPem) ->
|
||||
application:ensure_all_started(cowboy),
|
||||
{ok, ServerPid} = emqx_crl_cache_http_server:start_link(self(), 9878, CRLPem, []),
|
||||
receive
|
||||
{ServerPid, ready} -> ok
|
||||
after
|
||||
1000 -> error(timeout_starting_http_server)
|
||||
end,
|
||||
ServerPid.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Test cases
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
t_init_empty_urls(_Config) ->
|
||||
Ref = get_crl_cache_table(),
|
||||
?assertEqual([], ets:tab2list(Ref)),
|
||||
?assertMatch({ok, _}, emqx_crl_cache:start_link()),
|
||||
receive
|
||||
{http_get, _} ->
|
||||
error(should_not_make_http_request)
|
||||
after
|
||||
1000 -> ok
|
||||
end,
|
||||
?assertEqual([], ets:tab2list(Ref)),
|
||||
ok.
|
||||
|
||||
t_init_refresh(Config) ->
|
||||
CRLDer = ?config(crl_der, Config),
|
||||
Ref = get_crl_cache_table(),
|
||||
?assertEqual([], ets:tab2list(Ref)),
|
||||
URL1 = "http://localhost/crl1.pem",
|
||||
URL2 = "http://localhost/crl2.pem",
|
||||
Opts = #{ urls => [URL1, URL2]
|
||||
, refresh_interval => timer:minutes(15)
|
||||
},
|
||||
ok = snabbkaffe:start_trace(),
|
||||
{ok, SubRef} = snabbkaffe:subscribe(
|
||||
fun(#{?snk_kind := Kind}) ->
|
||||
Kind =:= crl_cache_insert
|
||||
end,
|
||||
_NEvents = 2,
|
||||
_Timeout = 2_000),
|
||||
?assertMatch({ok, _}, emqx_crl_cache:start_link(Opts)),
|
||||
lists:foreach(fun assert_http_get/1, [URL1, URL2]),
|
||||
{ok, _} = snabbkaffe:receive_events(SubRef),
|
||||
snabbkaffe:stop(),
|
||||
?assertEqual(
|
||||
[{"crl1.pem", [CRLDer]}, {"crl2.pem", [CRLDer]}],
|
||||
lists:sort(ets:tab2list(Ref))),
|
||||
ok.
|
||||
|
||||
t_manual_refresh(Config) ->
|
||||
CRLDer = ?config(crl_der, Config),
|
||||
Ref = get_crl_cache_table(),
|
||||
?assertEqual([], ets:tab2list(Ref)),
|
||||
{ok, _} = emqx_crl_cache:start_link(),
|
||||
URL = "http://localhost/crl.pem",
|
||||
?assertEqual({ok, [CRLDer]}, emqx_crl_cache:refresh(URL)),
|
||||
?assertEqual(
|
||||
[{"crl.pem", [CRLDer]}],
|
||||
ets:tab2list(Ref)),
|
||||
ok.
|
||||
|
||||
t_refresh_request_error(_Config) ->
|
||||
meck:expect(emqx_crl_cache, http_get,
|
||||
fun(_URL, _HTTPTimeout) ->
|
||||
{ok, {{"HTTP/1.0", 404, 'Not Found'}, [], <<"not found">>}}
|
||||
end),
|
||||
{ok, _} = emqx_crl_cache:start_link(),
|
||||
URL = "http://localhost/crl.pem",
|
||||
?assertEqual(error, emqx_crl_cache:refresh(URL)),
|
||||
ok.
|
||||
|
||||
t_refresh_invalid_response(_Config) ->
|
||||
meck:expect(emqx_crl_cache, http_get,
|
||||
fun(_URL, _HTTPTimeout) ->
|
||||
{ok, {{"HTTP/1.0", 200, 'OK'}, [], <<"not a crl">>}}
|
||||
end),
|
||||
{ok, _} = emqx_crl_cache:start_link(),
|
||||
URL = "http://localhost/crl.pem",
|
||||
?assertEqual({ok, []}, emqx_crl_cache:refresh(URL)),
|
||||
ok.
|
||||
|
||||
t_refresh_http_error(_Config) ->
|
||||
meck:expect(emqx_crl_cache, http_get,
|
||||
fun(_URL, _HTTPTimeout) ->
|
||||
{error, timeout}
|
||||
end),
|
||||
{ok, _} = emqx_crl_cache:start_link(),
|
||||
URL = "http://localhost/crl.pem",
|
||||
?assertEqual(error, emqx_crl_cache:refresh(URL)),
|
||||
ok.
|
||||
|
||||
t_unknown_messages(_Config) ->
|
||||
{ok, Server} = emqx_crl_cache:start_link(),
|
||||
gen_server:call(Server, foo),
|
||||
gen_server:cast(Server, foo),
|
||||
Server ! foo,
|
||||
ok.
|
||||
|
||||
t_evict(_Config) ->
|
||||
{ok, _} = emqx_crl_cache:start_link(),
|
||||
URL = "http://localhost/crl.pem",
|
||||
{ok, [_]} = emqx_crl_cache:refresh(URL),
|
||||
Ref = get_crl_cache_table(),
|
||||
?assertMatch([{"crl.pem", _}], ets:tab2list(Ref)),
|
||||
snabbkaffe:start_trace(),
|
||||
{ok, {ok, _}} = ?wait_async_action(emqx_crl_cache:evict(URL),
|
||||
#{?snk_kind := crl_cache_evict}),
|
||||
snabbkaffe:stop(),
|
||||
?assertEqual([], ets:tab2list(Ref)),
|
||||
ok.
|
||||
|
||||
%% check that the URL in the certificate is checked on the fly if the
|
||||
%% cache is empty.
|
||||
t_empty_cache(Config) ->
|
||||
DataDir = ?config(data_dir, Config),
|
||||
ClientCert = filename:join(DataDir, "client.cert.pem"),
|
||||
ClientKey = filename:join(DataDir, "client.key.pem"),
|
||||
{ok, C} = emqtt:start_link([ {ssl, true}
|
||||
, {ssl_opts, [ {certfile, ClientCert}
|
||||
, {keyfile, ClientKey}
|
||||
]}
|
||||
, {port, 8883}
|
||||
]),
|
||||
{ok, _} = emqtt:connect(C),
|
||||
receive
|
||||
http_get -> ok
|
||||
after
|
||||
2_000 -> error(should_have_checked_server)
|
||||
end,
|
||||
emqtt:disconnect(C),
|
||||
ok.
|
||||
|
||||
%% check that the URL in the certificate is *not* checked if the cache
|
||||
%% contains that URL.
|
||||
t_filled_cache(Config) ->
|
||||
DataDir = ?config(data_dir, Config),
|
||||
ClientCert = filename:join(DataDir, "client.cert.pem"),
|
||||
ClientKey = filename:join(DataDir, "client.key.pem"),
|
||||
{ok, C} = emqtt:start_link([ {ssl, true}
|
||||
, {ssl_opts, [ {certfile, ClientCert}
|
||||
, {keyfile, ClientKey}
|
||||
]}
|
||||
, {port, 8883}
|
||||
]),
|
||||
{ok, _} = emqtt:connect(C),
|
||||
receive
|
||||
http_get -> error(should_have_used_cache)
|
||||
after
|
||||
2_000 -> ok
|
||||
end,
|
||||
emqtt:disconnect(C),
|
||||
ok.
|
||||
|
||||
%% If the CRL is not cached when the client tries to connect and the
|
||||
%% CRL server is unreachable, the client will be denied connection.
|
||||
t_not_cached_and_unreachable(Config) ->
|
||||
DataDir = ?config(data_dir, Config),
|
||||
ClientCert = filename:join(DataDir, "client.cert.pem"),
|
||||
ClientKey = filename:join(DataDir, "client.key.pem"),
|
||||
{ok, C} = emqtt:start_link([ {ssl, true}
|
||||
, {ssl_opts, [ {certfile, ClientCert}
|
||||
, {keyfile, ClientKey}
|
||||
]}
|
||||
, {port, 8883}
|
||||
]),
|
||||
Ref = get_crl_cache_table(),
|
||||
?assertEqual([], ets:tab2list(Ref)),
|
||||
process_flag(trap_exit, true),
|
||||
?assertMatch({error, {{shutdown, {tls_alert, {bad_certificate, _}}}, _}}, emqtt:connect(C)),
|
||||
ok.
|
||||
|
||||
t_revoked(Config) ->
|
||||
DataDir = ?config(data_dir, Config),
|
||||
ClientCert = filename:join(DataDir, "client-revoked.cert.pem"),
|
||||
ClientKey = filename:join(DataDir, "client-revoked.key.pem"),
|
||||
{ok, C} = emqtt:start_link([ {ssl, true}
|
||||
, {ssl_opts, [ {certfile, ClientCert}
|
||||
, {keyfile, ClientKey}
|
||||
]}
|
||||
, {port, 8883}
|
||||
]),
|
||||
process_flag(trap_exit, true),
|
||||
?assertMatch({error, {{shutdown, {tls_alert, {certificate_revoked, _}}}, _}}, emqtt:connect(C)),
|
||||
ok.
|
|
@ -0,0 +1,68 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIF+zCCA+OgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwbzELMAkGA1UEBhMCU0Ux
|
||||
EjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJU3RvY2tob2xtMRIwEAYDVQQK
|
||||
DAlNeU9yZ05hbWUxETAPBgNVBAsMCE15Um9vdENBMREwDwYDVQQDDAhNeVJvb3RD
|
||||
QTAeFw0yMjA2MTMxMjQyMDVaFw0zMjA2MTAxMjQyMDVaMGsxCzAJBgNVBAYTAlNF
|
||||
MRIwEAYDVQQIDAlTdG9ja2hvbG0xEjAQBgNVBAoMCU15T3JnTmFtZTEZMBcGA1UE
|
||||
CwwQTXlJbnRlcm1lZGlhdGVDQTEZMBcGA1UEAwwQTXlJbnRlcm1lZGlhdGVDQTCC
|
||||
AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAN32wUPOGrLjVHY37ICI4sWH
|
||||
2GBEYgGtoKyZTfLKqK8W965uxHxMKKeKC7Ro93ScT4giR2GdsCvMyLP4Dlv7FxL7
|
||||
rWEHc7vbP22uT4NQQ0hgfl4ch8rTiSl/5FfynOuvMHnKh58z+lGyQ/uqKqAzc6OD
|
||||
6FypzSQO2R6JpA+kxKlbTYmOcsLiLFKjCjxyA1ibeozUaRHjPRM7VLHVxYfmpGm/
|
||||
86NJxtHrw4hwoKIM9bfURboxfn9R1YM14mZYA6Uw2pScS1+j79tNy74NQNYu1t9E
|
||||
cyPQk+AJdp2BsbR5KPYr1SMVLHlTwnzjk1IVW4wUUdX2h56ygmRNo9ui74ODfyud
|
||||
4cclg45SeRdOT0w5e3g20ZvfLZpIkhXk19EIIU/YbG6GpL8gLvBHkz6vvidE7L/2
|
||||
h2//alJBeWCvyOloIWYNYnwnHGeXR2c5pzNxHipkBSuMeBaLJOO7X9oqKVanu+xq
|
||||
nVagFhEYnd+T0PsPa5IVA73KiWMWWeFgJI0pRUydyp0/FhXEhkMWZNJHiscbxcdn
|
||||
hTdNCAbMfV/4fMar+d/QKY/GMWUVQ4OlXUoo3WjjRi4T8NJEjZGfLdKw5x81WM4A
|
||||
yqDV3OVVCBf1XrHH4IbvbUDgeG1OEGSV9pdvKX4Sm7226vdOc3HPfRnVyf/N1Pv7
|
||||
lzPbUlCheKbTW6Oeq97VAgMBAAGjgaQwgaEwHQYDVR0OBBYEFCuv1TkzC1fSgTfz
|
||||
E1m1u5pRCJsVMB8GA1UdIwQYMBaAFErA8sZMX6obhoo3XvUpaTy6Z4uhMBIGA1Ud
|
||||
EwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMDsGA1UdHwQ0MDIwMKAuoCyG
|
||||
Kmh0dHA6Ly9sb2NhbGhvc3Q6OTg3OC9pbnRlcm1lZGlhdGUuY3JsLnBlbTANBgkq
|
||||
hkiG9w0BAQsFAAOCAgEAtn/u/ZfmioZyh5DNETRNAftXWvxOyi2MK8soNEsaSbmq
|
||||
2ajkQwJ1MZ0+C5HuzsoEEoqStYtD3JG34ydPzQbMPwkTDg48+guu8ji30jYXGIfI
|
||||
RQQfseEj1hN8wTLEDAJGl17kJA+js6dcHkJp93qocOCoOwa5MAYB8ZZq/uRJlzVt
|
||||
ol6dvhBvhoxkKvJfrhg5dNISVBgIXrs5YOMyXqh6W3YMmepNjs05e5bcLYADyHCd
|
||||
f4TK9pgyys4OIVHiRZo0+hlaChKo4vDK7acgZOds7qxS/sxrwKe49FTIrAWWP6fG
|
||||
Ij2RHF91fLhi+10oVVSWtCyWRJOaSM4cenbLN36OUg1JswacsqojTCUylMAa8sAB
|
||||
RggZ+tt8LlARj3/pdz6IWrccabC+AGZQa1kOKl97hjsE0qy9V5WOiueJ+78u+BY5
|
||||
NXIoIyuPG0WCItb76jyn7UDjiCsJt7rfJ5t5rRVLpm8YRG43KuMWjXih/bT07YdE
|
||||
tA3X5Bk/XLQBqQbRKpR5+CKFhvXbNmKnuAdFbiG8UTFQdo/HGyBR7zzkF4vTqu8s
|
||||
2pMl4xFAMNnGsWQYce6YioQfYL3apgx9PIqgHr+IzXae6/NIpoIs9ShymEZP1jT5
|
||||
9DqKzmUQz3czc7RNXLUZbWxoWRtivrDJGPgbSHyMqVu4h2yLINftHSGegC+ZEgc=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFzzCCA7egAwIBAgIUVYtdlrTMH1BoMy9JpiiL/FXv5NMwDQYJKoZIhvcNAQEL
|
||||
BQAwbzELMAkGA1UEBhMCU0UxEjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJ
|
||||
U3RvY2tob2xtMRIwEAYDVQQKDAlNeU9yZ05hbWUxETAPBgNVBAsMCE15Um9vdENB
|
||||
MREwDwYDVQQDDAhNeVJvb3RDQTAeFw0yMjA2MTMxMjQyMDVaFw00MjA2MDgxMjQy
|
||||
MDVaMG8xCzAJBgNVBAYTAlNFMRIwEAYDVQQIDAlTdG9ja2hvbG0xEjAQBgNVBAcM
|
||||
CVN0b2NraG9sbTESMBAGA1UECgwJTXlPcmdOYW1lMREwDwYDVQQLDAhNeVJvb3RD
|
||||
QTERMA8GA1UEAwwITXlSb290Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
|
||||
AoICAQDRKeWdrXluKLuVxTXxpWbCzBm/6x+kg8EjhIKUm9Mq0t+cOLC1sn99s4E0
|
||||
/NBA7jc3O+hQ9S2fZuMp9OUXuf8HjjtlKQY2M9T9kCE9Yxc+ygpcPEvDYl0ke/1r
|
||||
m6eUOGS2LkIDHebuYbY+KGXjpaF15w49q199wQAk0sbdcCiv+OymBUxi3lrHepo3
|
||||
EBIjHtTwNtehgeiJfxCe8TLlLpbPdCsMNwlPvNa0E+1Ol1P9DfHPfK1Dt0ua8X5q
|
||||
ZBhnW1cawrwzrnth2ZtrSw7RaWhtNWFt/SdedGPgVUMIgFY2I7f2wT2hoe8R/D+e
|
||||
XLDzVMAW+yrlu/LlBaHE2Ffkk2La8mSHwJOvCZ/Q0V1mJnj6F4pfNemY4MnFX6Jm
|
||||
7u2TSWooowMppdNPCCRlGk+XS8lfmb9N/0RsOMSz5+1u8wOfFI6Z1GaH6K2Ne2xf
|
||||
VIBjhGRAiTCHDIFNYBCeAxR64zYeGFKv8L19oV6cbA6St7GMikfk5U/fyG9SUK88
|
||||
Fvjm+MrNDu/EEyjkKhK6I9ao3Pb4LcCofTcu44RBYT0HhkjqGWQgapm0i80NcRlL
|
||||
yK7wm5C8Tntmbehg1KE8iqSzAcBZu6L+j4qQ90T+AIHNTmRUa9dH2uhEej4hIhjd
|
||||
yJSJcDm4/CYEuK6WgZ6EuJadRy4QgPiH7zWCFXp/OGLvFmUEWQIDAQABo2MwYTAd
|
||||
BgNVHQ4EFgQUSsDyxkxfqhuGijde9SlpPLpni6EwHwYDVR0jBBgwFoAUSsDyxkxf
|
||||
qhuGijde9SlpPLpni6EwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYw
|
||||
DQYJKoZIhvcNAQELBQADggIBAJQSy3WpS/j8emep2gJXB7jYxolhhN78DiJaqUZh
|
||||
0YXa19YaghAsk5YVqbe2XLAGyFqAmmGQ6ymCbULqkdSNUTLTvwHYGFYH7NizUaCS
|
||||
r9XaB9lizr7dwpd/8HwpqMq7rxNXLMNfl3iVywiKkah6r3goNwz9xgclOj1Q2Uly
|
||||
s5S3BaNnf8Q9ypygbUMOalYN6KzHloj52inX3fpGfOov0O/1+bkp/3SvATU0aRpB
|
||||
4bKLt99b69x1HSIkCNxeJQz2klgEMNQYGsnXOYCvnI0cOpGGDxQuIYiZbYbTJb7X
|
||||
FI7FfV0ryn55mMLgZboGezMhWGYeHpWfa7H0La1ZjgedBsu3HH5VaPuHOngFNa+X
|
||||
78vbWFcD09biZJwatwZlRjFGItaPSyhqSWJx1IqBZ98ZG9ziOVQ+kq1+uRecI/3S
|
||||
jNw8LFDnOH20UxJnhRddA7f7cFWkk3WRIIec+wvE7uOibHDwFmqCo6JeOhkZDF5f
|
||||
R1dfN4CZznMzbUb5iqUb4JwGdViijCaDHTEe3C3nKS1mPxt2kn3H1C37y71Fh5yu
|
||||
mStVxjZZTdbaP8WPEAEDhK+7eB6pAKa+ERXOlWY1L5nNkh4ahIw5837yIY60oyBh
|
||||
goiqX/Vy0wEJMa7HgXT3cDnW4NiXQA8nVKsRUiZco136YAATS89iLk3ibYJxzGP+
|
||||
G2LZ
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,32 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIFnDCCA4SgAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCU0Ux
|
||||
EjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UECgwJTXlPcmdOYW1lMRkwFwYDVQQL
|
||||
DBBNeUludGVybWVkaWF0ZUNBMRkwFwYDVQQDDBBNeUludGVybWVkaWF0ZUNBMB4X
|
||||
DTIyMDYxMzEyNDIwNVoXDTIzMDYyMzEyNDIwNVowfTELMAkGA1UEBhMCU0UxEjAQ
|
||||
BgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJU3RvY2tob2xtMRIwEAYDVQQKDAlN
|
||||
eU9yZ05hbWUxGTAXBgNVBAsMEE15SW50ZXJtZWRpYXRlQ0ExFzAVBgNVBAMMDmNs
|
||||
aWVudC1yZXZva2VkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA584w
|
||||
VAczDWrlIJXm6+0oYepacPPrVEMC9WwsQFO5GbBDdxrBvxgQ5u8/DDNEtYk0sJMt
|
||||
zgSLxsYK5duhrwVyICXpKgxMI2fKdKP0zxB/eB4V0vrc6FqR4L8D8XoNPKSaTnjv
|
||||
NNh3wjLNvmBRfHRSUCe4zEvPZzMLuBIHXRR20prtA90FFV8fliNvMCBIbFkqthjf
|
||||
fQ/tSXXxNQNjacuHVfY+LVN3xu9Jjll4AaCKKz19rDexq9HTLLZ8y4jBD1eRobp+
|
||||
spKKu4HNpon+YMp3vJuNmxsTU+xBkbESWGJTFot7lZL1PVBvxdgbSd3OrPKI+QbK
|
||||
06FN3iBrcW3Yjp04LQIDAQABo4IBNjCCATIwCQYDVR0TBAIwADARBglghkgBhvhC
|
||||
AQEEBAMCBaAwMwYJYIZIAYb4QgENBCYWJE9wZW5TU0wgR2VuZXJhdGVkIENsaWVu
|
||||
dCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUSq+9djRFuryFk0Mdqlsy0chljWswHwYD
|
||||
VR0jBBgwFoAUK6/VOTMLV9KBN/MTWbW7mlEImxUwDgYDVR0PAQH/BAQDAgXgMB0G
|
||||
A1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDBDA7BgNVHR8ENDAyMDCgLqAshipo
|
||||
dHRwOi8vbG9jYWxob3N0Ojk4NzgvaW50ZXJtZWRpYXRlLmNybC5wZW0wMQYIKwYB
|
||||
BQUHAQEEJTAjMCEGCCsGAQUFBzABhhVodHRwOi8vbG9jYWxob3N0Ojk4NzcwDQYJ
|
||||
KoZIhvcNAQELBQADggIBAD4XyEjAfU0VE+YRQXUxlqpeJdMij0Io9ZCf1j6JMNFl
|
||||
/p8vbOm6orK55bYWXCzRaIEkgOGlpQLDIXpViAjmBbXisA97hS1v6rW10W6LyNkN
|
||||
i7LC6kTgBi4yIV3FiQk5CIE3Tj22+0GjaepHc2bPGaLSRBaoe8uBvDlDqRjxW64H
|
||||
KYOIyux3WqauEziNEVklXG3VNX+6WfUw0jP9p4cglLaL43htwdavA1g7RP0wE1/6
|
||||
hvZ242jK4bvCGn/p7IDa8YtgXTufjQf6hJB9kRAJRqrSJQnihb6P9UZr2saaR7mp
|
||||
28w7a6L1RLSkCSpcwRoTHgTMkFwCD/h7NxZmpoOCBk4vrHY7SXG05ptb6o7x8TD7
|
||||
lRV/+ay8EIPWCKGpTrGWHGzjxiuXw4TqnfETlvz0rFUG4TapXwmWnaSwTUk5d10v
|
||||
olBQJ22CIidkKvoW/6bmD6mtyRX/F3KDTN7vHSsjvaty+TzqjWLLd2rjWpZi5xcc
|
||||
h/lOyWHXbFkakRT879USUvqU/Y6+2CpbZ2ssks4+bnD8Dsdq1fFLTXtLhBBFcz11
|
||||
amuwx923tJTY9f6e7y3X/TveCKcibo+aluA4ACkYix8mR/oFxmsulW6MTVcqZZ+i
|
||||
9+oo9hyOPQsJhWYISAtLBuDCqz9fKM4llmnuQZA55FuZBkSmHBRAXw5xwA+gbZcs
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEA584wVAczDWrlIJXm6+0oYepacPPrVEMC9WwsQFO5GbBDdxrB
|
||||
vxgQ5u8/DDNEtYk0sJMtzgSLxsYK5duhrwVyICXpKgxMI2fKdKP0zxB/eB4V0vrc
|
||||
6FqR4L8D8XoNPKSaTnjvNNh3wjLNvmBRfHRSUCe4zEvPZzMLuBIHXRR20prtA90F
|
||||
FV8fliNvMCBIbFkqthjffQ/tSXXxNQNjacuHVfY+LVN3xu9Jjll4AaCKKz19rDex
|
||||
q9HTLLZ8y4jBD1eRobp+spKKu4HNpon+YMp3vJuNmxsTU+xBkbESWGJTFot7lZL1
|
||||
PVBvxdgbSd3OrPKI+QbK06FN3iBrcW3Yjp04LQIDAQABAoIBAQDN52MaYMLCel9I
|
||||
0J6slp62SxtHFgPFdzjbk9jC0xuqa92hoIzVF6V73KxeQ/QWZOf+qN2ZEISwbh4k
|
||||
CzHVa7ryP3qbtQy0rm8xqKm+fGMd6WttWxR6+Gh4AHSaPNYhNf0zE0033ciTIdmL
|
||||
77ayHAk51e7a2cRDYR5ZxPnxfkoFy5M2y9U7daAZsEjIyoxjGmkO92YU4byPfUxA
|
||||
vxC+rcSCUHA1OSoKONuGmwAYrLyHHoIgmVSXe6qSjgSWBPFVLp6OKTiBc7klRnlw
|
||||
EAcoop7NSit+eMCqQM85Tp8wMmKd+9jfoek9yc5ahWgLDMsd5Dlc/+CXoChmQ5dO
|
||||
w0h7vqbpAoGBAPk5HEuZ752VJQOtcSfJacOsYlAc8tlOBl33negGIplAKYeWKib7
|
||||
xtCzdU29oW4tPIJO6v+e7y+GcV7n2+6DpoXTUr6f4fG3vPQ3ZKPctsAHyFpKvzR3
|
||||
137YllShpgQKIcnwC7y/KG+wwgiVjZ+cDWCUuGA9/3m249DdO2b8F+jXAoGBAO4b
|
||||
1KC32rjzOk3QOZWIvWADbC8MxY0jeM+phtycoeyCreSOKq6dLpX4JD5NZ1XP4uol
|
||||
wr0Ta0D8FN7T/JRfT8FnoFXTJxwKTi6oGwXQ3Am/bXsz2A2H8vsXL8tgutYfX4Xi
|
||||
YNf57zNiCTHLC4K9aiS8iJ2UtJIwGi558ONVOPKbAoGBAJcjS0WN1QJ7sDbKuBSo
|
||||
0LsZj4WGCMA/0RyrTden4NOPVaAkMOvzRF7MdhbvKTbnuApOUbUzbVok7hvgAEBl
|
||||
FleSEFwKGbu88Zoo/Z9h+nH6RkZ7jfkDtGv4bTJl1YgdnOAZ8wRD6QHS79jE2V4y
|
||||
BOrNRgMXlhb6Eq5Xe+64cseBAoGBANuLB6tEukQr6AdVRbMNyGbd4QMkyIXRPhRj
|
||||
IDkLpvVWrJV/S/WCcjDPAkP7xJrHulbgiEUjwZHCnE+0sD/x/ay7KofX0Ei3a8zz
|
||||
LS9Ym3nValHdxIj9X9mKUIQ6ZSsG9GGTEG4zQg1jiEzEBZH/qf3DZEe/lBryhUFz
|
||||
J9vEeWSfAoGAPV6RXEP4uJstCBUsWnWXGNtQ8TwS/NsH5FyPuWEXWxJwrUHEDyiA
|
||||
FhRW2tGH/k5cCZw7lvzmZui+iWBv90l1/N3J2+SKZMVZNGsXCYx9szSd5uAer/Zs
|
||||
emH0oOBC6NXXVDTIp+vDzy/lx6Xxcp4n6iiI3I+uCOeI5qRhM0GHcdU=
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1,32 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIFljCCA36gAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCU0Ux
|
||||
EjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UECgwJTXlPcmdOYW1lMRkwFwYDVQQL
|
||||
DBBNeUludGVybWVkaWF0ZUNBMRkwFwYDVQQDDBBNeUludGVybWVkaWF0ZUNBMB4X
|
||||
DTIyMDYxMzEyNDIwNVoXDTIzMDYyMzEyNDIwNVowdzELMAkGA1UEBhMCU0UxEjAQ
|
||||
BgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJU3RvY2tob2xtMRIwEAYDVQQKDAlN
|
||||
eU9yZ05hbWUxGTAXBgNVBAsMEE15SW50ZXJtZWRpYXRlQ0ExETAPBgNVBAMMCE15
|
||||
Q2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtoHIpsxDnO4y
|
||||
TLKghUGkAIg5102fcVj2sQ4kJhybnN7tfbQ0gZo0GE8A7SQAYM/lO0VcDknp/nkz
|
||||
A8Lsc5Lc1SJ7z1Iyd+/ZUg3GJE/ijPoPdMlFU4/HndmoMdLhcflCRZiirnLGwdvT
|
||||
bBrlGD0alK/kOqbSc9LT3oFVXWo2XvAyNtBxU7dHznxT5JuFnPyCZvoY+FvSpCbP
|
||||
5rV0NLzdCpk+6C4KTs3YAaTh6kVvSCch55E7hE0I2KKLjz1Ge563vPEWOnB40p6w
|
||||
ZhzcwFIbiXmq8TpA342HInES1tFRwv7ZGSs+B8t/q9nGWTDf1as4+wBrQ/i35Xav
|
||||
sMuNp+O+mQIDAQABo4IBNjCCATIwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMC
|
||||
BaAwMwYJYIZIAYb4QgENBCYWJE9wZW5TU0wgR2VuZXJhdGVkIENsaWVudCBDZXJ0
|
||||
aWZpY2F0ZTAdBgNVHQ4EFgQUwRox6T4QUt1kUQDCuZ6NJNKGHIgwHwYDVR0jBBgw
|
||||
FoAUK6/VOTMLV9KBN/MTWbW7mlEImxUwDgYDVR0PAQH/BAQDAgXgMB0GA1UdJQQW
|
||||
MBQGCCsGAQUFBwMCBggrBgEFBQcDBDA7BgNVHR8ENDAyMDCgLqAshipodHRwOi8v
|
||||
bG9jYWxob3N0Ojk4NzgvaW50ZXJtZWRpYXRlLmNybC5wZW0wMQYIKwYBBQUHAQEE
|
||||
JTAjMCEGCCsGAQUFBzABhhVodHRwOi8vbG9jYWxob3N0Ojk4NzcwDQYJKoZIhvcN
|
||||
AQELBQADggIBAEFjWfL32GKfOExZ+4FPUj80V282pJ4iEuRSmbti/EVZse0GsKvZ
|
||||
xF1swDdoTrcdNTruHxIKfojwKz28XP6JoM6MUruxQglwdwZGJGlC0KpanR7dvQHa
|
||||
XT/ushugHptobRh3+f47Gbyd0A0MKfGFLGLC1XZokdrvLPoKHCJWq/DRUgHMnn53
|
||||
FrM7a+JXZUpkSU+uqCPTMpkmuZc0E+SPVtLGiH71q1kbMzJKIKSuzkRCG91PW8fT
|
||||
B2PUmy4W6YV6MUSi+jHx5JDLvjwycTsNFBiK0zZtcKsaz9QWi/qnrjkCWR0LXaRZ
|
||||
W99ER7hEMe8J5CYt3lrmtaVL8lZ4TnoQE/Rsr1GBr7+WA9WnB/ijSG/3Q02hyqcK
|
||||
EaFE+aqOvY03UlIyk/m+PFoRwge9vERf2dGgFN4osbczDMwXc67jx1MKyFQLQvg4
|
||||
TcN7fXCbqQeWDH0RCAF4goFGqjq/KZQYHN+BOJ1cubUxbY4+9zuQ7pZ1s1tonchA
|
||||
TQFYbZNXKFUYTrgSSZRoJGGkSQkUKvARsraIdFvOt6qzLq4k86efQAgPDwFd1Kni
|
||||
+iML7b3fIzHAl00WYH/hvjyjiDbh9YJpGorflgzjyt/dDNcYk5mcMNJJyrY/kPUG
|
||||
3/eiMMIfW6UFSJq3ePK0NBFCzmdGWGUb+ZQS+JYP51eN2zsbZRm/Pfel
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAtoHIpsxDnO4yTLKghUGkAIg5102fcVj2sQ4kJhybnN7tfbQ0
|
||||
gZo0GE8A7SQAYM/lO0VcDknp/nkzA8Lsc5Lc1SJ7z1Iyd+/ZUg3GJE/ijPoPdMlF
|
||||
U4/HndmoMdLhcflCRZiirnLGwdvTbBrlGD0alK/kOqbSc9LT3oFVXWo2XvAyNtBx
|
||||
U7dHznxT5JuFnPyCZvoY+FvSpCbP5rV0NLzdCpk+6C4KTs3YAaTh6kVvSCch55E7
|
||||
hE0I2KKLjz1Ge563vPEWOnB40p6wZhzcwFIbiXmq8TpA342HInES1tFRwv7ZGSs+
|
||||
B8t/q9nGWTDf1as4+wBrQ/i35XavsMuNp+O+mQIDAQABAoIBACZaJ5xFmH/F3nQX
|
||||
pXvbS2eBOQZxnWvoUg7q9dW8dUcF4cpksBP8H65sC7nJsvqlNXq7HJk0FyQOvBWy
|
||||
RJYU6qsvT+1FTK2/jV+c3WKMFwOhGNZl5VemA0C8mIe/1PhqdO7DIIygOfxLAaba
|
||||
EAKD9K4COGfK3rbQOw2rCBFVXI+ed0L2q+IzpyZdvtUFVdDKF9d5i3m2Qr6Ayeuq
|
||||
dpf2ig69nC+4I505govODX2O9+q7x+zkddooxL72OXdEQFHn8p1K69Zk9iLz2jPX
|
||||
HPVyKXGYzCOyfZZk/jJjBCv+8bkFQg/Ncm/KSBeBjp2Gxo6EjfERUhkShkxtOB5P
|
||||
pAETPXECgYEA4SP/hV/MbmXnouKvjoWCZ0dYH92lNVOMMr2Ij/nOpvxTAyc1FXg+
|
||||
m5NrvMjRnGu32QfLcJmaXnK9YjqAZD9koQ3DisS0FbwTa7V8NSR5ImJfPWpa2VMP
|
||||
SiyWQCh6Iba2852hl8Ryph7wbbSXAcocthx6dIuP/QsX2Q95jrh+g0cCgYEAz4XO
|
||||
MSjwsrT/9i3voVKD+7eWBYkFpOJ+piYxrrmIrkMSWrwPIE9KBFfn7TlPvBPCb+W2
|
||||
uN/eLtO8HDePKfJQdZxphvlmy8eeIfjzBhod2HELJy3ShHS57uaD7iLLhPXrV9Gm
|
||||
As99lS+kKGeH30j9Si53oQU0eIyH8iZX+beR3x8CgYB/DGhqZHghqIIByjhVjgPb
|
||||
skgJm3NaV25bR9ejn829L9DMi7iKCBQUiSmYHB8lTSgvYhWs0hFp0QgMQYUojRmF
|
||||
RRYe3gfd6AdxlbWk65MsEyU5rCXeU9/h9K1JQU5CbjBp439H/MTR982nquw4R0zS
|
||||
e9mioQs9OaBYjkIDhxtliwKBgCRtaHRYq2ezPfsItTesNF7LKxptov/+ghzIN5Bk
|
||||
IQn13BLxT/Zr9KIujBeoJ8br8QWTXS+2nFm78RlC526Finoaqqt2vASpVajA+mfn
|
||||
zbVgooSOFpYJp1m4PRBgKzl7sYQI2QtFQNYfNsGg6sjXFx8eaQFq2HsQsAxhjq/W
|
||||
+VQhAoGBALWrW12fQ0dJffRIXs/PCfwPy24071Q41YEadiZbV2/ZYlpDG6MQIENe
|
||||
KaDMpflkuHhR6RQZmfJoImLvpKH0iOoxnTzPJhDQHKvwiqei3v7SuN2vh7VWGCGH
|
||||
ikLROTT/oVrq4Z5XrZuAR1wtqRgi/KwvztG+QTzxLhygOmmkbSdt
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1,20 @@
|
|||
-----BEGIN X509 CRL-----
|
||||
MIIDPDCCASQCAQEwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCU0UxEjAQBgNV
|
||||
BAgMCVN0b2NraG9sbTESMBAGA1UECgwJTXlPcmdOYW1lMRkwFwYDVQQLDBBNeUlu
|
||||
dGVybWVkaWF0ZUNBMRkwFwYDVQQDDBBNeUludGVybWVkaWF0ZUNBFw0yMjA3MjAy
|
||||
MDIzNTNaFw0zMjEwMjUyMDIzNTNaMBUwEwICEAIXDTIyMDYxMzEyNDIwNVqgbjBs
|
||||
MB8GA1UdIwQYMBaAFCuv1TkzC1fSgTfzE1m1u5pRCJsVMDwGA1UdHAQ1MDOgLqAs
|
||||
hipodHRwOi8vbG9jYWxob3N0Ojk4NzgvaW50ZXJtZWRpYXRlLmNybC5wZW2EAf8w
|
||||
CwYDVR0UBAQCAhADMA0GCSqGSIb3DQEBCwUAA4ICAQBbWdqRFsIrG6coL6ln1RL+
|
||||
uhgW9l3XMmjNlyiYHHNzOgnlBok9xu9UdaVCOKC6GEthWSzSlBY1AZugje57DQQd
|
||||
RkIJol9am94lKMTjF/qhzFLiSjho8fwZGDGyES5YeZXkLqNMOf6m/drKaI3iofWf
|
||||
l63qU9jY8dnSrVDkwgCguUL2FTx60v5H9NPxSctQ3VDxDvDj0sTAcHFknQcZbfvY
|
||||
ZWpOYNS0FAJlQPVK9wUoDxI0LhrWDq5h/T1jcGO34fPT8RUA5HRtFVUevqSuOLWx
|
||||
WTfTx5oDeMZPJTvHWUcg4yMElHty4tEvtkFxLSYbZqj7qTU+mi/LAN3UKBH/gBEN
|
||||
y2OsJvFhVRgHf+zPYegf3WzBSoeaXNAJZ4UnRo34P9AL3Mrh+4OOUP++oYRKjWno
|
||||
pYtAmTrIwEYoLyisEhhZ6aD92f/Op3dIYsxwhHt0n0lKrbTmUfiJUAe7kUZ4PMn4
|
||||
Gg/OHlbEDaDxW1dCymjyRGl+3/8kjy7bkYUXCf7w6JBeL2Hw2dFp1Gh13NRjre93
|
||||
PYlSOvI6QNisYGscfuYPwefXogVrNjf/ttCksMa51tUk+ylw7ZMZqQjcPPSzmwKc
|
||||
5CqpnQHfolvRuN0xIVZiAn5V6/MdHm7ocrXxOkzWQyaoNODTq4js8h8eYXgAkt1w
|
||||
p1PTEFBucGud7uBDE6Ub6A==
|
||||
-----END X509 CRL-----
|
|
@ -0,0 +1,67 @@
|
|||
-module(emqx_crl_cache_http_server).
|
||||
|
||||
-behaviour(gen_server).
|
||||
-compile([nowarn_export_all, export_all]).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% APIs
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
start_link(Parent, BasePort, CRLPem, Opts) ->
|
||||
process_flag(trap_exit, true),
|
||||
stop_http(),
|
||||
timer:sleep(100),
|
||||
gen_server:start_link(?MODULE, {Parent, BasePort, CRLPem, Opts}, []).
|
||||
|
||||
init({Parent, BasePort, CRLPem, Opts}) ->
|
||||
ok = start_http(Parent, CRLPem, [{port, BasePort} | Opts]),
|
||||
Parent ! {self(), ready},
|
||||
{ok, #{parent => Parent, crl_pem => CRLPem}}.
|
||||
|
||||
handle_call(_Request, _From, State) ->
|
||||
{reply, ignored, State}.
|
||||
|
||||
handle_cast(_Msg, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
stop_http().
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
stop(Pid) ->
|
||||
ok = gen_server:stop(Pid).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Callbacks
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
start_http(Parent, CRLPem, Opts) ->
|
||||
{ok, _Pid1} = cowboy:start_clear(http, Opts, #{
|
||||
env => #{dispatch => compile_router(Parent, CRLPem)}
|
||||
}),
|
||||
ok.
|
||||
|
||||
stop_http() ->
|
||||
cowboy:stop_listener(http),
|
||||
ok.
|
||||
|
||||
compile_router(Parent, CRLPem) ->
|
||||
{ok, _} = application:ensure_all_started(cowboy),
|
||||
cowboy_router:compile([
|
||||
{'_', [{'_', ?MODULE, #{parent => Parent, crl_pem => CRLPem}}]}
|
||||
]).
|
||||
|
||||
init(Req, #{parent := Parent, crl_pem := CRLPem} = State) ->
|
||||
%% assert
|
||||
<<"GET">> = cowboy_req:method(Req),
|
||||
Parent ! http_get,
|
||||
Reply = reply(Req, CRLPem),
|
||||
{ok, Reply, State}.
|
||||
|
||||
reply(Req, CRLPem) ->
|
||||
cowboy_req:reply(200, #{<<"content-type">> => <<"text/plain">>}, CRLPem, Req).
|
|
@ -0,0 +1,20 @@
|
|||
-----BEGIN X509 CRL-----
|
||||
MIIDPDCCASQCAQEwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCU0UxEjAQBgNV
|
||||
BAgMCVN0b2NraG9sbTESMBAGA1UECgwJTXlPcmdOYW1lMRkwFwYDVQQLDBBNeUlu
|
||||
dGVybWVkaWF0ZUNBMRkwFwYDVQQDDBBNeUludGVybWVkaWF0ZUNBFw0yMjA3MjAy
|
||||
MDIzNTNaFw0zMjEwMjUyMDIzNTNaMBUwEwICEAIXDTIyMDYxMzEyNDIwNVqgbjBs
|
||||
MB8GA1UdIwQYMBaAFCuv1TkzC1fSgTfzE1m1u5pRCJsVMDwGA1UdHAQ1MDOgLqAs
|
||||
hipodHRwOi8vbG9jYWxob3N0Ojk4NzgvaW50ZXJtZWRpYXRlLmNybC5wZW2EAf8w
|
||||
CwYDVR0UBAQCAhADMA0GCSqGSIb3DQEBCwUAA4ICAQBbWdqRFsIrG6coL6ln1RL+
|
||||
uhgW9l3XMmjNlyiYHHNzOgnlBok9xu9UdaVCOKC6GEthWSzSlBY1AZugje57DQQd
|
||||
RkIJol9am94lKMTjF/qhzFLiSjho8fwZGDGyES5YeZXkLqNMOf6m/drKaI3iofWf
|
||||
l63qU9jY8dnSrVDkwgCguUL2FTx60v5H9NPxSctQ3VDxDvDj0sTAcHFknQcZbfvY
|
||||
ZWpOYNS0FAJlQPVK9wUoDxI0LhrWDq5h/T1jcGO34fPT8RUA5HRtFVUevqSuOLWx
|
||||
WTfTx5oDeMZPJTvHWUcg4yMElHty4tEvtkFxLSYbZqj7qTU+mi/LAN3UKBH/gBEN
|
||||
y2OsJvFhVRgHf+zPYegf3WzBSoeaXNAJZ4UnRo34P9AL3Mrh+4OOUP++oYRKjWno
|
||||
pYtAmTrIwEYoLyisEhhZ6aD92f/Op3dIYsxwhHt0n0lKrbTmUfiJUAe7kUZ4PMn4
|
||||
Gg/OHlbEDaDxW1dCymjyRGl+3/8kjy7bkYUXCf7w6JBeL2Hw2dFp1Gh13NRjre93
|
||||
PYlSOvI6QNisYGscfuYPwefXogVrNjf/ttCksMa51tUk+ylw7ZMZqQjcPPSzmwKc
|
||||
5CqpnQHfolvRuN0xIVZiAn5V6/MdHm7ocrXxOkzWQyaoNODTq4js8h8eYXgAkt1w
|
||||
p1PTEFBucGud7uBDE6Ub6A==
|
||||
-----END X509 CRL-----
|
|
@ -0,0 +1,35 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIGCTCCA/GgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCU0Ux
|
||||
EjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UECgwJTXlPcmdOYW1lMRkwFwYDVQQL
|
||||
DBBNeUludGVybWVkaWF0ZUNBMRkwFwYDVQQDDBBNeUludGVybWVkaWF0ZUNBMB4X
|
||||
DTIyMDYxMzEyNDIwNVoXDTIzMDYyMzEyNDIwNVoweDELMAkGA1UEBhMCU0UxEjAQ
|
||||
BgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJU3RvY2tob2xtMRIwEAYDVQQKDAlN
|
||||
eU9yZ05hbWUxGTAXBgNVBAsMEE15SW50ZXJtZWRpYXRlQ0ExEjAQBgNVBAMMCWxv
|
||||
Y2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOpBaUId5ga7
|
||||
NzIXvSvnzqLAsyejjEZYcSVwWQinAH3il/Y/NbUOakPbDCQbb3U7p3oD6H0R0pVx
|
||||
x0nQyhh2XlSNRc8ORAcrZlfS6/Su5o07htSKL7FZ0ySYbia2EQ6ZRFpke/+UPwr3
|
||||
eXSQUhdOX+iA0Uf5gN3fUtgGorCuT2POGemGjBvYiPoA3aD1MxHyV7PnI7CoTw8Y
|
||||
cvFiXIW0Jqp69ZgSuzrmfIiMyum94pfkB1ljxao9TBSFqJTn2A3ysNZAXpGPyfP+
|
||||
1zPznxiMSvAFLihUVjKwE+glSNKvdhUgl3yAxaFI9+IeoAytPNmQU+E+o4YCuRHT
|
||||
TCM1Tch95/ECAwEAAaOCAagwggGkMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQD
|
||||
AgZAMDMGCWCGSAGG+EIBDQQmFiRPcGVuU1NMIEdlbmVyYXRlZCBTZXJ2ZXIgQ2Vy
|
||||
dGlmaWNhdGUwHQYDVR0OBBYEFAPF2lui5EhHG8lTbpglZ3BZYj2kMIGaBgNVHSME
|
||||
gZIwgY+AFCuv1TkzC1fSgTfzE1m1u5pRCJsVoXOkcTBvMQswCQYDVQQGEwJTRTES
|
||||
MBAGA1UECAwJU3RvY2tob2xtMRIwEAYDVQQHDAlTdG9ja2hvbG0xEjAQBgNVBAoM
|
||||
CU15T3JnTmFtZTERMA8GA1UECwwITXlSb290Q0ExETAPBgNVBAMMCE15Um9vdENB
|
||||
ggIQADAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwOwYDVR0f
|
||||
BDQwMjAwoC6gLIYqaHR0cDovL2xvY2FsaG9zdDo5ODc4L2ludGVybWVkaWF0ZS5j
|
||||
cmwucGVtMDEGCCsGAQUFBwEBBCUwIzAhBggrBgEFBQcwAYYVaHR0cDovL2xvY2Fs
|
||||
aG9zdDo5ODc3MA0GCSqGSIb3DQEBCwUAA4ICAQCCTuM+KXLH2EuUn+TB78LgYrzl
|
||||
/dlPj5UWPTDgiF+93fQSNvkg2uWVKPkckKNKttg+VyoRnRX87vI/bZp+dzvHfp5v
|
||||
hXZuv2TkvA8ZMnJ6QUbAmiQlNTqNh0esftfXbmZAo+VvtwC/NZfevH08SO1+3R4S
|
||||
PMC/Bnqmv/G3mR2qWqLu4X1ikpkgNGj4DTEkkqZFerlRyaeZHZLSVRNt6x3u4lXa
|
||||
KQKbTPYP+yYpJfi0eBFy6t0hdN63BZDao3z9fulpxfjWfL32gt+TQLMf/6aJugs+
|
||||
+2BS0LAWXr9mP89Ljzo4V0G6pMBQ84/oK+mYtkFCNjRaAT+b4xGE1Ttp9H40xRZY
|
||||
hm2xzVH05PgXw+IJdfnvH+245vtPUTtrnysy9FEfF7px3tAAZCiT4ogrgr52+VK6
|
||||
vGliEIxZq2ICWCK1Wy0i9zZFk3lQCFF3bWjKQCapCAFPE0naZZv7g10HkXl8+5Gc
|
||||
1aSyP2LcroUyDfYx8VwKw1sLi1KtO71hs/TOTeoAPzFBmZA8h325mU5krpQtuNPg
|
||||
TRxAPLztWltHR3T+WKOUMq+YZx0IiUvS6VZaCeoQeXTSZZTX5j6BXz8ffmyAcXMZ
|
||||
tNATxIgDyGUfw0V6fiKQrnZCqP+fF/tdks5LKeU6tYE4JKWDarIzNNnPmhPQnJOI
|
||||
YeW1Ts3aExER9EorUw==
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA6kFpQh3mBrs3Mhe9K+fOosCzJ6OMRlhxJXBZCKcAfeKX9j81
|
||||
tQ5qQ9sMJBtvdTunegPofRHSlXHHSdDKGHZeVI1Fzw5EBytmV9Lr9K7mjTuG1Iov
|
||||
sVnTJJhuJrYRDplEWmR7/5Q/Cvd5dJBSF05f6IDRR/mA3d9S2AaisK5PY84Z6YaM
|
||||
G9iI+gDdoPUzEfJXs+cjsKhPDxhy8WJchbQmqnr1mBK7OuZ8iIzK6b3il+QHWWPF
|
||||
qj1MFIWolOfYDfKw1kBekY/J8/7XM/OfGIxK8AUuKFRWMrAT6CVI0q92FSCXfIDF
|
||||
oUj34h6gDK082ZBT4T6jhgK5EdNMIzVNyH3n8QIDAQABAoIBABG/NN86bqPR6SOV
|
||||
YtKBtEjmOmxlWoo1xxSkB0q9hC8FTKfuL/5wgiJz5N6TaYVDKLP4udNH12FVBlkU
|
||||
RUtHJGxZa5F9LjAw3IcIxrF50qOef994PJa+DF34YlfycSZe/Cuw8yfwrjoBd6Ua
|
||||
De4QFPoDUFeYkme8tIUDM64Y9pDD9llfS/ZOQiCAgn+8mk99uCnJVIFo19Umof5N
|
||||
009Cn6PElDKOw40Pz8s4ZJ9bpXH/YPKLeNRn0wlgFzYGhJ1z74YBmXF3CUHU6WEy
|
||||
pVbJkPDAwnFScuAjpwJmzO4TDBfTBd5L/CJy4nsLpY06UOm1PHuUpAhPAgiwuADR
|
||||
TM3100ECgYEA/F5Glpb0ROR38UTgCSOT8xvxyzNV84YMaQrWzGMUxqJpWWyVaKEy
|
||||
k/jzUxUKbkUrJDz/Y+bcGElVCI+g8x0yekpcLn1mtB8XwltpmCSBhNBTMgUHnI9v
|
||||
M+PadQhyZiJFrE/eoXo0V/K30Xv8jokWhWGTIkCdhiVSks/0eWSzPzkCgYEA7aBo
|
||||
d/0e0R2Y2wXDsfGqxcKFs4IMHnzNl0isycakLOoZwinKPvb1MSg1zxW0mU+I1c/n
|
||||
18rftcS8/siai26iXErDeZnvtlj+OKzQboRQf5JvDHp+rXBjGpZSmI2Nb+kAQ71S
|
||||
JeIkglfBHGCKAMJQjE3N3U9YMBqCB1xdJ/TnNnkCgYA4PJnmPMU6BN9leD+kSbVS
|
||||
W0vKSCpDFf/1+GBdM0cR7GclcjjpE+K9bqBqRyoH4In4jU8r5+nrz4uPWNI42qzA
|
||||
64kXIwKb6MHWoaAqMxhZjEK9xrknfh79pSytH7C+aay09SdbPGwlnQSxPbvN12aZ
|
||||
WmD7JQL1PaPk60pDMtluoQKBgDYJLxhyB+r3twW/VtQFJ5dW975tSUI5kSrgzOIJ
|
||||
eNX52iesByCwWet2wF26Ctp+Gpi8cXVB3gNgnLW3emVQoD0qhy8E0Vz++bh7m941
|
||||
2nRYIUaOKHZaQz8NhfTI46vaKUQ+LgsNVM4LFI/WaCtqBJUTMEguPdiafo0b9Ncc
|
||||
OuPJAoGBAPSYfuXinT9IAVwzIjc1TkfdN3G39ckBFQtNSvIoynBro+dlh3us+QJi
|
||||
i24MtpUiBbCKmFZrrk5WrddxNLbE/JSa40+O+s1RaP3SfNuDGTWMe3HGMh+PVH8D
|
||||
yte7L8pWouXSQPLVXUb3SmPboxBvXwPZMB54gkbKcSB2Gg7CjfCS
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1,467 @@
|
|||
%%--------------------------------------------------------------------
|
||||
%% Copyright (c) 2022 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
-module(emqx_ocsp_cache_SUITE).
|
||||
|
||||
-compile(export_all).
|
||||
-compile(nowarn_export_all).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include_lib("common_test/include/ct.hrl").
|
||||
-include_lib("snabbkaffe/include/snabbkaffe.hrl").
|
||||
|
||||
-include_lib("ssl/src/ssl_handshake.hrl").
|
||||
|
||||
-define(CACHE_TAB, emqx_ocsp_cache).
|
||||
|
||||
all() ->
|
||||
[{group, openssl}] ++ tests().
|
||||
|
||||
tests() ->
|
||||
emqx_ct:all(?MODULE) -- openssl_tests().
|
||||
|
||||
openssl_tests() ->
|
||||
[t_openssl_client].
|
||||
|
||||
groups() ->
|
||||
OpensslTests = openssl_tests(),
|
||||
[ {openssl, [ {group, tls12}
|
||||
, {group, tls13}
|
||||
]}
|
||||
, {tls12, [ {group, with_status_request}
|
||||
, {group, without_status_request}
|
||||
]}
|
||||
, {tls13, [ {group, with_status_request}
|
||||
, {group, without_status_request}
|
||||
]}
|
||||
, {with_status_request, [], OpensslTests}
|
||||
, {without_status_request, [], OpensslTests}
|
||||
].
|
||||
|
||||
init_per_suite(Config) ->
|
||||
application:load(emqx),
|
||||
OriginalListeners = application:get_env(emqx, listeners, []),
|
||||
[ {original_listeners, OriginalListeners}
|
||||
| Config].
|
||||
|
||||
end_per_suite(Config) ->
|
||||
OriginalListeners = ?config(original_listeners, Config),
|
||||
application:set_env(emqx, listeners, OriginalListeners),
|
||||
ok.
|
||||
|
||||
init_per_group(tls12, Config) ->
|
||||
[{tls_vsn, "-tls1_2"} | Config];
|
||||
init_per_group(tls13, Config) ->
|
||||
[{tls_vsn, "-tls1_3"} | Config];
|
||||
init_per_group(with_status_request, Config) ->
|
||||
[{status_request, true} | Config];
|
||||
init_per_group(without_status_request, Config) ->
|
||||
[{status_request, false} | Config];
|
||||
init_per_group(_Group, Config) ->
|
||||
Config.
|
||||
|
||||
end_per_group(_Group, _Config) ->
|
||||
ok.
|
||||
|
||||
init_per_testcase(t_openssl_client, Config) ->
|
||||
ct:timetrap(10_000),
|
||||
OriginalListeners = application:get_env(emqx, listeners),
|
||||
DataDir = ?config(data_dir, Config),
|
||||
IssuerPem = filename:join([DataDir, "ocsp-issuer.pem"]),
|
||||
ServerCert = filename:join([DataDir, "server.pem"]),
|
||||
ServerKey = filename:join([DataDir, "server.key"]),
|
||||
CACert = filename:join([DataDir, "ca.pem"]),
|
||||
Handler =
|
||||
fun(emqx) ->
|
||||
Listeners0 = emqx:get_env(listeners, []),
|
||||
{[SSLListener0 = #{opts := Opts0}], Listeners1} =
|
||||
lists:partition(
|
||||
fun(#{proto := P, name := N}) ->
|
||||
N =:= "external" andalso P =:= ssl
|
||||
end,
|
||||
Listeners0),
|
||||
SSLOpts0 = proplists:get_value(ssl_options, Opts0),
|
||||
SSLOpts1 = lists:foldl(
|
||||
fun proplists:delete/2,
|
||||
SSLOpts0,
|
||||
[certfile, keyfile]),
|
||||
SSLOpts2 = lists:foldl(
|
||||
fun({K, V}, Acc) ->
|
||||
[{K, V} | Acc]
|
||||
end,
|
||||
SSLOpts1,
|
||||
[ {certfile, ServerCert}
|
||||
, {keyfile, ServerKey}
|
||||
, {cacertfile, CACert}
|
||||
]),
|
||||
Opts1 = proplists:delete(ssl_options, Opts0),
|
||||
Opts2 = [ {ocsp_responder_url, "http://127.0.0.1:9877"}
|
||||
, {ocsp_issuer_pem, IssuerPem}
|
||||
, {ssl_options, SSLOpts2}
|
||||
| Opts1],
|
||||
Listeners = [ SSLListener0#{opts => Opts2}
|
||||
| Listeners1],
|
||||
application:set_env(emqx, listeners, Listeners),
|
||||
ok;
|
||||
(_) ->
|
||||
ok
|
||||
end,
|
||||
OCSPResponderPort = spawn_openssl_ocsp_responder(Config),
|
||||
{os_pid, OCSPOSPid} = erlang:port_info(OCSPResponderPort, os_pid),
|
||||
ensure_port_open(9877),
|
||||
ct:sleep(1_000),
|
||||
emqx_ct_helpers:start_apps([], Handler),
|
||||
ct:sleep(1_000),
|
||||
[ {original_listeners, OriginalListeners}
|
||||
, {ocsp_responder_port, OCSPResponderPort}
|
||||
, {ocsp_responder_os_pid, OCSPOSPid}
|
||||
| Config];
|
||||
init_per_testcase(_TestCase, Config) ->
|
||||
ct:timetrap(10_000),
|
||||
TestPid = self(),
|
||||
ok = meck:new(emqx_ocsp_cache, [non_strict, passthrough, no_history, no_link]),
|
||||
meck:expect(emqx_ocsp_cache, http_get,
|
||||
fun(URL, _HTTPTimeout) ->
|
||||
TestPid ! {http_get, URL},
|
||||
{ok, {{"HTTP/1.0", 200, 'OK'}, [], <<"ocsp response">>}}
|
||||
end),
|
||||
{ok, CachePid} = emqx_ocsp_cache:start_link(),
|
||||
DataDir = ?config(data_dir, Config),
|
||||
application:set_env(
|
||||
emqx, listeners,
|
||||
[#{ proto => ssl
|
||||
, name => "test_ocsp"
|
||||
, opts => [ {ssl_options, [{certfile,
|
||||
filename:join(DataDir, "server.pem")}]}
|
||||
, {ocsp_responder_url, "http://localhost:9877"}
|
||||
, {ocsp_issuer_pem,
|
||||
filename:join(DataDir, "ocsp-issuer.pem")}
|
||||
, {ocsp_refresh_http_timeout, 15_000}
|
||||
, {ocsp_refresh_interval, 1_000}
|
||||
]
|
||||
}]),
|
||||
snabbkaffe:start_trace(),
|
||||
[ {cache_pid, CachePid}
|
||||
| Config].
|
||||
|
||||
end_per_testcase(t_openssl_client, Config) ->
|
||||
OriginalListeners = ?config(original_listeners, Config),
|
||||
OCSPResponderOSPid = ?config(ocsp_responder_os_pid, Config),
|
||||
case OriginalListeners of
|
||||
{ok, Listeners} -> application:set_env(emqx, listeners, Listeners);
|
||||
_ -> ok
|
||||
end,
|
||||
catch kill_pid(OCSPResponderOSPid),
|
||||
emqx_ct_helpers:stop_apps([]),
|
||||
ok;
|
||||
end_per_testcase(_TestCase, Config) ->
|
||||
CachePid = ?config(cache_pid, Config),
|
||||
catch gen_server:stop(CachePid),
|
||||
meck:unload([emqx_ocsp_cache]),
|
||||
ok.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Helper functions
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
assert_no_http_get() ->
|
||||
receive
|
||||
{http_get, _URL} ->
|
||||
error(should_be_cached)
|
||||
after
|
||||
0 ->
|
||||
ok
|
||||
end.
|
||||
|
||||
assert_http_get(0) -> ok;
|
||||
assert_http_get(N) when N > 0 ->
|
||||
receive
|
||||
{http_get, URL} ->
|
||||
?assertMatch("http://localhost:9877/" ++ _Request64, URL),
|
||||
ok
|
||||
after
|
||||
0 ->
|
||||
error(no_http_get)
|
||||
end,
|
||||
assert_http_get(N - 1).
|
||||
|
||||
spawn_openssl_client(TLSVsn, RequestStatus, Config) ->
|
||||
DataDir = ?config(data_dir, Config),
|
||||
ClientCert = filename:join([DataDir, "client.pem"]),
|
||||
ClientKey = filename:join([DataDir, "client.key"]),
|
||||
Cacert = filename:join([DataDir, "ca.pem"]),
|
||||
Openssl = os:find_executable("openssl"),
|
||||
StatusOpt = case RequestStatus of
|
||||
true -> ["-status"];
|
||||
false -> []
|
||||
end,
|
||||
open_port( {spawn_executable, Openssl}
|
||||
, [ {args, [ "s_client"
|
||||
, "-connect", "localhost:8883"
|
||||
%% needed to trigger `sni_fun'
|
||||
, "-servername", "localhost"
|
||||
, TLSVsn
|
||||
, "-CAfile", Cacert
|
||||
, "-cert", ClientCert
|
||||
, "-key", ClientKey
|
||||
] ++ StatusOpt}
|
||||
, binary
|
||||
, stderr_to_stdout
|
||||
]
|
||||
).
|
||||
|
||||
spawn_openssl_ocsp_responder(Config) ->
|
||||
DataDir = ?config(data_dir, Config),
|
||||
IssuerCert = filename:join([DataDir, "ocsp-issuer.pem"]),
|
||||
IssuerKey = filename:join([DataDir, "ocsp-issuer.key"]),
|
||||
Cacert = filename:join([DataDir, "ca.pem"]),
|
||||
Index = filename:join([DataDir, "index.txt"]),
|
||||
Openssl = os:find_executable("openssl"),
|
||||
open_port( {spawn_executable, Openssl}
|
||||
, [ {args, [ "ocsp"
|
||||
, "-ignore_err"
|
||||
, "-port", "9877"
|
||||
, "-CA", Cacert
|
||||
, "-rkey", IssuerKey
|
||||
, "-rsigner", IssuerCert
|
||||
, "-index", Index
|
||||
]}
|
||||
, binary
|
||||
, stderr_to_stdout
|
||||
]
|
||||
).
|
||||
|
||||
kill_pid(OSPid) ->
|
||||
os:cmd("kill -9 " ++ integer_to_list(OSPid)).
|
||||
|
||||
test_ocsp_connection(TLSVsn, WithRequestStatus = true, Config) ->
|
||||
ClientPort = spawn_openssl_client(TLSVsn, WithRequestStatus, Config),
|
||||
{os_pid, ClientOSPid} = erlang:port_info(ClientPort, os_pid),
|
||||
try
|
||||
timer:sleep(timer:seconds(1)),
|
||||
{messages, Messages} = process_info(self(), messages),
|
||||
OCSPOutput0 = [Output || {_Port, {data, Output}} <- Messages,
|
||||
re:run(Output, "OCSP response:") =/= nomatch],
|
||||
?assertMatch([_], OCSPOutput0,
|
||||
#{ all_messages => Messages
|
||||
}),
|
||||
[OCSPOutput] = OCSPOutput0,
|
||||
?assertMatch({match, _}, re:run(OCSPOutput, "OCSP Response Status: successful"),
|
||||
#{all_messages => Messages}),
|
||||
?assertMatch({match, _}, re:run(OCSPOutput, "Cert Status: good"),
|
||||
#{all_messages => Messages}),
|
||||
ok
|
||||
after
|
||||
catch kill_pid(ClientOSPid)
|
||||
end;
|
||||
test_ocsp_connection(TLSVsn, WithRequestStatus = false, Config) ->
|
||||
ClientPort = spawn_openssl_client(TLSVsn, WithRequestStatus, Config),
|
||||
{os_pid, ClientOSPid} = erlang:port_info(ClientPort, os_pid),
|
||||
try
|
||||
timer:sleep(timer:seconds(1)),
|
||||
{messages, Messages} = process_info(self(), messages),
|
||||
OCSPOutput = [Output || {_Port, {data, Output}} <- Messages,
|
||||
re:run(Output, "OCSP response:") =/= nomatch],
|
||||
?assertEqual([], OCSPOutput,
|
||||
#{all_messages => Messages}),
|
||||
ok
|
||||
after
|
||||
catch kill_pid(ClientOSPid)
|
||||
end.
|
||||
|
||||
ensure_port_open(Port) ->
|
||||
do_ensure_port_open(Port, 10).
|
||||
|
||||
do_ensure_port_open(Port, 0) ->
|
||||
error({port_not_open, Port});
|
||||
do_ensure_port_open(Port, N) when N > 0 ->
|
||||
Timeout = 1_000,
|
||||
case gen_tcp:connect("localhost", Port, [], Timeout) of
|
||||
{ok, Sock} ->
|
||||
gen_tcp:close(Sock),
|
||||
ok;
|
||||
{error, _} ->
|
||||
ct:sleep(500),
|
||||
do_ensure_port_open(Port, N - 1)
|
||||
end.
|
||||
|
||||
get_sni_fun(ListenerID) ->
|
||||
#{opts := Opts} = emqx_listeners:find_by_id(ListenerID),
|
||||
SSLOpts = proplists:get_value(ssl_options, Opts),
|
||||
proplists:get_value(sni_fun, SSLOpts).
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Test cases
|
||||
%%--------------------------------------------------------------------
|
||||
|
||||
t_request_ocsp_response(_Config) ->
|
||||
?check_trace(
|
||||
begin
|
||||
ListenerID = <<"mqtt:ssl:test_ocsp">>,
|
||||
%% not yet cached.
|
||||
?assertEqual([], ets:tab2list(?CACHE_TAB)),
|
||||
?assertEqual({ok, <<"ocsp response">>},
|
||||
emqx_ocsp_cache:fetch_response(ListenerID)),
|
||||
assert_http_get(1),
|
||||
?assertMatch([{_, <<"ocsp response">>}], ets:tab2list(?CACHE_TAB)),
|
||||
%% already cached; should not perform request again.
|
||||
?assertEqual({ok, <<"ocsp response">>},
|
||||
emqx_ocsp_cache:fetch_response(ListenerID)),
|
||||
assert_no_http_get(),
|
||||
ok
|
||||
end,
|
||||
fun(Trace) ->
|
||||
?assert(
|
||||
?strict_causality(
|
||||
#{?snk_kind := ocsp_cache_miss, listener_id := _ListenerID},
|
||||
#{?snk_kind := ocsp_http_fetch_and_cache, listener_id := _ListenerID},
|
||||
Trace)),
|
||||
?assertMatch(
|
||||
[_],
|
||||
?of_kind(ocsp_cache_miss, Trace)),
|
||||
?assertMatch(
|
||||
[_],
|
||||
?of_kind(ocsp_http_fetch_and_cache, Trace)),
|
||||
?assertMatch(
|
||||
[_],
|
||||
?of_kind(ocsp_cache_hit, Trace)),
|
||||
ok
|
||||
end).
|
||||
|
||||
t_request_ocsp_response_restart_cache(Config) ->
|
||||
process_flag(trap_exit, true),
|
||||
CachePid = ?config(cache_pid, Config),
|
||||
ListenerID = <<"mqtt:ssl:test_ocsp">>,
|
||||
?check_trace(
|
||||
begin
|
||||
[] = ets:tab2list(?CACHE_TAB),
|
||||
{ok, _} = emqx_ocsp_cache:fetch_response(ListenerID),
|
||||
?wait_async_action(
|
||||
begin
|
||||
Ref = monitor(process, CachePid),
|
||||
exit(CachePid, kill),
|
||||
receive
|
||||
{'DOWN', Ref, process, CachePid, killed} ->
|
||||
ok
|
||||
after
|
||||
1_000 ->
|
||||
error(cache_not_killed)
|
||||
end,
|
||||
{ok, _} = emqx_ocsp_cache:start_link(),
|
||||
ok
|
||||
end,
|
||||
#{?snk_kind := ocsp_cache_init}),
|
||||
{ok, _} = emqx_ocsp_cache:fetch_response(ListenerID),
|
||||
ok
|
||||
end,
|
||||
fun(Trace) ->
|
||||
?assertMatch(
|
||||
[_, _],
|
||||
?of_kind(ocsp_http_fetch_and_cache, Trace)),
|
||||
assert_http_get(2),
|
||||
ok
|
||||
end).
|
||||
|
||||
t_request_ocsp_response_bad_http_status(_Config) ->
|
||||
TestPid = self(),
|
||||
meck:expect(emqx_ocsp_cache, http_get,
|
||||
fun(URL, _HTTPTimeout) ->
|
||||
TestPid ! {http_get, URL},
|
||||
{ok, {{"HTTP/1.0", 404, 'Not Found'}, [], <<"not found">>}}
|
||||
end),
|
||||
ListenerID = <<"mqtt:ssl:test_ocsp">>,
|
||||
%% not yet cached.
|
||||
?assertEqual([], ets:tab2list(?CACHE_TAB)),
|
||||
?assertEqual(error,
|
||||
emqx_ocsp_cache:fetch_response(ListenerID)),
|
||||
assert_http_get(1),
|
||||
?assertEqual([], ets:tab2list(?CACHE_TAB)),
|
||||
ok.
|
||||
|
||||
t_request_ocsp_response_timeout(_Config) ->
|
||||
TestPid = self(),
|
||||
meck:expect(emqx_ocsp_cache, http_get,
|
||||
fun(URL, _HTTPTimeout) ->
|
||||
TestPid ! {http_get, URL},
|
||||
{error, timeout}
|
||||
end),
|
||||
ListenerID = <<"mqtt:ssl:test_ocsp">>,
|
||||
%% not yet cached.
|
||||
?assertEqual([], ets:tab2list(?CACHE_TAB)),
|
||||
?assertEqual(error,
|
||||
emqx_ocsp_cache:fetch_response(ListenerID)),
|
||||
assert_http_get(1),
|
||||
?assertEqual([], ets:tab2list(?CACHE_TAB)),
|
||||
ok.
|
||||
|
||||
t_register_listener(_Config) ->
|
||||
ListenerID = <<"mqtt:ssl:test_ocsp">>,
|
||||
%% should fetch and cache immediately
|
||||
{ok, {ok, _}} =
|
||||
?wait_async_action(
|
||||
emqx_ocsp_cache:register_listener(ListenerID),
|
||||
#{?snk_kind := ocsp_http_fetch_and_cache, listener_id := ListenerID}),
|
||||
assert_http_get(1),
|
||||
?assertMatch([{_, <<"ocsp response">>}], ets:tab2list(?CACHE_TAB)),
|
||||
ok.
|
||||
|
||||
t_refresh_periodically(_Config) ->
|
||||
ListenerID = <<"mqtt:ssl:test_ocsp">>,
|
||||
%% should refresh periodically
|
||||
{ok, SubRef} =
|
||||
snabbkaffe:subscribe(
|
||||
fun(#{?snk_kind := ocsp_http_fetch_and_cache, listener_id := ListenerID0}) ->
|
||||
ListenerID0 =:= ListenerID;
|
||||
(_) ->
|
||||
false
|
||||
end,
|
||||
_NEvents = 2,
|
||||
_Timeout = 5_000),
|
||||
ok = emqx_ocsp_cache:register_listener(ListenerID),
|
||||
?assertMatch({ok, [_, _]}, snabbkaffe:receive_events(SubRef)),
|
||||
assert_http_get(2),
|
||||
ok.
|
||||
|
||||
t_sni_fun_success(_Config) ->
|
||||
ListenerID = <<"mqtt:ssl:test_ocsp">>,
|
||||
ServerName = "localhost",
|
||||
?assertEqual(
|
||||
[{certificate_status,
|
||||
#certificate_status{
|
||||
status_type = ?CERTIFICATE_STATUS_TYPE_OCSP,
|
||||
response = <<"ocsp response">>
|
||||
}}],
|
||||
emqx_ocsp_cache:sni_fun(ServerName, ListenerID)),
|
||||
ok.
|
||||
|
||||
t_sni_fun_http_error(_Config) ->
|
||||
meck:expect(emqx_ocsp_cache, http_get,
|
||||
fun(_URL, _HTTPTimeout) ->
|
||||
{error, timeout}
|
||||
end),
|
||||
ListenerID = <<"mqtt:ssl:test_ocsp">>,
|
||||
ServerName = "localhost",
|
||||
?assertEqual(
|
||||
[],
|
||||
emqx_ocsp_cache:sni_fun(ServerName, ListenerID)),
|
||||
ok.
|
||||
|
||||
t_code_change(_Config) ->
|
||||
ListenerID = <<"mqtt:ssl:test_ocsp">>,
|
||||
SNIFun0 = get_sni_fun(ListenerID),
|
||||
?assertMatch({ok, _}, emqx_ocsp_cache:code_change(vsn, state, extra)),
|
||||
SNIFun1 = get_sni_fun(ListenerID),
|
||||
?assertNotEqual(SNIFun0, SNIFun1).
|
||||
|
||||
t_openssl_client(Config) ->
|
||||
TLSVsn = ?config(tls_vsn, Config),
|
||||
WithStatusRequest = ?config(status_request, Config),
|
||||
%% ensure ocsp response is already cached.
|
||||
ListenerID = <<"mqtt:ssl:external">>,
|
||||
?assertMatch(
|
||||
{ok, _},
|
||||
emqx_ocsp_cache:fetch_response(ListenerID),
|
||||
#{msgs => process_info(self(), messages)}),
|
||||
timer:sleep(500),
|
||||
test_ocsp_connection(TLSVsn, WithStatusRequest, Config).
|
|
@ -0,0 +1,68 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIF+zCCA+OgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwbzELMAkGA1UEBhMCU0Ux
|
||||
EjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJU3RvY2tob2xtMRIwEAYDVQQK
|
||||
DAlNeU9yZ05hbWUxETAPBgNVBAsMCE15Um9vdENBMREwDwYDVQQDDAhNeVJvb3RD
|
||||
QTAeFw0yMjA2MDYxNjU1NTlaFw0zMjA2MDMxNjU1NTlaMGsxCzAJBgNVBAYTAlNF
|
||||
MRIwEAYDVQQIDAlTdG9ja2hvbG0xEjAQBgNVBAoMCU15T3JnTmFtZTEZMBcGA1UE
|
||||
CwwQTXlJbnRlcm1lZGlhdGVDQTEZMBcGA1UEAwwQTXlJbnRlcm1lZGlhdGVDQTCC
|
||||
AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANbHOqvXq/7kNmXX6DW4O6EW
|
||||
/2PBdj/1oPwdIOYo6NfmieCa0BoQ/+W1k+Cj6OR75m0c6JmzPLfUckMvduuW/tJ9
|
||||
aDz/wFFbSYv/bA9f99mNXI0cebAZvYOI+cRBF4nyag3miRbMcCiOPMMDq936F3hd
|
||||
Mi6CL+pIFHif74Q66Ncx10M/nL+E9sEhVyTLrAF0FsMmjkSPdzChb+HByKWRD4f2
|
||||
OYiJxhvJNjLf/+qtn7+A5cwqqbW8zYi6jJPWun24zd2HWHLfehVW8CYzGxBJR7kw
|
||||
KRnPXEuXhnkgEkgfqj2122Jyd35UC/VUpJW1uJJgFNa2w7+bDj7PS6i4kdqjcEHF
|
||||
GqUELN8BUIwyicvJ25KnGt0J5JnHy0rDfJeVv9/6EB1Ekm/tKwtJsKLqLThH5Kql
|
||||
nFM0PUx6OV3nYzXvmmU44SvfIDPy3rhhpVCyaBBzdPyt0ugDkKuwwWcuo6KWSxR1
|
||||
lGTs2YjIYPwnRm8OqvynqCsn0vWUFOluR7suKlUmJFdPpWw04x+v1GZ3YRAig/GL
|
||||
6qtByXDgZ27F4QwNPR5nGMmiu7M2QOE/nJS65A3mjrKeYSD+reQ/WbIWtLuHIhn0
|
||||
XDfHIsuzG6yK9Up1Ske+S1Uos8hTGzvF5NhJlqexd5VJ+VKYLtLkEa/tOj4p/Z5S
|
||||
3nH+tEqg15nE54ovtDMdAgMBAAGjgaQwgaEwHQYDVR0OBBYEFNdekbfOfTuU9Z2G
|
||||
fCx0uoDMaaT5MB8GA1UdIwQYMBaAFJf5t2YMW5pYapGnvfGXaTDV4tQKMBIGA1Ud
|
||||
EwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMDsGA1UdHwQ0MDIwMKAuoCyG
|
||||
Kmh0dHA6Ly9sb2NhbGhvc3Q6OTg3OC9pbnRlcm1lZGlhdGUuY3JsLnBlbTANBgkq
|
||||
hkiG9w0BAQsFAAOCAgEAn6Mw7+BBqzsEEtSDrd97v8niqJdVXPgxyIeHJvdZm1iF
|
||||
3eRmxAUXy1DvD8tCqI8vJoz6p/4ELjedxc/Pcum0OravAElO6+jBOplwZCyhmpl8
|
||||
D4wgh/tbKnOWL20sX6sQESq/dORPf/oPDm9CBu55LaYnNTQHCNtYBZ7UrBhlsdYA
|
||||
CDtsthnYcC4bYTjmm5fYlg/r3LeVN5HINYFUkh3ffKxIpqWdYOhQShOHv2k5hrzM
|
||||
Lg8qu098q3kqJ+2thauXuEpD2HME0HelXaqWokwv9ETtJpuA6xQshJrT+2DjF3CY
|
||||
UY3qdwjWwLr9xjf9DyHmC4hdsaO2ZAqKfMoVi6pcJ4F/20vsTM8063SFxKCbb9aw
|
||||
hpzLWHB2iiKdRs/bczrW7f6XUbCFF2Et6Wl271Q7cyf8GzIdepvGsRGlcl6UaZ40
|
||||
wGfpQTgrCZ/kWHNaii4BAXGc9oj8f4NeKRVybV+cCwboed0cvNqnnKBPuz6iQz9c
|
||||
JzVc7SSgOoAPJ5oh8QYi0vhfVYo34C+ZC+NderWGtMrcQrUzYH1vAv1MQ7Sfxnso
|
||||
VWO7tCIjeBHcZZ+F7t2d4dJEaGYO9eYjIKSq2brFEQ+4Ix124ZWSbluCzQIXuaR3
|
||||
49qRH9Ont5UW4mGBAAgSAbyQxPkOZ3IH9vIFa61jyUE4RKGxkOAwHgajKPpmG9w=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFzzCCA7egAwIBAgIUFiQDd5Zw7kGG4XxaFayO4MUPSl0wDQYJKoZIhvcNAQEL
|
||||
BQAwbzELMAkGA1UEBhMCU0UxEjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJ
|
||||
U3RvY2tob2xtMRIwEAYDVQQKDAlNeU9yZ05hbWUxETAPBgNVBAsMCE15Um9vdENB
|
||||
MREwDwYDVQQDDAhNeVJvb3RDQTAeFw0yMjA2MDYxNjU1NTlaFw00MjA2MDExNjU1
|
||||
NTlaMG8xCzAJBgNVBAYTAlNFMRIwEAYDVQQIDAlTdG9ja2hvbG0xEjAQBgNVBAcM
|
||||
CVN0b2NraG9sbTESMBAGA1UECgwJTXlPcmdOYW1lMREwDwYDVQQLDAhNeVJvb3RD
|
||||
QTERMA8GA1UEAwwITXlSb290Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
|
||||
AoICAQDdpbLFm3BSkpA22VYdjksOBNmVzM3v26I/Vx9H/j0/k7TTPlQ1bmlvFV5m
|
||||
TiSRgXXuTpw/Sl2x2OpMnIKS11JKf+bsD2jJW+hHDjqykRCjKSec5iAYrBlm28kE
|
||||
SIxAOuG/uGWD2aGUd0WvHCDJgI/rXmjieKncZlyybqukJSGis7ZJ0pSaVQMfAaaW
|
||||
p/lHrsjZawoGi/SJrEwRGsXhCh4eGW9ZGrRmuv8zbp0AFy+YImM/6nysonteBsDl
|
||||
s9Bjiuj3uM+RqKHCMH/sIF92wJ6rnQSIvjJgvNvENHxCdZWy+dzxlP3mYZ62k7ii
|
||||
7D7uyBk9hClwGDbEKtrEXmXhhmPO+f/8uOKyOGdhanNu+3Wl9XJZnVYSIEOveypW
|
||||
4ZD02DBd84mkS5Y4Cq+VDYYRgK8SwfsdyUlxRIz63w1jD6seEguizVlf0J9+G5yW
|
||||
4rBLM8WAAR6n9PGbFPF8j9r6N8ld56gdKzPhXM5/TpZS/egCbtTunIRp+h7MdE7B
|
||||
Ztifh7H9MBCuWx+axgEjjiukAIMhMd0NiUr5iWFQcQLg/q8bNsnZy/AKHhN5NkKp
|
||||
45MRLBgeRNMEBCLc1kOJsbDTGJ7KH1s7fIPATHj0AMF6DZRjO30lJBgUbnL5VOzv
|
||||
uNVdVhrLIV+Az7v3kSffeoHLiIdQEK2U9OYbkWqz+i/a0a3kjwIDAQABo2MwYTAd
|
||||
BgNVHQ4EFgQUl/m3Zgxbmlhqkae98ZdpMNXi1AowHwYDVR0jBBgwFoAUl/m3Zgxb
|
||||
mlhqkae98ZdpMNXi1AowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYw
|
||||
DQYJKoZIhvcNAQELBQADggIBAMB94Md8vcKF9rNrHxE1wcCMPKlPUu13y0MAyc2c
|
||||
fXEWM2R756xg8zNM+InIrJ4olmwqdS79W33Wuk+jCI2zcyb5/YRmaVB+wpCwQoym
|
||||
gDKIFypxxM3A010aDI3Z2IgL4iFtF98NC3MB60xeXQIAkk1TtggnpI8HMTLxF4Lz
|
||||
lZtQXYcBXq7mmLuuQjaP+iWoqzKL6GaL395s3JT8EhgnXB2s7c2/5v3/r+xBbdsX
|
||||
XphUAgOXB9eXTfHDfxeBZSpwDL3LyuSO3VuD4ByxXhykKR9URWwBNSS9CzwemGn3
|
||||
n5wal18L3XDF4g7SSGzbU2tjFAbzDdaUXerwhhtes2LePgx1ukuBWO9ctMrql/WN
|
||||
bV/nL5c/eAfyKQ93oWYa/a09kyIey/YqknKTEcnHVq1rBylwdc6Vp0Wd98y7W2sM
|
||||
gR6n/d41W81a44/q/g7Pxp05byX0EHfmtFjyIGaO+5Y+l1Y6mnT/+lO3ppxgic1m
|
||||
NceB1MZ2mPWZKio+/eTcwF88M2CXUgDTwZUf+pLfP2ow5TCQCSt57ftb4D9BZ/Bg
|
||||
2wDNyEiPLzvpN9eklZiWNp6zPp8WX+RskejMF//8d2lWWnZ4jgppXqWj3DO5fN8v
|
||||
xXVwi/qOGb5f51MMB+uSlp9LdspJYQPJZ9E7WVIV4BBbYcygM60pT0z6EiPK5x99
|
||||
8d9i
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,51 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
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
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1,34 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIF1jCCA76gAwIBAgICEAMwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCU0Ux
|
||||
EjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UECgwJTXlPcmdOYW1lMRkwFwYDVQQL
|
||||
DBBNeUludGVybWVkaWF0ZUNBMRkwFwYDVQQDDBBNeUludGVybWVkaWF0ZUNBMB4X
|
||||
DTIyMDYwNjE2NTYwMVoXDTIzMDYxNjE2NTYwMVowejELMAkGA1UEBhMCU0UxEjAQ
|
||||
BgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJU3RvY2tob2xtMRIwEAYDVQQKDAlN
|
||||
eU9yZ05hbWUxGTAXBgNVBAsMEE15SW50ZXJtZWRpYXRlQ0ExFDASBgNVBAMMC29j
|
||||
c3AuY2xpZW50MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvte8rjsO
|
||||
QmvDS/5Nu+HVwiphq1TmmMnkEXA2J8xar3WUbyuehW4JaWg1c2FBTPG9egGmrZ3Y
|
||||
7x62ZfBCUap2IgyVZjC9F74EMOSTU9Xd2QSsU9QlzAwq6RvHfUQj2mUgEibyT4Mt
|
||||
aqwiIXC7zALXNPr2JYD/08/5ORysiUUt5Yi+vrgfMYntCJ84ZGkoaP4Y/TUf4uuV
|
||||
KJlrZCcQ48g8h1z9mOlxznhm26esn/S0xyuThFEUI2xWySannxSCTqVt/+IDEeVT
|
||||
Iw4ZVLCTnGsoV1weKRKfrI32krnMYrSxZPoAp9ISSGio8eWmtn29O8Z/N87DEFKd
|
||||
WVZCTdq1wb0RlfP6SMAYltFw01hj5UKgj9IqzrndiK7s9svs4HmyVS3TbfwJOLy8
|
||||
GTZOsNxFLbO//yVJbrXHh6YblKHa5YN4/DTiN9yigllAk/Sh3drrnBr7wn7zRD92
|
||||
0GUZPd/MI/e6My4CMcvVBAFFaTH4iu/hpYLMSH4MO7eGNIAvpLuVMNgrDTZ+JbUn
|
||||
XaVbIU8CtrT+MvBFrj1eexGNWtoOA22NxBvXVrJtDV5kBO9mwhCKMnth1wxinCrO
|
||||
XE8wS1YslykIWTEiKZFPSeOhivIojZNSKOrMMpKnh8nglHiuBN2RBottFzuMLAze
|
||||
LIOmkhCKBVbq4wNKZxzgdfQsk9h8FSZXgOkCAwEAAaN1MHMwCQYDVR0TBAIwADAd
|
||||
BgNVHQ4EFgQUMKfAZbTdlLNdtBwAz5GR633YxkwwHwYDVR0jBBgwFoAU116Rt859
|
||||
O5T1nYZ8LHS6gMxppPkwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsG
|
||||
AQUFBwMJMA0GCSqGSIb3DQEBCwUAA4ICAQA/w+fCPxmZTIH6DUmFJeNR1sFGE0yi
|
||||
oaSjOxHsKhOdOnDj9UQekXEYX9qDbooWgElyMS9V65FKg2CkM3c/DD4vqK5yBiy/
|
||||
8iK21XKBV2ZeOQJdfFsZlNOgugc9FNsQhYZJfSsEdZhHyhANLj7hEeNyc1CqosFm
|
||||
inmTPCwyGNYYN3DsGcSb8D4pM3CmMPxnJnZ1EI5jY11Qe4rURSax8KO3cyz5fSTq
|
||||
LCNqpby1PAK5q5GRKGPN3mqKkPym0BXX7ASB8eNCYrkgqGxkHc1riE1rJ9G9xAau
|
||||
6kFgS7lOuEsb12aZ95NT4tdPEXU5+3ute8Zq0PoLn2ejOCPiE0whqzF53QcdRKdO
|
||||
rZ7pPzFpF6Djo+8Q8Q2kYr16KEJ2VQYLKAl5zWW5J8qBUJdG53svp/gt8SqLcvJB
|
||||
HVNBIcVc/PkRF1yUgJSukVxA30bxv3AqSlATUu0f2cQAQaOftn7dL0g+20pnbJ3f
|
||||
i5D9xyNozSuuCIBeutaG2vTh8LXJbauH4xC5eF606oNITuKjituHnUP2h32Q3lwN
|
||||
LiOozmVfHmCz4IdvcW9sbsJu/anwMrnl4JYkBgMfTxAO9juNJOR93SrR4CUoDMaU
|
||||
Xgv43+dH5Xjap/Rw14t9WfHNVI5yCwAc9f95bo98XKng0W71sKLLg2ZTPgNv38p3
|
||||
mGlYiXqsw2ImTw==
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,4 @@
|
|||
V 230616165600Z 1000 unknown /C=SE/ST=Stockholm/L=Stockholm/O=MyOrgName/OU=MyIntermediateCA/CN=localhost
|
||||
V 230616165600Z 1001 unknown /C=SE/ST=Stockholm/L=Stockholm/O=MyOrgName/OU=MyIntermediateCA/CN=MyClient
|
||||
V 230616165601Z 1002 unknown /C=SE/ST=Stockholm/L=Stockholm/O=MyOrgName/OU=MyIntermediateCA/CN=ocsp.server
|
||||
V 230616165601Z 1003 unknown /C=SE/ST=Stockholm/L=Stockholm/O=MyOrgName/OU=MyIntermediateCA/CN=ocsp.client
|
|
@ -0,0 +1,51 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
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
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1,34 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIF+zCCA+OgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwbzELMAkGA1UEBhMCU0Ux
|
||||
EjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJU3RvY2tob2xtMRIwEAYDVQQK
|
||||
DAlNeU9yZ05hbWUxETAPBgNVBAsMCE15Um9vdENBMREwDwYDVQQDDAhNeVJvb3RD
|
||||
QTAeFw0yMjA2MDYxNjU1NTlaFw0zMjA2MDMxNjU1NTlaMGsxCzAJBgNVBAYTAlNF
|
||||
MRIwEAYDVQQIDAlTdG9ja2hvbG0xEjAQBgNVBAoMCU15T3JnTmFtZTEZMBcGA1UE
|
||||
CwwQTXlJbnRlcm1lZGlhdGVDQTEZMBcGA1UEAwwQTXlJbnRlcm1lZGlhdGVDQTCC
|
||||
AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANbHOqvXq/7kNmXX6DW4O6EW
|
||||
/2PBdj/1oPwdIOYo6NfmieCa0BoQ/+W1k+Cj6OR75m0c6JmzPLfUckMvduuW/tJ9
|
||||
aDz/wFFbSYv/bA9f99mNXI0cebAZvYOI+cRBF4nyag3miRbMcCiOPMMDq936F3hd
|
||||
Mi6CL+pIFHif74Q66Ncx10M/nL+E9sEhVyTLrAF0FsMmjkSPdzChb+HByKWRD4f2
|
||||
OYiJxhvJNjLf/+qtn7+A5cwqqbW8zYi6jJPWun24zd2HWHLfehVW8CYzGxBJR7kw
|
||||
KRnPXEuXhnkgEkgfqj2122Jyd35UC/VUpJW1uJJgFNa2w7+bDj7PS6i4kdqjcEHF
|
||||
GqUELN8BUIwyicvJ25KnGt0J5JnHy0rDfJeVv9/6EB1Ekm/tKwtJsKLqLThH5Kql
|
||||
nFM0PUx6OV3nYzXvmmU44SvfIDPy3rhhpVCyaBBzdPyt0ugDkKuwwWcuo6KWSxR1
|
||||
lGTs2YjIYPwnRm8OqvynqCsn0vWUFOluR7suKlUmJFdPpWw04x+v1GZ3YRAig/GL
|
||||
6qtByXDgZ27F4QwNPR5nGMmiu7M2QOE/nJS65A3mjrKeYSD+reQ/WbIWtLuHIhn0
|
||||
XDfHIsuzG6yK9Up1Ske+S1Uos8hTGzvF5NhJlqexd5VJ+VKYLtLkEa/tOj4p/Z5S
|
||||
3nH+tEqg15nE54ovtDMdAgMBAAGjgaQwgaEwHQYDVR0OBBYEFNdekbfOfTuU9Z2G
|
||||
fCx0uoDMaaT5MB8GA1UdIwQYMBaAFJf5t2YMW5pYapGnvfGXaTDV4tQKMBIGA1Ud
|
||||
EwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMDsGA1UdHwQ0MDIwMKAuoCyG
|
||||
Kmh0dHA6Ly9sb2NhbGhvc3Q6OTg3OC9pbnRlcm1lZGlhdGUuY3JsLnBlbTANBgkq
|
||||
hkiG9w0BAQsFAAOCAgEAn6Mw7+BBqzsEEtSDrd97v8niqJdVXPgxyIeHJvdZm1iF
|
||||
3eRmxAUXy1DvD8tCqI8vJoz6p/4ELjedxc/Pcum0OravAElO6+jBOplwZCyhmpl8
|
||||
D4wgh/tbKnOWL20sX6sQESq/dORPf/oPDm9CBu55LaYnNTQHCNtYBZ7UrBhlsdYA
|
||||
CDtsthnYcC4bYTjmm5fYlg/r3LeVN5HINYFUkh3ffKxIpqWdYOhQShOHv2k5hrzM
|
||||
Lg8qu098q3kqJ+2thauXuEpD2HME0HelXaqWokwv9ETtJpuA6xQshJrT+2DjF3CY
|
||||
UY3qdwjWwLr9xjf9DyHmC4hdsaO2ZAqKfMoVi6pcJ4F/20vsTM8063SFxKCbb9aw
|
||||
hpzLWHB2iiKdRs/bczrW7f6XUbCFF2Et6Wl271Q7cyf8GzIdepvGsRGlcl6UaZ40
|
||||
wGfpQTgrCZ/kWHNaii4BAXGc9oj8f4NeKRVybV+cCwboed0cvNqnnKBPuz6iQz9c
|
||||
JzVc7SSgOoAPJ5oh8QYi0vhfVYo34C+ZC+NderWGtMrcQrUzYH1vAv1MQ7Sfxnso
|
||||
VWO7tCIjeBHcZZ+F7t2d4dJEaGYO9eYjIKSq2brFEQ+4Ix124ZWSbluCzQIXuaR3
|
||||
49qRH9Ont5UW4mGBAAgSAbyQxPkOZ3IH9vIFa61jyUE4RKGxkOAwHgajKPpmG9w=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,51 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIJKQIBAAKCAgEAvte8rjsOQmvDS/5Nu+HVwiphq1TmmMnkEXA2J8xar3WUbyue
|
||||
hW4JaWg1c2FBTPG9egGmrZ3Y7x62ZfBCUap2IgyVZjC9F74EMOSTU9Xd2QSsU9Ql
|
||||
zAwq6RvHfUQj2mUgEibyT4MtaqwiIXC7zALXNPr2JYD/08/5ORysiUUt5Yi+vrgf
|
||||
MYntCJ84ZGkoaP4Y/TUf4uuVKJlrZCcQ48g8h1z9mOlxznhm26esn/S0xyuThFEU
|
||||
I2xWySannxSCTqVt/+IDEeVTIw4ZVLCTnGsoV1weKRKfrI32krnMYrSxZPoAp9IS
|
||||
SGio8eWmtn29O8Z/N87DEFKdWVZCTdq1wb0RlfP6SMAYltFw01hj5UKgj9Iqzrnd
|
||||
iK7s9svs4HmyVS3TbfwJOLy8GTZOsNxFLbO//yVJbrXHh6YblKHa5YN4/DTiN9yi
|
||||
gllAk/Sh3drrnBr7wn7zRD920GUZPd/MI/e6My4CMcvVBAFFaTH4iu/hpYLMSH4M
|
||||
O7eGNIAvpLuVMNgrDTZ+JbUnXaVbIU8CtrT+MvBFrj1eexGNWtoOA22NxBvXVrJt
|
||||
DV5kBO9mwhCKMnth1wxinCrOXE8wS1YslykIWTEiKZFPSeOhivIojZNSKOrMMpKn
|
||||
h8nglHiuBN2RBottFzuMLAzeLIOmkhCKBVbq4wNKZxzgdfQsk9h8FSZXgOkCAwEA
|
||||
AQKCAgA8xG0r/Vn5BF9XX05ZOPvbq6sV5x7WH9MjSlu0KHnnzMTK8VS0n+kXSztZ
|
||||
1en5GjB/HO958P0Whu0FYI99eY3MIb2goHxVhwfGmcHfvAW8CKTmvHKcmnM/br0C
|
||||
wiO4cuLXZNdbQiXABbeIdmEWAQGNuSuPnZfYVizBZsP5obGPmQ9Do1UrbQOw7yIB
|
||||
twpLBD3ownW4x9Li7pcneNkD7sp3P8DwY8T9PZFi9+0bscuub821ICwNa33m5wwQ
|
||||
t3MWn8563/iiRidGzkJ6mQ8ni2d5CbgA1BFL3Jha+BaAh5DE2mZDCb0QmWLSQ5Hm
|
||||
IfuTgQ0ZNBkgpW28+J58M9wUKKPkUtafV25fqPOVnynKgdPVlZEH0gUpZg9oQ4+c
|
||||
DTCHkLFIQDCUEIrGk0XyyGLAmqxTqQaD1qNuT76cR+t/SJXCr3CFDp/haJ4Xzl4k
|
||||
YiOWcVMX1gcLY1lz8hExLtg+mVvsHzIP7mvi5MBNWxAsfupUx+cwOZ4+hFBOtxcb
|
||||
/QVbVS0BXWkYBQj92JB1yEws2nA+Yh7Ua4KBlq5vRYcJuo4zDBtL4M56V6B2pcvE
|
||||
4+ZkBSgkqHGukSPg5ThdRpB1spi/oSjfr+dkdYsvQJt/vYejh8JEyfhTCy3m9lbd
|
||||
0cR6VuoahkZKYYcrPwZjIbwtYhrrHb66M0cCidTgC9I9c9CJQQKCAQEA4QBl5ZI/
|
||||
Wlpx6Pwq6YzU1Dsm0Fx2NtXA7kpeMp5o04Ea0gEFrsb4/817KPXWRry2dvNrKgyq
|
||||
+j3ebN9bJ3UZCMhl5CeXS97VHP2rtfK25fHfaH9cz7EpC3Wn5vUTwfkPlQ7PiLS+
|
||||
3SYXEII6Qt4qjWS2xgiJNalUBmW6Ee8A3DXam4q2xyB6FjqjHhs4PRX2qYCgvt5P
|
||||
AAnC20byELIV2NKOC6FBvcpsTbrABUpmUBr+oqz2NGMMKJcOn3mGCyRNB0WuyhfA
|
||||
RoRWbfNYLgd6Jr4pRRt15leVVMi0Yu6nSwrIEebwfYoKD5en4zNh5XcK6C92FDm5
|
||||
MuEXiAENBRFcNQKCAQEA2SKWeZb81y0qsCLcmo6oq+hT0TPLIV+cF4oTnTz8YkK1
|
||||
e6zHO7SPYZUCS45vD2yb/GFWqCaPTCrmKq43IpcO7aThJNQHjlhFC0nfaIrfW0tu
|
||||
SVo5D2/KDHyI98rHLHMAOoM4QIcOa3sB6JIkuFBtJHah2mHpI+hSeToBLzPARN9H
|
||||
+GZWqbVlk5Bkw2eBIpwE5WY3twr6ireutxtIGw5AqM+Xe1cEuCscaCG+bdchp7eW
|
||||
Nbch7cC4Nys9ae6ikqXyOyC534SfY7TGD8d7ptt57Km61iU0vB67qmAJkIpgF9iy
|
||||
xVXtS36SKNzJl8fONFsyJE7PP7GCscu7gikqswmgZQKCAQEAxidole7fln4y9aIn
|
||||
VLovsZ1KiJP1lENZ/0JFhUTXQvXfnfVCgPNA1V+syJ0BEhkehNHJniW76ljUa4Ol
|
||||
rrFE0+RRP8bSSwI7I6YQNFRGWWpTe583KL54TFxY+2D4/oqO+5Iomde8g90I0QQv
|
||||
tTYduZpqESvxUu4GTwAGB8c6NbXECdn3MJVcj/kiMOB9/eY67nqWlqIIZxsxnZX0
|
||||
l1mPPf3pFTr2P+Rmz2nZtI8aXg/4E2JKDkbwrUqjmTUYJPXaVL48ukSClFf2Gi0G
|
||||
irsRC8/LXs5ZXfviSuHbY4mUkdUz7/g38gfA5oyEUeatn73gQC2USymu/a0Y4TEz
|
||||
uzPXxQKCAQEAuwt/glhdj+K5nic5z+5KQGQPJ+ys9B/Pb7ui79VkOCTs/w+0RKti
|
||||
xBW/d2TIKQpPSNZ09r7YvC1MPsH6ftKPqolY5Qe9RpKlT3cge4b9p0BQTeHpu3F6
|
||||
JM12k7ZbYt/h94WoBHYTJuU8nKKf/SJTEpEbFes4EZWEzib4dDfpTarl7YYC64h2
|
||||
aup80pMr+6tY0GAAaK6NaseWOufGcoARlRnWjQpF53xDqTRAPZCPzlFolPcfxBY6
|
||||
2lNmQQWviBJpmyOy2mf9gb9sypT14KO54PPJHcXJKrByyu6V7qw04PXr8e/2TQ1I
|
||||
TOj8w8H70MAqbnpxL5XzVsOA0Dw0KyyEvQKCAQAdWRD7ZYswuaS5dqZi/vJQqFP5
|
||||
/L1U6KCfrzE5UKtXkx5KfcfBhUPpxxRZSuFsknYCcPVGKFJb8dlrp0fec7Sd5sFA
|
||||
vOI2rcgqGUie1oIIAJvUTQg+udvdagSSnUz35wdL3aE1iDqKf92caWLvThTgXC3f
|
||||
4HyxzWBY13vHXqCNAL2YomuusmhVhB7SyHtkwalZjpx+Ao8Ijx7O+2gMoAL7YHOA
|
||||
fhiqNcf4CsKFzyJKPBge87uy3GP2T/k3x0j0OGKkYZIDL3kzeUL9pCSYvg3c4Cqq
|
||||
afpg9Z/FtQaoHrRfOYNYVIhsxgn6ePzGbw3rLiQ9ERAtv424t4lg4r0iQjBb
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1,34 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIF1jCCA76gAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCU0Ux
|
||||
EjAQBgNVBAgMCVN0b2NraG9sbTESMBAGA1UECgwJTXlPcmdOYW1lMRkwFwYDVQQL
|
||||
DBBNeUludGVybWVkaWF0ZUNBMRkwFwYDVQQDDBBNeUludGVybWVkaWF0ZUNBMB4X
|
||||
DTIyMDYwNjE2NTYwMVoXDTIzMDYxNjE2NTYwMVowejELMAkGA1UEBhMCU0UxEjAQ
|
||||
BgNVBAgMCVN0b2NraG9sbTESMBAGA1UEBwwJU3RvY2tob2xtMRIwEAYDVQQKDAlN
|
||||
eU9yZ05hbWUxGTAXBgNVBAsMEE15SW50ZXJtZWRpYXRlQ0ExFDASBgNVBAMMC29j
|
||||
c3Auc2VydmVyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvte8rjsO
|
||||
QmvDS/5Nu+HVwiphq1TmmMnkEXA2J8xar3WUbyuehW4JaWg1c2FBTPG9egGmrZ3Y
|
||||
7x62ZfBCUap2IgyVZjC9F74EMOSTU9Xd2QSsU9QlzAwq6RvHfUQj2mUgEibyT4Mt
|
||||
aqwiIXC7zALXNPr2JYD/08/5ORysiUUt5Yi+vrgfMYntCJ84ZGkoaP4Y/TUf4uuV
|
||||
KJlrZCcQ48g8h1z9mOlxznhm26esn/S0xyuThFEUI2xWySannxSCTqVt/+IDEeVT
|
||||
Iw4ZVLCTnGsoV1weKRKfrI32krnMYrSxZPoAp9ISSGio8eWmtn29O8Z/N87DEFKd
|
||||
WVZCTdq1wb0RlfP6SMAYltFw01hj5UKgj9IqzrndiK7s9svs4HmyVS3TbfwJOLy8
|
||||
GTZOsNxFLbO//yVJbrXHh6YblKHa5YN4/DTiN9yigllAk/Sh3drrnBr7wn7zRD92
|
||||
0GUZPd/MI/e6My4CMcvVBAFFaTH4iu/hpYLMSH4MO7eGNIAvpLuVMNgrDTZ+JbUn
|
||||
XaVbIU8CtrT+MvBFrj1eexGNWtoOA22NxBvXVrJtDV5kBO9mwhCKMnth1wxinCrO
|
||||
XE8wS1YslykIWTEiKZFPSeOhivIojZNSKOrMMpKnh8nglHiuBN2RBottFzuMLAze
|
||||
LIOmkhCKBVbq4wNKZxzgdfQsk9h8FSZXgOkCAwEAAaN1MHMwCQYDVR0TBAIwADAd
|
||||
BgNVHQ4EFgQUMKfAZbTdlLNdtBwAz5GR633YxkwwHwYDVR0jBBgwFoAU116Rt859
|
||||
O5T1nYZ8LHS6gMxppPkwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsG
|
||||
AQUFBwMJMA0GCSqGSIb3DQEBCwUAA4ICAQCT7/RmzBl1TAQIvSFlnmqVd9Ygh1MD
|
||||
kKcLA9akepntTvZ4nlmHZergx8D2CDovT3wKonvmp8pBt83sXDBjZxejET7TXX8j
|
||||
NlvlwZk99/ikWBI0ZA8udl+V15HOVQKF571eeSgRgrsy19YiqLwoK5hZdiYpBLl7
|
||||
zy9EO9X+zggzerAGBDuf87BxAbYChSjVC4PBgsWfGgD0SMehGvVDq90u5FYr8Fd7
|
||||
karVIQYZM3+z/BPqjyX9Ac6bfNFHrxj3cXifu4cLS4Ly0wWNIuNo9t733wfcP6ff
|
||||
PYaibEWsQ7iLH8E5Q/cJ4Pwrwqpxh7PuJlHWe8ObmR2I5YiHIi6dWHwKxveAw3ks
|
||||
Ep56kdEk69jBgT51VzvLhIunSMpf8glPWIL1GGyRyFvDGWeHFPq2zLuEivKlYQpY
|
||||
Nm3xn6yTiO2d6qTgDMTB+nO0mM2dpk8yFepsC4Jsgw6iHh5ofuyXS2GleucxPiii
|
||||
nPvNKFOFPXYXqqScwisnQ2GJjzmLap3SWMRxHt72IyecAQ+e6Bb5WuGM2DOu16in
|
||||
Ds2oXiE3zOPCOyKwA/IOawNBPQvqmNPdDBYVEdYcQelw9KfiRtyyRppl+EIYM4ay
|
||||
ekIKHwN3efb45+TnVAY2ea936vfjyb5C/frpnP8voa6aEkgzOSIxAuLMWQg20Y94
|
||||
jl8CEkTUF4vfqg==
|
||||
-----END CERTIFICATE-----
|
Loading…
Reference in New Issue