Forráskód Böngészése

Merge pull request #12970 from zmstone/0503-refactor-dashboard-listener

refactor: simplify https listener config for dashboard
Zaiming (Stone) Shi 1 éve
szülő
commit
e7f0c83406

+ 13 - 2
apps/emqx_dashboard/etc/emqx_dashboard.conf

@@ -1,5 +1,16 @@
 dashboard {
-    listeners.http {
-        bind = 18083
+    listeners {
+        http {
+            ## Comment out 'bind' (or set bind=0) to disable listener.
+            bind = 18083
+        }
+        https {
+            ## Uncomment to enable
+            # bind = 18084
+            ssl_options {
+                certfile = "${EMQX_ETC_DIR}/certs/cert.pem"
+                keyfile = "${EMQX_ETC_DIR}/certs/key.pem"
+            }
+        }
     }
 }

+ 12 - 30
apps/emqx_dashboard/src/emqx_dashboard_schema.erl

@@ -101,8 +101,7 @@ fields("https") ->
         enable(false),
         bind(18084),
         ssl_options()
-        | common_listener_fields() ++
-            hidden_server_ssl_options()
+        | common_listener_fields()
     ];
 fields("ssl_options") ->
     server_ssl_options().
@@ -118,30 +117,8 @@ ssl_options() ->
             }
         )}.
 
-hidden_server_ssl_options() ->
-    lists:map(
-        fun({K, V}) ->
-            {K, V#{
-                importance => ?IMPORTANCE_HIDDEN,
-                default => undefined,
-                required => false
-            }}
-        end,
-        server_ssl_options()
-    ).
-
 server_ssl_options() ->
-    Opts0 = emqx_schema:server_ssl_opts_schema(#{}, true),
-    exclude_fields(["fail_if_no_peer_cert"], Opts0).
-
-exclude_fields([], Fields) ->
-    Fields;
-exclude_fields([FieldName | Rest], Fields) ->
-    %% assert field exists
-    case lists:keytake(FieldName, 1, Fields) of
-        {value, _, New} -> exclude_fields(Rest, New);
-        false -> error({FieldName, Fields})
-    end.
+    emqx_schema:server_ssl_opts_schema(#{}, true).
 
 common_listener_fields() ->
     [
@@ -217,6 +194,7 @@ enable(Bool) ->
             #{
                 default => Bool,
                 required => false,
+                %% deprecated because we use port number =:= 0 to disable
                 deprecated => {since, "5.1.0"},
                 importance => ?IMPORTANCE_HIDDEN,
                 desc => ?DESC(listener_enable)
@@ -300,15 +278,19 @@ validate_sample_interval(Second) ->
             {error, Msg}
     end.
 
-https_converter(Conf = #{<<"ssl_options">> := _}, _Opts) ->
+https_converter(undefined, _Opts) ->
+    %% no https listener configured
+    undefined;
+https_converter(Conf, Opts) ->
+    convert_ssl_layout(Conf, Opts).
+
+convert_ssl_layout(Conf = #{<<"ssl_options">> := _}, _Opts) ->
     Conf;
-https_converter(Conf = #{}, _Opts) ->
+convert_ssl_layout(Conf = #{}, _Opts) ->
     Keys = lists:map(fun({K, _}) -> list_to_binary(K) end, server_ssl_options()),
     SslOpts = maps:with(Keys, Conf),
     Conf1 = maps:without(Keys, Conf),
-    Conf1#{<<"ssl_options">> => SslOpts};
-https_converter(Conf, _Opts) ->
-    Conf.
+    Conf1#{<<"ssl_options">> => SslOpts}.
 
 -if(?EMQX_RELEASE_EDITION == ee).
 sso_fields() ->

+ 23 - 13
apps/emqx_management/test/emqx_mgmt_api_configs_SUITE.erl

@@ -222,7 +222,7 @@ t_dashboard(_Config) ->
     {ok, Dashboard = #{<<"listeners">> := Listeners}} = get_config("dashboard"),
     Https1 = #{enable => true, bind => 18084},
     ?assertMatch(
-        {error, {"HTTP/1.1", 400, _}},
+        {ok, _},
         update_config("dashboard", Dashboard#{<<"listeners">> => Listeners#{<<"https">> => Https1}})
     ),
 
@@ -241,17 +241,20 @@ t_dashboard(_Config) ->
         update_config("dashboard", Dashboard2)
     ),
 
-    KeyFile = emqx_common_test_helpers:app_path(emqx, filename:join(["etc", "certs", "key.pem"])),
-    CertFile = emqx_common_test_helpers:app_path(emqx, filename:join(["etc", "certs", "cert.pem"])),
-    CacertFile = emqx_common_test_helpers:app_path(
-        emqx, filename:join(["etc", "certs", "cacert.pem"])
-    ),
+    FilePath = fun(Name) ->
+        iolist_to_binary(
+            emqx_common_test_helpers:app_path(emqx, filename:join(["etc", "certs", Name]))
+        )
+    end,
+    KeyFile = FilePath("key.pem"),
+    CertFile = FilePath("cert.pem"),
+    CacertFile = FilePath("cacert.pem"),
     Https3 = #{
         <<"bind">> => 18084,
         <<"ssl_options">> => #{
-            <<"keyfile">> => list_to_binary(KeyFile),
-            <<"cacertfile">> => list_to_binary(CacertFile),
-            <<"certfile">> => list_to_binary(CertFile)
+            <<"keyfile">> => KeyFile,
+            <<"cacertfile">> => CacertFile,
+            <<"certfile">> => CertFile
         }
     },
     Dashboard3 = Dashboard#{<<"listeners">> => Listeners#{<<"https">> => Https3}},
@@ -260,16 +263,23 @@ t_dashboard(_Config) ->
     Dashboard4 = Dashboard#{<<"listeners">> => Listeners#{<<"https">> => #{<<"bind">> => 0}}},
     ?assertMatch({ok, _}, update_config("dashboard", Dashboard4)),
     {ok, Dashboard41} = get_config("dashboard"),
-    ?assertEqual(
-        Https3#{<<"bind">> => 0},
+    ?assertMatch(
+        #{
+            <<"bind">> := 0,
+            <<"ssl_options">> :=
+                #{
+                    <<"keyfile">> := KeyFile,
+                    <<"cacertfile">> := CacertFile,
+                    <<"certfile">> := CertFile
+                }
+        },
         read_conf([<<"dashboard">>, <<"listeners">>, <<"https">>]),
         Dashboard41
     ),
 
     ?assertMatch({ok, _}, update_config("dashboard", Dashboard)),
-
     {ok, Dashboard1} = get_config("dashboard"),
-    ?assertNotEqual(Dashboard, Dashboard1),
+    ?assertEqual(Dashboard, Dashboard1),
     timer:sleep(1500),
     ok.
 

+ 21 - 47
rel/config/examples/dashboard-with-https.conf.example

@@ -10,14 +10,32 @@ dashboard {
     cors = false
 
     listeners.https {
-
         ## Port or Address to listen on, 0 means disable
         bind = "0.0.0.0:18084" ## or just a port number, e.g. 18084
 
+        ssl_options {
+            ## PEM format certificates chain.
+            ## Server certificate as the first one,
+            ## followed by its immediate issuer certificate
+            ## then the issuer's issuer certificate, and so on.
+            ## Root CA certificate is optional.
+            ## The path prefix (only prefix) can be an environment variable.
+            certfile = "${EMQX_ETC_DIR}/certs/cert.pem"
+
+            ## PEM format private key
+            keyfile = "${EMQX_ETC_DIR}/certs/key.pem"
+
+            ## Optional. When need to verify client certificates, list trusted client's root CA certificates in this file
+            # cacertfile = "${EMQX_ETC_DIR}/certs/cacert.pem"
+
+            ## Optional. Force client to send their certificate chain during TLS handshake.
+            # fail_if_no_peer_cert = true
+        }
+
         ## Socket acceptor pool size for TCP protocols
         num_acceptors = 8
 
-        ## Maximum number of simultaneous connections
+        ## Maximum number of concurrent connections
         max_connections = 512
 
         ## Defines the maximum length that the queue of pending connections can grow to
@@ -32,51 +50,7 @@ dashboard {
         ## Disable IPv4-to-IPv6 mapping for the listener
         ipv6_v6only = false
 
-        ## Enable support for `HAProxy` header
+        ## Enable support for ProxyProtocol v2 header
         proxy_header = false
-
-        ## Trusted PEM format CA certificates bundle file
-        cacertfile = "data/certs/cacert.pem"
-
-        ## PEM format certificates chain file
-        certfile = "data/certs/cert.pem"
-
-        ## PEM format private key file
-        keyfile = "data/certs/key.pem"
-
-        ## Enable or disable peer verification
-        verify = verify_none  ## use verify_peer to enable
-
-        ## Enable TLS session reuse
-        reuse_sessions = true
-
-        ## Maximum number of non-self-issued intermediate certificates that can follow the peer certificate in a valid certification path
-        depth = 10
-
-        ## Which versions are to be supported
-        versions = [tlsv1.3, tlsv1.2]
-
-        ## TLS cipher suite names
-        ## Note: By default, all available suites are supported, you do not need to set this
-        ciphers = ["TLS_AES_256_GCM_SHA384","TLS_AES_128_GCM_SHA256"]
-
-        ## Allows a client and a server to renegotiate the parameters of the SSL connection on the fly
-        secure_renegotiate = true
-
-        ## Log level for SSL communication
-        ## Type: emergency | alert | critical | error | warning | notice | info | debug | none | all
-        log_level = notice
-
-        ## Hibernate the SSL process after idling for amount of time reducing its memory footprint
-        hibernate_after = 5s
-
-        ## Forces the cipher to be set based on the server-specified order instead of the client-specified order
-        honor_cipher_order = true
-
-        ##  Setting this to false to disable client-initiated renegotiation
-        client_renegotiation = true
-
-        ## Maximum time duration allowed for the handshake to complete
-        handshake_timeout = 15s
     }
 }

+ 3 - 2
rel/i18n/emqx_dashboard_schema.hocon

@@ -7,8 +7,9 @@ backlog.label:
 """Backlog"""
 
 bind.desc:
-"""Port without IP(18083) or port with specified IP(127.0.0.1:18083).
-Disabled when setting bind to `0`."""
+"""Bind the listener to a specified address and port number, for example `127.0.0.1:18083`.
+If configured with just the port number (e.g. `18083`) it's equivalent to binding to all addresses `0.0.0.0`.
+The listener is disabled if `bind` is `0`."""
 
 bind.label:
 """Bind"""