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

Merge pull request #1411 from emqtt/emq24

Version 2.3.2
huangdan 8 лет назад
Родитель
Сommit
1f2de7492c
5 измененных файлов с 39 добавлено и 278 удалено
  1. 1 1
      Makefile
  2. 11 0
      etc/emq.conf
  3. 20 4
      priv/emq.schema
  4. 7 14
      src/emqttd_protocol.erl
  5. 0 259
      src/emqttd_ssl.erl

+ 1 - 1
Makefile

@@ -8,7 +8,7 @@ dep_goldrush     = git https://github.com/basho/goldrush 0.1.9
 dep_gproc        = git https://github.com/uwiger/gproc
 dep_getopt       = git https://github.com/jcomellas/getopt v0.8.2
 dep_lager        = git https://github.com/basho/lager master
-dep_esockd       = git https://github.com/emqtt/esockd v5.1
+dep_esockd       = git https://github.com/emqtt/esockd v5.2
 dep_ekka         = git https://github.com/emqtt/ekka master
 dep_mochiweb     = git https://github.com/emqtt/mochiweb v4.2.0
 dep_pbkdf2       = git https://github.com/emqtt/pbkdf2 2.0.1

+ 11 - 0
etc/emq.conf

@@ -340,6 +340,9 @@ listener.tcp.external.access.2 = allow all
 ## listener.tcp.external.proxy_protocol = on
 ## listener.tcp.external.proxy_protocol_timeout = 3s
 
+### Use the PP2_SUBTYPE_SSL_CN field from Proxy Protocol V2 as a username.
+## listener.tcp.external.peer_cert_as_username = cn
+
 ## TCP Socket Options
 listener.tcp.external.backlog = 1024
 
@@ -508,6 +511,10 @@ listener.ws.external.max_clients = 64
 
 listener.ws.external.access.1 = allow all
 
+## Proxy Protocol V1/2
+## listener.ws.external.proxy_protocol = on
+## listener.ws.external.proxy_protocol_timeout = 3s
+
 ## TCP Options
 listener.ws.external.backlog = 1024
 
@@ -534,6 +541,10 @@ listener.wss.external.max_clients = 64
 
 listener.wss.external.access.1 = allow all
 
+## Proxy Protocol V1/2
+## listener.wss.external.proxy_protocol = on
+## listener.wss.external.proxy_protocol_timeout = 3s
+
 ## SSL Options
 listener.wss.external.handshake_timeout = 15s
 

+ 20 - 4
priv/emq.schema

@@ -795,15 +795,17 @@ end}.
 ]}.
 
 {mapping, "listener.tcp.$name.proxy_protocol", "emqttd.listeners", [
-  %%{default, off},
   {datatype, flag}
 ]}.
 
 {mapping, "listener.tcp.$name.proxy_protocol_timeout", "emqttd.listeners", [
-  %%{default, "5s"},
   {datatype, {duration, ms}}
 ]}.
 
+{mapping, "listener.tcp.$name.peer_cert_as_username", "emqttd.listeners", [
+  {datatype, {enum, [cn, dn]}}
+]}.
+
 {mapping, "listener.tcp.$name.backlog", "emqttd.listeners", [
   {datatype, integer},
   {default, 1024}
@@ -879,12 +881,10 @@ end}.
 ]}.
 
 {mapping, "listener.ssl.$name.proxy_protocol", "emqttd.listeners", [
-  %%{default, off},
   {datatype, flag}
 ]}.
 
 {mapping, "listener.ssl.$name.proxy_protocol_timeout", "emqttd.listeners", [
-  %%{default, "5s"},
   {datatype, {duration, ms}}
 ]}.
 
@@ -1011,6 +1011,14 @@ end}.
   {datatype, string}
 ]}.
 
+{mapping, "listener.ws.$name.proxy_protocol", "emqttd.listeners", [
+  {datatype, flag}
+]}.
+
+{mapping, "listener.ws.$name.proxy_protocol_timeout", "emqttd.listeners", [
+  {datatype, {duration, ms}}
+]}.
+
 {mapping, "listener.ws.$name.backlog", "emqttd.listeners", [
   {default, 1024},
   {datatype, integer}
@@ -1084,6 +1092,14 @@ end}.
   {datatype, string}
 ]}.
 
+{mapping, "listener.wss.$name.proxy_protocol", "emqttd.listeners", [
+  {datatype, flag}
+]}.
+
+{mapping, "listener.wss.$name.proxy_protocol_timeout", "emqttd.listeners", [
+  {datatype, {duration, ms}}
+]}.
+
 {mapping, "listener.wss.$name.backlog", "emqttd.listeners", [
   {default, 1024},
   {datatype, integer}

+ 7 - 14
src/emqttd_protocol.erl

@@ -69,7 +69,7 @@ init(Peername, SendFun, Opts) ->
                  max_clientid_len   = MaxLen,
                  is_superuser       = false,
                  client_pid         = self(),
-                 peercert_username  = false,
+                 peercert_username  = undefined,
                  ws_initial_headers = WsInitialHeaders,
                  keepalive_backoff  = Backoff,
                  stats_data         = #proto_stats{enable_stats = EnableStats}}.
@@ -82,23 +82,16 @@ enrich_opt([], _Conn, State) ->
 enrich_opt([{mountpoint, MountPoint} | ConnOpts], Conn, State) ->
     enrich_opt(ConnOpts, Conn, State#proto_state{mountpoint = MountPoint});
 enrich_opt([{peer_cert_as_username, N} | ConnOpts], Conn, State) ->
-    case Conn:type() of
-        ssl -> enrich_opt(ConnOpts, Conn, State#proto_state{
-                                            peercert_username = peercert_username(N, Conn:peercert())});
-        _   -> enrich_opt(ConnOpts, Conn, State)
-    end;
+    enrich_opt(ConnOpts, Conn, State#proto_state{peercert_username = peercert_username(N, Conn)});
 enrich_opt([_ | ConnOpts], Conn, State) ->
     enrich_opt(ConnOpts, Conn, State).
 
-peercert_username(cn, Cert) ->
-    case emqttd_ssl:peer_cert_common_name(Cert) of
-        not_found -> undefined;
-        CN -> iolist_to_binary(CN)
-    end;
-peercert_username(dn, Cert) ->
-    iolist_to_binary(emqttd_ssl:peer_cert_subject(Cert)).
+peercert_username(cn, Conn) ->
+    Conn:peer_cert_common_name();
+peercert_username(dn, Conn) ->
+    Conn:peer_cert_subject().
 
-repl_username_with_peercert(State = #proto_state{peercert_username = false}) ->
+repl_username_with_peercert(State = #proto_state{peercert_username = undefined}) ->
     State;
 repl_username_with_peercert(State = #proto_state{peercert_username = PeerCert}) ->
     State#proto_state{username = PeerCert}.

+ 0 - 259
src/emqttd_ssl.erl

@@ -1,259 +0,0 @@
-%%--------------------------------------------------------------------
-%% Copyright (c) 2013-2017 EMQ Enterprise, Inc. (http://emqtt.io)
-%%
-%% 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.
-%%--------------------------------------------------------------------
-%%
-%% @doc SSL Utility Functions. This module is copied from rabbit_ssl.erl
-%% 
-
--module(emqttd_ssl).
-
--include_lib("public_key/include/public_key.hrl").
-
--type(certificate() :: binary()).
-
--export([peer_cert_issuer/1, peer_cert_subject/1, peer_cert_common_name/1,
-         peer_cert_subject_items/2, peer_cert_validity/1]).
-
-%% Return a string describing the certificate's issuer.
--spec(peer_cert_issuer(certificate()) -> string()).
-peer_cert_issuer(Cert) ->
-    cert_info(fun(#'OTPCertificate' {
-                     tbsCertificate = #'OTPTBSCertificate' {
-                       issuer = Issuer }}) ->
-                      format_rdn_sequence(Issuer)
-              end, Cert).
-
-%% Return a string describing the certificate's subject, as per RFC4514.
--spec(peer_cert_subject(certificate()) -> string()).
-peer_cert_subject(Cert) ->
-    cert_info(fun(#'OTPCertificate' {
-                     tbsCertificate = #'OTPTBSCertificate' {
-                       subject = Subject }}) ->
-                      format_rdn_sequence(Subject)
-              end, Cert).
-
--spec(peer_cert_common_name(certificate()) -> string() | 'not_found').
-peer_cert_common_name(Cert) ->
-    case peer_cert_subject_items(Cert, ?'id-at-commonName') of
-        not_found -> not_found;
-        CNs       -> string:join(CNs, ",")
-     end.
-
-%% Return the parts of the certificate's subject.
--spec(peer_cert_subject_items(certificate(), tuple()) -> [string()] | 'undefined').
-peer_cert_subject_items(Cert, Type) ->
-    cert_info(fun(#'OTPCertificate' {
-                     tbsCertificate = #'OTPTBSCertificate' {
-                       subject = Subject }}) ->
-                      find_by_type(Type, Subject)
-              end, Cert).
-
-%% Return a string describing the certificate's validity.
--spec(peer_cert_validity(certificate()) -> string()).
-peer_cert_validity(Cert) ->
-    cert_info(fun(#'OTPCertificate' {
-                     tbsCertificate = #'OTPTBSCertificate' {
-                       validity = {'Validity', Start, End} }}) ->
-                      format("~s - ~s", [format_asn1_value(Start),
-                                         format_asn1_value(End)])
-              end, Cert).
-
-cert_info(F, {ok, Cert}) ->
-    F(case public_key:pkix_decode_cert(Cert, otp) of
-          {ok, DecCert} -> DecCert; %%pre R14B
-          DecCert       -> DecCert  %%R14B onwards
-      end).
-
-find_by_type(Type, {rdnSequence, RDNs}) ->
-    case [V || #'AttributeTypeAndValue'{type = T, value = V}
-                   <- lists:flatten(RDNs),
-               T == Type] of
-        [] -> not_found;
-        L  -> [format_asn1_value(V) || V <- L]
-    end.
-    
-%%--------------------------------------------------------------------------
-%% Formatting functions.
-%%--------------------------------------------------------------------------
-
-%% Format and rdnSequence as a RFC4514 subject string.
-format_rdn_sequence({rdnSequence, Seq}) ->
-    string:join(lists:reverse([format_complex_rdn(RDN) || RDN <- Seq]), ",").
-
-%% Format an RDN set.
-format_complex_rdn(RDNs) ->
-    string:join([format_rdn(RDN) || RDN <- RDNs], "+").
-
-%% Format an RDN.  If the type name is unknown, use the dotted decimal
-%% representation.  See RFC4514, section 2.3.
-format_rdn(#'AttributeTypeAndValue'{type = T, value = V}) ->
-    FV = escape_rdn_value(format_asn1_value(V)),
-    Fmts = [{?'id-at-surname'                , "SN"},
-            {?'id-at-givenName'              , "GIVENNAME"},
-            {?'id-at-initials'               , "INITIALS"},
-            {?'id-at-generationQualifier'    , "GENERATIONQUALIFIER"},
-            {?'id-at-commonName'             , "CN"},
-            {?'id-at-localityName'           , "L"},
-            {?'id-at-stateOrProvinceName'    , "ST"},
-            {?'id-at-organizationName'       , "O"},
-            {?'id-at-organizationalUnitName' , "OU"},
-            {?'id-at-title'                  , "TITLE"},
-            {?'id-at-countryName'            , "C"},
-            {?'id-at-serialNumber'           , "SERIALNUMBER"},
-            {?'id-at-pseudonym'              , "PSEUDONYM"},
-            {?'id-domainComponent'           , "DC"},
-            {?'id-emailAddress'              , "EMAILADDRESS"},
-            {?'street-address'               , "STREET"},
-            {{0,9,2342,19200300,100,1,1}     , "UID"}], %% Not in public_key.hrl
-    case proplists:lookup(T, Fmts) of
-        {_, Fmt} ->
-            format(Fmt ++ "=~s", [FV]);
-        none when is_tuple(T) ->
-            TypeL = [format("~w", [X]) || X <- tuple_to_list(T)],
-            format("~s=~s", [string:join(TypeL, "."), FV]);
-        none ->
-            format("~p=~s", [T, FV])
-    end.
-
-%% Escape a string as per RFC4514.
-escape_rdn_value(V) ->
-    escape_rdn_value(V, start).
-
-escape_rdn_value([], _) ->
-    [];
-escape_rdn_value([C | S], start) when C =:= $ ; C =:= $# ->
-    [$\\, C | escape_rdn_value(S, middle)];
-escape_rdn_value(S, start) ->
-    escape_rdn_value(S, middle);
-escape_rdn_value([$ ], middle) ->
-    [$\\, $ ];
-escape_rdn_value([C | S], middle) when C =:= $"; C =:= $+; C =:= $,; C =:= $;;
-                                       C =:= $<; C =:= $>; C =:= $\\ ->
-    [$\\, C | escape_rdn_value(S, middle)];
-escape_rdn_value([C | S], middle) when C < 32 ; C >= 126 ->
-    %% Of ASCII characters only U+0000 needs escaping, but for display
-    %% purposes it's handy to escape all non-printable chars. All non-ASCII
-    %% characters get converted to UTF-8 sequences and then escaped. We've
-    %% already got a UTF-8 sequence here, so just escape it.
-    rabbit_misc:format("\\~2.16.0B", [C]) ++ escape_rdn_value(S, middle);
-escape_rdn_value([C | S], middle) ->
-    [C | escape_rdn_value(S, middle)].
-
-%% Get the string representation of an OTPCertificate field.
-format_asn1_value({ST, S}) when ST =:= teletexString; ST =:= printableString;
-                                ST =:= universalString; ST =:= utf8String;
-                                ST =:= bmpString ->
-    format_directory_string(ST, S);
-format_asn1_value({utcTime, [Y1, Y2, M1, M2, D1, D2, H1, H2,
-                             Min1, Min2, S1, S2, $Z]}) ->
-    format("20~c~c-~c~c-~c~cT~c~c:~c~c:~c~cZ",
-           [Y1, Y2, M1, M2, D1, D2, H1, H2, Min1, Min2, S1, S2]);
-%% We appear to get an untagged value back for an ia5string
-%% (e.g. domainComponent).
-format_asn1_value(V) when is_list(V) ->
-    V;
-format_asn1_value(V) when is_binary(V) ->
-    %% OTP does not decode some values when combined with an unknown
-    %% type. That's probably wrong, so as a last ditch effort let's
-    %% try manually decoding. 'DirectoryString' is semi-arbitrary -
-    %% but it is the type which covers the various string types we
-    %% handle below.
-    try
-        {ST, S} = public_key:der_decode('DirectoryString', V),
-        format_directory_string(ST, S)
-    catch _:_ ->
-            format("~p", [V])
-    end;
-format_asn1_value(V) ->
-    format("~p", [V]).
-
-%% DirectoryString { INTEGER : maxSize } ::= CHOICE {
-%%     teletexString     TeletexString (SIZE (1..maxSize)),
-%%     printableString   PrintableString (SIZE (1..maxSize)),
-%%     bmpString         BMPString (SIZE (1..maxSize)),
-%%     universalString   UniversalString (SIZE (1..maxSize)),
-%%     uTF8String        UTF8String (SIZE (1..maxSize)) }
-%%
-%% Precise definitions of printable / teletexString are hard to come
-%% by. This is what I reconstructed:
-%%
-%% printableString:
-%% "intended to represent the limited character sets available to
-%% mainframe input terminals"
-%% A-Z a-z 0-9 ' ( ) + , - . / : = ? [space]
-%% http://msdn.microsoft.com/en-us/library/bb540814(v=vs.85).aspx
-%%
-%% teletexString:
-%% "a sizable volume of software in the world treats TeletexString
-%% (T61String) as a simple 8-bit string with mostly Windows Latin 1
-%% (superset of iso-8859-1) encoding"
-%% http://www.mail-archive.com/asn1@asn1.org/msg00460.html
-%%
-%% (However according to that link X.680 actually defines
-%% TeletexString in some much more involved and crazy way. I suggest
-%% we treat it as ISO-8859-1 since Erlang does not support Windows
-%% Latin 1).
-%%
-%% bmpString:
-%% UCS-2 according to RFC 3641. Hence cannot represent Unicode
-%% characters above 65535 (outside the "Basic Multilingual Plane").
-%%
-%% universalString:
-%% UCS-4 according to RFC 3641.
-%%
-%% utf8String:
-%% UTF-8 according to RFC 3641.
-%%
-%% Within Rabbit we assume UTF-8 encoding. Since printableString is a
-%% subset of ASCII it is also a subset of UTF-8. The others need
-%% converting. Fortunately since the Erlang SSL library does the
-%% decoding for us (albeit into a weird format, see below), we just
-%% need to handle encoding into UTF-8. Note also that utf8Strings come
-%% back as binary.
-%%
-%% Note for testing: the default Ubuntu configuration for openssl will
-%% only create printableString or teletexString types no matter what
-%% you do. Edit string_mask in the [req] section of
-%% /etc/ssl/openssl.cnf to change this (see comments there). You
-%% probably also need to set utf8 = yes to get it to accept UTF-8 on
-%% the command line. Also note I could not get openssl to generate a
-%% universalString.
-
-format_directory_string(printableString, S) -> S;
-format_directory_string(teletexString,   S) -> utf8_list_from(S);
-format_directory_string(bmpString,       S) -> utf8_list_from(S);
-format_directory_string(universalString, S) -> utf8_list_from(S);
-format_directory_string(utf8String,      S) -> binary_to_list(S).
-
-utf8_list_from(S) ->
-    binary_to_list(
-          unicode:characters_to_binary(flatten_ssl_list(S), utf32, utf8)).
-
-%% The Erlang SSL implementation invents its own representation for
-%% non-ascii strings - looking like [97,{0,0,3,187}] (that's LATIN
-%% SMALL LETTER A followed by GREEK SMALL LETTER LAMDA). We convert
-%% this into a list of unicode characters, which we can tell
-%% unicode:characters_to_binary is utf32.
-
-flatten_ssl_list(L) -> [flatten_ssl_list_item(I) || I <- L].
-
-flatten_ssl_list_item({A, B, C, D}) ->
-    A * (1 bsl 24) + B * (1 bsl 16) + C * (1 bsl 8) + D;
-flatten_ssl_list_item(N) when is_number (N) ->
-    N.
-
-format(Fmt, Args) ->
-    lists:flatten(io_lib:format(Fmt, Args)).
-