Skip to content

Commit

Permalink
Merge pull request #3510 from esl/fix-sni
Browse files Browse the repository at this point in the history
Add server_name_indication_host config option
  • Loading branch information
chrzaszcz authored Jan 26, 2022
2 parents 40c7551 + f175761 commit 6f72056
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 22 deletions.
17 changes: 12 additions & 5 deletions doc/configuration/outgoing-connections.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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. It can be specified only when `server_name_indication` is set to `true`.
53 changes: 36 additions & 17 deletions src/config/mongoose_config_spec.erl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
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,
process_http_handler/2,
Expand Down Expand Up @@ -361,8 +361,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
Expand All @@ -389,7 +388,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
Expand Down Expand Up @@ -562,7 +562,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">>) ->
Expand All @@ -583,7 +584,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">>) ->
Expand All @@ -600,7 +602,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">>) ->
Expand Down Expand Up @@ -700,8 +703,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,
Expand All @@ -716,8 +720,9 @@ 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},
<<"versions">> => #list{items = #option{type = atom}}
}.
Expand Down Expand Up @@ -1032,9 +1037,6 @@ get_all_hosts_and_host_types(General) ->
[]
end, General).

process_sni(false) ->
disable.

process_verify_peer(false) -> verify_none;
process_verify_peer(true) -> verify_peer.

Expand All @@ -1046,15 +1048,17 @@ 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].

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;
Expand Down Expand Up @@ -1206,12 +1210,27 @@ 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 `disable` or a string with the SNI host
% as value for `server_name_indication`
SNIKeys = [server_name_indication, server_name_indication_host],
{[SNIOpt, SNIHostOpt], SSLOpts} = proplists:split(KVs, SNIKeys),
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) ->
{[[{user, User}], [{password, Pass}]], []} = proplists:split(KVs, [user, password]),
{User, Pass}.
Expand Down
29 changes: 29 additions & 0 deletions test/config_parser_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -1161,6 +1161,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">>})).

Expand Down Expand Up @@ -3239,6 +3264,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}) ->
Expand Down Expand Up @@ -3283,6 +3310,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}) ->
Expand Down

0 comments on commit 6f72056

Please sign in to comment.