From c5869b95e61c4b80eb1c2d11e3bdd79d14e7bba0 Mon Sep 17 00:00:00 2001 From: Gustaw Lippa Date: Mon, 17 Jan 2022 12:58:58 +0100 Subject: [PATCH 1/6] Add server_name_indication_host config option --- src/config/mongoose_config_spec.erl | 36 +++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index be7c5937e4f..dbff6071df4 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -16,6 +16,7 @@ process_listener/2, process_verify_peer/1, process_sni/1, + process_tls_sni/1, process_xmpp_tls/1, process_fast_tls/1, process_http_handler/2, @@ -556,7 +557,8 @@ outgoing_pool_connection(<<"cassandra">>) -> required = all, process = fun ?MODULE:process_cassandra_auth/1}, <<"tls">> => #section{items = tls_items(), - wrap = {kv, ssl}} + wrap = {kv, ssl}, + process = fun ?MODULE:process_tls_sni/1} } }; outgoing_pool_connection(<<"elastic">>) -> @@ -577,7 +579,8 @@ outgoing_pool_connection(<<"http">>) -> <<"request_timeout">> => #option{type = integer, validate = non_negative}, <<"tls">> => #section{items = tls_items(), - wrap = {kv, http_opts}} + wrap = {kv, http_opts}, + process = fun ?MODULE:process_tls_sni/1} } }; outgoing_pool_connection(<<"ldap">>) -> @@ -594,7 +597,8 @@ outgoing_pool_connection(<<"ldap">>) -> <<"connect_interval">> => #option{type = integer, validate = positive}, <<"tls">> => #section{items = tls_items(), - wrap = {kv, tls_options}} + wrap = {kv, tls_options}, + process = fun ?MODULE:process_tls_sni/1} } }; outgoing_pool_connection(<<"rabbit">>) -> @@ -694,8 +698,9 @@ riak_credentials() -> sql_tls() -> Items = tls_items(), #section{ - items = Items#{<<"required">> => #option{type = boolean}} - }. + items = Items#{<<"required">> => #option{type = boolean}}, + process = fun ?MODULE:process_tls_sni/1 + }. tls_items() -> #{<<"verify_peer">> => #option{type = boolean, @@ -712,6 +717,8 @@ tls_items() -> <<"password">> => #option{type = string}, <<"server_name_indication">> => #option{type = boolean, process = fun ?MODULE:process_sni/1}, + <<"server_name_indication_host">> => #option{type = string, + validate = non_empty}, <<"ciphers">> => #option{type = string}, <<"versions">> => #list{items = #option{type = atom}} }. @@ -1045,7 +1052,9 @@ get_all_hosts_and_host_types(General) -> end, General). process_sni(false) -> - disable. + disable; +process_sni(true) -> + enable. process_verify_peer(false) -> verify_none; process_verify_peer(true) -> verify_peer. @@ -1218,12 +1227,25 @@ ssl_opts(pgsql, Opts) -> [{ssl_opts, Opts}]; ssl_opts(mysql, Opts) -> Opts. process_riak_tls(KVs) -> - {[CACertFileOpts], SSLOpts} = proplists:split(KVs, [cacertfile]), + KVsWithSNI = process_tls_sni(KVs), + {[CACertFileOpts], SSLOpts} = proplists:split(KVsWithSNI, [cacertfile]), riak_ssl(SSLOpts) ++ CACertFileOpts. riak_ssl([]) -> []; riak_ssl(Opts) -> [{ssl_opts, Opts}]. +process_tls_sni(KVs) -> + % the SSL library expects either the atom `disabled` or a string with the SNI host + % as value for `server_name_indication` + case proplists:get_value(server_name_indication, KVs, disable) of + enable -> + SNIHost = proplists:get_value(server_name_indication_host, KVs), + KVs1 = proplists:delete(server_name_indication, KVs), + lists:keyreplace(server_name_indication_host, 1, KVs1, {server_name_indication, SNIHost}); + disable -> + proplists:delete(server_name_indication_host, KVs) + end. + process_riak_credentials(KVs) -> {[[{user, User}], [{password, Pass}]], []} = proplists:split(KVs, [user, password]), {User, Pass}. From 6fcce69c8132e18a51ccd3ad13fbefd8df0c5678 Mon Sep 17 00:00:00 2001 From: Gustaw Lippa Date: Thu, 20 Jan 2022 12:28:08 +0100 Subject: [PATCH 2/6] Test server_name_indication_host --- test/config_parser_SUITE.erl | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index e37cd0d614a..d3c39249084 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -1138,6 +1138,31 @@ pool_http_request_timeout(_Config) -> pool_http_tls(_Config) -> ?cfg(pool_config({http, global, default, [], [{http_opts, [{certfile, "cert.pem"} ]}]}), pool_conn_raw(<<"http">>, #{<<"tls">> => #{<<"certfile">> => <<"cert.pem">>}})), + ?cfg(pool_config({http, global, default, [], [{http_opts, [{certfile, "cert.pem"}, + {verify, verify_peer}, + {cacertfile, "priv/ca.pem"}, + {server_name_indication, disable}]}]}), + pool_conn_raw(<<"http">>, #{<<"tls">> => #{<<"certfile">> => <<"cert.pem">>, + <<"verify_peer">> => true, + <<"cacertfile">> => <<"priv/ca.pem">>, + <<"server_name_indication">> => false}})), + ?cfg(pool_config({http, global, default, [], [{http_opts, [{certfile, "cert.pem"}, + {verify, verify_peer}, + {cacertfile, "priv/ca.pem"}, + {server_name_indication, "domain.com"}]}]}), + pool_conn_raw(<<"http">>, #{<<"tls">> => #{<<"certfile">> => <<"cert.pem">>, + <<"verify_peer">> => true, + <<"cacertfile">> => <<"priv/ca.pem">>, + <<"server_name_indication">> => true, + <<"server_name_indication_host">> => <<"domain.com">>}})), + ?cfg(pool_config({http, global, default, [], [{http_opts, [{verify, verify_peer}, + {cacertfile, "priv/ca.pem"}]}]}), + pool_conn_raw(<<"http">>, #{<<"tls">> => #{<<"verify_peer">> => true, + <<"cacertfile">> => <<"priv/ca.pem">>}})), + ?err(pool_conn_raw(<<"http">>, #{<<"tls">> => #{<<"verify_peer">> => true, + <<"cacertfile">> => <<"priv/ca.pem">>, + <<"server_name_indication">> => <<"domain.com">>, + <<"server_name_indication_host">> => <<"domain.com">>}})), ?err(pool_conn_raw(<<"http">>, #{<<"tls">> => #{<<"certfile">> => true}})), ?err(pool_conn_raw(<<"http">>, #{<<"tls">> => <<"secure">>})). @@ -3216,6 +3241,8 @@ handle_listener(V1, V2) -> handle_listener_option({tls, O1}, {tls, O2}) -> compare_unordered_lists(O1, O2); +handle_listener_option({ssl, O1}, {ssl, O2}) -> + compare_unordered_lists(O1, O2); handle_listener_option({modules, M1}, {modules, M2}) -> compare_unordered_lists(M1, M2, fun handle_listener_module/2); handle_listener_option({transport_options, O1}, {transport_options, O2}) -> @@ -3260,6 +3287,8 @@ handle_conn_opt({server, {D1, H1, DB1, U1, P1, O1}}, ?eq(U1, U2), ?eq(P1, P2), compare_unordered_lists(O1, O2, fun handle_db_server_opt/2); +handle_conn_opt({http_opts, O1}, {http_opts, O2}) -> + compare_unordered_lists(O1, O2); handle_conn_opt(V1, V2) -> ?eq(V1, V2). handle_db_server_opt({ssl_opts, O1}, {ssl_opts, O2}) -> From 483b73844ca96f68b892ded501af5ae60d3eefb8 Mon Sep 17 00:00:00 2001 From: Gustaw Lippa Date: Thu, 20 Jan 2022 15:14:33 +0100 Subject: [PATCH 3/6] Document server_name_indication_host option --- doc/configuration/outgoing-connections.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/doc/configuration/outgoing-connections.md b/doc/configuration/outgoing-connections.md index a9c858612d6..b2778694c40 100644 --- a/doc/configuration/outgoing-connections.md +++ b/doc/configuration/outgoing-connections.md @@ -195,7 +195,7 @@ Logical database index (zero-based). ## Riak options -Currently only one Riak connection pool can exist for each supported XMPP host (the default pool). +Currently, only one Riak connection pool can exist for each supported XMPP host (the default pool). !!! WARNING `riak` backend is not compatible with `available_worker` strategy. @@ -245,7 +245,7 @@ Cassandra also supports all TLS-specific options described in the TLS section. ## Elasticsearch options -Currently only one pool tagged `default` can be used. +Currently, only one pool tagged `default` can be used. ### `outgoing_pools.elastic.default.connection.host` * **Syntax:** string @@ -363,7 +363,7 @@ Reconnect interval after a failed connection. * **Default:** `"none"` * **Example:** `encrypt = "tls"` -LDAP also supports all TLS-specific options described in the TLS section (provided `encrypt` is set to `tls`). +LDAP also supports all TLS-specific options described in the TLS section (provided `encrypt` is set to `tls`). ## TLS options @@ -435,7 +435,14 @@ Cipher suites to use. For allowed values, see the [Erlang/OTP SSL documentation] ### `outgoing_pools.*.*.connection.tls.server_name_indication` * **Syntax:** boolean -* **Default:** `true` +* **Default:** `false`, but enabled if the `verify_peer` option is set to `true` * **Example:** `tls.server_name_indication = false` -Enables SNI extension to TLS protocol. +Enables SNI extension to TLS protocol. If set to `true`, the `server_name_indication_host` option should be provided. + +### `outgoing_pools.*.*.connection.tls.server_name_indication_host` +* **Syntax:** string +* **Default:** not set +* **Example:** `tls.server_name_indication_host = "domain.com"` + +Domain against which the certificates will be checked, using SNI. From 1c17815a7cd4d4e751d27048744b6080de99ec89 Mon Sep 17 00:00:00 2001 From: Gustaw Lippa Date: Tue, 25 Jan 2022 12:31:19 +0100 Subject: [PATCH 4/6] Refactor TLS SNI parsing Remove process_sni/1, refactor process_tls_sni/1, enable SNI host in more places. --- src/config/mongoose_config_spec.erl | 37 +++++++++++++---------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index dbff6071df4..d438cbf435f 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -15,7 +15,6 @@ process_ctl_access_rule/1, process_listener/2, process_verify_peer/1, - process_sni/1, process_tls_sni/1, process_xmpp_tls/1, process_fast_tls/1, @@ -356,8 +355,7 @@ c2s_tls() -> validate = non_empty}, wrap = {kv, crlfiles}}, <<"password">> => #option{type = string}, - <<"server_name_indication">> => #option{type = boolean, - process = fun ?MODULE:process_sni/1}, + <<"server_name_indication">> => #option{type = boolean}, <<"versions">> => #list{items = #option{type = atom}} }, process = fun ?MODULE:process_xmpp_tls/1 @@ -384,7 +382,8 @@ http_listener_tls() -> items = Items#{<<"verify_mode">> => #option{type = atom, validate = {enum, [peer, selfsigned_peer, none]}} }, - wrap = {kv, ssl} + wrap = {kv, ssl}, + process = fun ?MODULE:process_tls_sni/1 }. %% path: listen.http[].transport @@ -715,8 +714,7 @@ tls_items() -> <<"keyfile">> => #option{type = string, validate = non_empty}, <<"password">> => #option{type = string}, - <<"server_name_indication">> => #option{type = boolean, - process = fun ?MODULE:process_sni/1}, + <<"server_name_indication">> => #option{type = boolean}, <<"server_name_indication_host">> => #option{type = string, validate = non_empty}, <<"ciphers">> => #option{type = string}, @@ -1051,11 +1049,6 @@ get_all_hosts_and_host_types(General) -> [] end, General). -process_sni(false) -> - disable; -process_sni(true) -> - enable. - process_verify_peer(false) -> verify_none; process_verify_peer(true) -> verify_peer. @@ -1067,7 +1060,7 @@ process_xmpp_tls(KVs) -> end. tls_keys(just_tls) -> - [verify_mode, disconnect_on_failure, crlfiles, password, server_name_indication, versions]; + [verify_mode, disconnect_on_failure, crlfiles, password, server_name_indication, server_name_indication_host, versions]; tls_keys(fast_tls) -> [protocol_options]. @@ -1075,7 +1068,8 @@ common_tls_keys() -> [module, mode, verify_peer, certfile, cacertfile, dhfile, ciphers]. process_xmpp_tls(just_tls, KVs) -> - {[VM, DoF], Opts} = proplists:split(KVs, [verify_mode, disconnect_on_failure]), + KVsWithSNI = process_tls_sni(KVs), + {[VM, DoF], Opts} = proplists:split(KVsWithSNI, [verify_mode, disconnect_on_failure]), {External, Internal} = lists:partition(fun is_external_tls_opt/1, Opts), SSLOpts = ssl_opts(verify_fun(VM, DoF) ++ Internal), [{tls_module, just_tls}] ++ SSLOpts ++ External; @@ -1235,15 +1229,16 @@ riak_ssl([]) -> []; riak_ssl(Opts) -> [{ssl_opts, Opts}]. process_tls_sni(KVs) -> - % the SSL library expects either the atom `disabled` or a string with the SNI host + % the SSL library expects either the atom `disable` or a string with the SNI host % as value for `server_name_indication` - case proplists:get_value(server_name_indication, KVs, disable) of - enable -> - SNIHost = proplists:get_value(server_name_indication_host, KVs), - KVs1 = proplists:delete(server_name_indication, KVs), - lists:keyreplace(server_name_indication_host, 1, KVs1, {server_name_indication, SNIHost}); - disable -> - proplists:delete(server_name_indication_host, KVs) + {[SNIOpt, SNIHostOpt], SSLOpts} = proplists:split(KVs, [server_name_indication, server_name_indication_host]), + case {SNIOpt, SNIHostOpt} of + {[], []} -> + SSLOpts; + {[{server_name_indication, false}], _} -> + [{server_name_indication, disable}] ++ SSLOpts; + {[{server_name_indication, true}], [{server_name_indication_host, SNIHost}]} -> + [{server_name_indication, SNIHost}] ++ SSLOpts end. process_riak_credentials(KVs) -> From 83654806804eacdc4f9967330fd2be9a69e5ef03 Mon Sep 17 00:00:00 2001 From: Gustaw Lippa <34194983+gustawlippa@users.noreply.github.com> Date: Wed, 26 Jan 2022 16:05:38 +0100 Subject: [PATCH 5/6] Update doc/configuration/outgoing-connections.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Paweł Chrząszcz --- doc/configuration/outgoing-connections.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/configuration/outgoing-connections.md b/doc/configuration/outgoing-connections.md index b2778694c40..14cdb56240b 100644 --- a/doc/configuration/outgoing-connections.md +++ b/doc/configuration/outgoing-connections.md @@ -445,4 +445,4 @@ Enables SNI extension to TLS protocol. If set to `true`, the `server_name_indica * **Default:** not set * **Example:** `tls.server_name_indication_host = "domain.com"` -Domain against which the certificates will be checked, using SNI. +Domain against which the certificates will be checked, using SNI. It can be specified only when `server_name_indication` is set to `true`. From f175761b4fca460723a1508ea86a8ccd3b90b05e Mon Sep 17 00:00:00 2001 From: Gustaw Lippa Date: Wed, 26 Jan 2022 16:10:05 +0100 Subject: [PATCH 6/6] Shorten lines --- src/config/mongoose_config_spec.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index d438cbf435f..4e1304364c3 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -1060,7 +1060,8 @@ process_xmpp_tls(KVs) -> end. tls_keys(just_tls) -> - [verify_mode, disconnect_on_failure, crlfiles, password, server_name_indication, server_name_indication_host, versions]; + [verify_mode, disconnect_on_failure, crlfiles, password, server_name_indication, + server_name_indication_host, versions]; tls_keys(fast_tls) -> [protocol_options]. @@ -1231,7 +1232,8 @@ riak_ssl(Opts) -> [{ssl_opts, Opts}]. process_tls_sni(KVs) -> % the SSL library expects either the atom `disable` or a string with the SNI host % as value for `server_name_indication` - {[SNIOpt, SNIHostOpt], SSLOpts} = proplists:split(KVs, [server_name_indication, server_name_indication_host]), + SNIKeys = [server_name_indication, server_name_indication_host], + {[SNIOpt, SNIHostOpt], SSLOpts} = proplists:split(KVs, SNIKeys), case {SNIOpt, SNIHostOpt} of {[], []} -> SSLOpts;