feat(peer_cert_as_clientid): peer_cert_as_clientid = cn | dn | crt | pem | md5

pem is base64 encoded instead of binary crt

peer_cert_as_username=crt breaks exhook proto utf8 parsing of username

crt cannot be used in a topic name due to being invalid utf8
pem cannot be used in a topic name due to having slashes

peer_cert_as_clientid = md5 allows to e.g. subscribe to "t/%c" with ACL

existing peer_cert_as_username combinations are unaffected.
This commit is contained in:
Benjamin Große 2021-02-15 09:36:21 +01:00 committed by JianBo He
parent dfcc2dd566
commit 28f9b4d519
3 changed files with 53 additions and 13 deletions

View File

@ -1088,10 +1088,18 @@ listener.tcp.external.access.1 = allow all
## Enable the option for X.509 certificate based authentication. ## Enable the option for X.509 certificate based authentication.
## EMQX will use the common name of certificate as MQTT username. ## EMQX will use the common name of certificate as MQTT username.
## 'pem' encodes CRT in base64, and md5 is the md5 hash of CRT.
## ##
## Value: cn | dn | crt ## Value: cn | dn | crt | pem | md5
## listener.tcp.external.peer_cert_as_username = cn ## listener.tcp.external.peer_cert_as_username = cn
## Enable the option for X.509 certificate based authentication.
## EMQX will use the common name of certificate as MQTT clientid.
## 'pem' encodes CRT in base64, and md5 is the md5 hash of CRT.
##
## Value: cn | dn | crt | pem | md5
## listener.tcp.external.peer_cert_as_clientid = cn
## The TCP backlog defines the maximum length that the queue of pending ## The TCP backlog defines the maximum length that the queue of pending
## connections can grow to. ## connections can grow to.
## ##
@ -1443,10 +1451,18 @@ listener.ssl.external.ciphers = TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TL
## Use the CN, DN or CRT field from the client certificate as a username. ## Use the CN, DN 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'.
## 'pem' encodes CRT in base64, and md5 is the md5 hash of CRT.
## ##
## Value: cn | dn | crt ## Value: cn | dn | crt | pem | md5
## listener.ssl.external.peer_cert_as_username = cn ## listener.ssl.external.peer_cert_as_username = cn
## Use the CN, DN or CRT field from the client certificate as a username.
## Notice that 'verify' should be set as 'verify_peer'.
## 'pem' encodes CRT in base64, and md5 is the md5 hash of CRT.
##
## Value: cn | dn | crt | pem | md5
## listener.ssl.external.peer_cert_as_clientid = cn
## TCP backlog for the SSL connection. ## TCP backlog for the SSL connection.
## ##
## See listener.tcp.$name.backlog ## See listener.tcp.$name.backlog
@ -1890,9 +1906,14 @@ listener.wss.external.ciphers = TLS_AES_256_GCM_SHA384,TLS_AES_128_GCM_SHA256,TL
## See: listener.ssl.$name.peer_cert_as_username ## See: listener.ssl.$name.peer_cert_as_username
## ##
## Value: cn | dn | crt ## Value: cn | dn | crt | pem | md5
## listener.wss.external.peer_cert_as_username = cn ## listener.wss.external.peer_cert_as_username = cn
## See: listener.ssl.$name.peer_cert_as_clientid
##
## Value: cn | dn | crt | pem | md5
## listener.wss.external.peer_cert_as_clientid = cn
## TCP backlog for the WebSocket/SSL connection. ## TCP backlog for the WebSocket/SSL connection.
## ##
## See: listener.tcp.$name.backlog ## See: listener.tcp.$name.backlog

View File

@ -1212,7 +1212,11 @@ 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, crt]}} {datatype, {enum, [cn, dn, crt, pem, md5]}}
]}.
{mapping, "listener.tcp.$name.peer_cert_as_clientid", "emqx.listeners", [
{datatype, {enum, [cn, dn, crt, pem, md5]}}
]}. ]}.
{mapping, "listener.tcp.$name.backlog", "emqx.listeners", [ {mapping, "listener.tcp.$name.backlog", "emqx.listeners", [
@ -1426,7 +1430,11 @@ 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, crt]}} {datatype, {enum, [cn, dn, crt, pem, md5]}}
]}.
{mapping, "listener.ssl.$name.peer_cert_as_clientid", "emqx.listeners", [
{datatype, {enum, [cn, dn, crt, pem, md5]}}
]}. ]}.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
@ -1766,7 +1774,11 @@ 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, crt]}} {datatype, {enum, [cn, dn, crt, pem, md5]}}
]}.
{mapping, "listener.wss.$name.peer_cert_as_clientid", "emqx.listeners", [
{datatype, {enum, [cn, dn, crt, pem, md5]}}
]}. ]}.
{mapping, "listener.wss.$name.compress", "emqx.listeners", [ {mapping, "listener.wss.$name.compress", "emqx.listeners", [
@ -1906,6 +1918,7 @@ end}.
{fail_if_no_subprotocol, cuttlefish:conf_get(Prefix ++ ".fail_if_no_subprotocol", Conf, undefined)}, {fail_if_no_subprotocol, cuttlefish:conf_get(Prefix ++ ".fail_if_no_subprotocol", Conf, undefined)},
{supported_subprotocols, string:tokens(cuttlefish:conf_get(Prefix ++ ".supported_subprotocols", Conf, ""), ", ")}, {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_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)},
{compress, cuttlefish:conf_get(Prefix ++ ".compress", Conf, undefined)}, {compress, cuttlefish:conf_get(Prefix ++ ".compress", Conf, undefined)},
{idle_timeout, cuttlefish:conf_get(Prefix ++ ".idle_timeout", Conf, undefined)}, {idle_timeout, cuttlefish:conf_get(Prefix ++ ".idle_timeout", Conf, undefined)},
{max_frame_size, cuttlefish:conf_get(Prefix ++ ".max_frame_size", Conf, undefined)}, {max_frame_size, cuttlefish:conf_get(Prefix ++ ".max_frame_size", Conf, undefined)},

View File

@ -220,13 +220,19 @@ setting_peercert_infos(NoSSL, ClientInfo, _Options)
setting_peercert_infos(Peercert, ClientInfo, Options) -> setting_peercert_infos(Peercert, ClientInfo, Options) ->
{DN, CN} = {esockd_peercert:subject(Peercert), {DN, CN} = {esockd_peercert:subject(Peercert),
esockd_peercert:common_name(Peercert)}, esockd_peercert:common_name(Peercert)},
Username = case proplists:get_value(peer_cert_as_username, Options) of Username = peer_cert_as(peer_cert_as_username, Options, Peercert, DN, CN),
cn -> CN; ClientId = peer_cert_as(peer_cert_as_clientid, Options, Peercert, DN, CN),
dn -> DN; ClientInfo#{username => Username, clientid => ClientId, dn => DN, cn => CN}.
crt -> Peercert;
_ -> undefined peer_cert_as(Key, Options, Peercert, DN, CN) ->
end, case proplists:get_value(Key, Options) of
ClientInfo#{username => Username, dn => DN, cn => CN}. cn -> CN;
dn -> DN;
crt -> Peercert;
pem -> base64:encode(Peercert);
md5 -> emqx_passwd:hash(md5, Peercert);
_ -> undefined
end.
take_ws_cookie(ClientInfo, ConnInfo) -> take_ws_cookie(ClientInfo, ConnInfo) ->
case maps:take(ws_cookie, ConnInfo) of case maps:take(ws_cookie, ConnInfo) of