Merge pull request #1913 from emqx/cert_as_username

Support use certificate as username
This commit is contained in:
turtleDeng 2018-10-26 19:37:20 +08:00 committed by GitHub
commit 71b198f543
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 73 additions and 41 deletions

View File

@ -1159,10 +1159,10 @@ listener.ssl.external.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-G
## Value: on | off ## Value: on | off
## listener.ssl.external.honor_cipher_order = on ## listener.ssl.external.honor_cipher_order = on
## Use the CN field from the client certificate as a username. ## Use the CN, EN or CRT field from the client certificate as a username.
## Notice that 'verify' should be set as 'verify_peer'. ## Notice that 'verify' should be set as 'verify_peer'.
## ##
## Value: cn | en ## Value: cn | en | crt
## listener.ssl.external.peer_cert_as_username = cn ## listener.ssl.external.peer_cert_as_username = cn
## TCP backlog for the SSL connection. ## TCP backlog for the SSL connection.
@ -1522,7 +1522,7 @@ listener.wss.external.certfile = {{ platform_etc_dir }}/certs/cert.pem
## See: listener.ssl.$name.peer_cert_as_username ## See: listener.ssl.$name.peer_cert_as_username
## ##
## Value: cn | dn ## Value: cn | dn | crt
## listener.wss.external.peer_cert_as_username = cn ## listener.wss.external.peer_cert_as_username = cn
## TCP backlog for the WebSocket/SSL connection. ## TCP backlog for the WebSocket/SSL connection.

View File

@ -949,7 +949,7 @@ end}.
]}. ]}.
{mapping, "listener.tcp.$name.peer_cert_as_username", "emqx.listeners", [ {mapping, "listener.tcp.$name.peer_cert_as_username", "emqx.listeners", [
{datatype, {enum, [cn, dn]}} {datatype, {enum, [cn, dn, crt]}}
]}. ]}.
{mapping, "listener.tcp.$name.backlog", "emqx.listeners", [ {mapping, "listener.tcp.$name.backlog", "emqx.listeners", [
@ -1139,7 +1139,7 @@ end}.
]}. ]}.
{mapping, "listener.ssl.$name.peer_cert_as_username", "emqx.listeners", [ {mapping, "listener.ssl.$name.peer_cert_as_username", "emqx.listeners", [
{datatype, {enum, [cn, dn]}} {datatype, {enum, [cn, dn, crt]}}
]}. ]}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
@ -1400,7 +1400,7 @@ end}.
]}. ]}.
{mapping, "listener.wss.$name.peer_cert_as_username", "emqx.listeners", [ {mapping, "listener.wss.$name.peer_cert_as_username", "emqx.listeners", [
{datatype, {enum, [cn, dn]}} {datatype, {enum, [cn, dn, crt]}}
]}. ]}.
{translation, "emqx.listeners", fun(Conf) -> {translation, "emqx.listeners", fun(Conf) ->

View File

@ -33,35 +33,34 @@
-export([shutdown/2]). -export([shutdown/2]).
-record(pstate, { -record(pstate, {
zone, zone,
sendfun, sendfun,
peername, peername,
peercert, peercert,
proto_ver, proto_ver,
proto_name, proto_name,
ackprops, client_id,
client_id, is_assigned,
is_assigned, conn_pid,
conn_pid, conn_props,
conn_props, ack_props,
ack_props, username,
username, session,
session, clean_start,
clean_start, topic_aliases,
topic_aliases, packet_size,
packet_size, will_topic,
will_topic, will_msg,
will_msg, keepalive,
keepalive, mountpoint,
mountpoint, is_super,
is_super, is_bridge,
is_bridge, enable_ban,
enable_ban, enable_acl,
enable_acl, recv_stats,
recv_stats, send_stats,
send_stats, connected,
connected, connected_at
connected_at
}). }).
-type(state() :: #pstate{}). -type(state() :: #pstate{}).
@ -71,6 +70,8 @@
-compile(export_all). -compile(export_all).
-endif. -endif.
-define(NO_PROPS, undefined).
-define(LOG(Level, Format, Args, PState), -define(LOG(Level, Format, Args, PState),
emqx_logger:Level([{client, PState#pstate.client_id}], "MQTT(~s@~s): " ++ Format, emqx_logger:Level([{client, PState#pstate.client_id}], "MQTT(~s@~s): " ++ Format,
[PState#pstate.client_id, esockd_net:format(PState#pstate.peername) | Args])). [PState#pstate.client_id, esockd_net:format(PState#pstate.peername) | Args])).
@ -106,9 +107,10 @@ init(#{peername := Peername, peercert := Peercert, sendfun := SendFun}, Options)
init_username(Peercert, Options) -> init_username(Peercert, Options) ->
case proplists:get_value(peer_cert_as_username, Options) of case proplists:get_value(peer_cert_as_username, Options) of
cn -> esockd_peercert:common_name(Peercert); cn -> esockd_peercert:common_name(Peercert);
dn -> esockd_peercert:subject(Peercert); dn -> esockd_peercert:subject(Peercert);
_ -> undefined crt -> Peercert;
_ -> undefined
end. end.
set_username(Username, PState = #pstate{username = undefined}) -> set_username(Username, PState = #pstate{username = undefined}) ->
@ -603,10 +605,10 @@ send(Packet = ?PACKET(Type), PState = #pstate{proto_ver = Ver, sendfun = SendFun
%%------------------------------------------------------------------------------ %%------------------------------------------------------------------------------
%% Assign a clientid %% Assign a clientid
maybe_assign_client_id(PState = #pstate{client_id = <<>>, ackprops = AckProps}) -> maybe_assign_client_id(PState = #pstate{client_id = <<>>, ack_props = AckProps}) ->
ClientId = emqx_guid:to_base62(emqx_guid:gen()), ClientId = emqx_guid:to_base62(emqx_guid:gen()),
AckProps1 = set_property('Assigned-Client-Identifier', ClientId, AckProps), AckProps1 = set_property('Assigned-Client-Identifier', ClientId, AckProps),
PState#pstate{client_id = ClientId, is_assigned = true, ackprops = AckProps1}; PState#pstate{client_id = ClientId, is_assigned = true, ack_props = AckProps1};
maybe_assign_client_id(PState) -> maybe_assign_client_id(PState) ->
PState. PState.
@ -671,7 +673,7 @@ authenticate(Credentials, Password) ->
{error, Error} {error, Error}
end. end.
set_property(Name, Value, undefined) -> set_property(Name, Value, ?NO_PROPS) ->
#{Name => Value}; #{Name => Value};
set_property(Name, Value, Props) -> set_property(Name, Value, Props) ->
Props#{Name => Value}. Props#{Name => Value}.

View File

@ -0,0 +1,30 @@
%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
-module(emqx_protocol_tests).
-include_lib("eunit/include/eunit.hrl").
set_property_test() ->
?assertEqual(#{test => test_property}, emqx_protocol:set_property(test, test_property, undefined)),
TestMap = #{test => test_property},
?assertEqual(#{test => test_property, test1 => test_property2},
emqx_protocol:set_property(test1, test_property2, TestMap)),
ok.
init_username_test() ->
?assertEqual(<<"Peercert">>,
emqx_protocol:init_username(<<"Peercert">>, [{peer_cert_as_username, crt}])),
?assertEqual(undefined,
emqx_protocol:init_username(undefined, [{peer_cert_as_username, undefined}])).