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.
## 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
## 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
## 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.
## 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
## 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.
##
## 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
##
## Value: cn | dn | crt
## Value: cn | dn | crt | pem | md5
## 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.
##
## See: listener.tcp.$name.backlog

View File

@ -1212,7 +1212,11 @@ end}.
]}.
{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", [
@ -1426,7 +1430,11 @@ end}.
]}.
{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", [
{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", [
@ -1906,6 +1918,7 @@ end}.
{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, ""), ", ")},
{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)},
{idle_timeout, cuttlefish:conf_get(Prefix ++ ".idle_timeout", 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) ->
{DN, CN} = {esockd_peercert:subject(Peercert),
esockd_peercert:common_name(Peercert)},
Username = case proplists:get_value(peer_cert_as_username, Options) of
cn -> CN;
dn -> DN;
crt -> Peercert;
_ -> undefined
end,
ClientInfo#{username => Username, dn => DN, cn => CN}.
Username = peer_cert_as(peer_cert_as_username, Options, Peercert, DN, CN),
ClientId = peer_cert_as(peer_cert_as_clientid, Options, Peercert, DN, CN),
ClientInfo#{username => Username, clientid => ClientId, dn => DN, cn => CN}.
peer_cert_as(Key, Options, Peercert, DN, CN) ->
case proplists:get_value(Key, Options) of
cn -> CN;
dn -> DN;
crt -> Peercert;
pem -> base64:encode(Peercert);
md5 -> emqx_passwd:hash(md5, Peercert);
_ -> undefined
end.
take_ws_cookie(ClientInfo, ConnInfo) ->
case maps:take(ws_cookie, ConnInfo) of