Просмотр исходного кода

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.
Benjamin Große 5 лет назад
Родитель
Сommit
28f9b4d519
3 измененных файлов с 53 добавлено и 13 удалено
  1. 24 3
      etc/emqx.conf
  2. 16 3
      priv/emqx.schema
  3. 13 7
      src/emqx_channel.erl

+ 24 - 3
etc/emqx.conf

@@ -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

+ 16 - 3
priv/emqx.schema

@@ -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)},

+ 13 - 7
src/emqx_channel.erl

@@ -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