Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add server_name_indication_host config option #3510

Merged
merged 6 commits into from
Jan 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -355,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
Expand All @@ -383,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
Expand Down Expand Up @@ -556,7 +556,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 @@ -577,7 +578,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 @@ -594,7 +596,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 @@ -694,8 +697,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 @@ -710,8 +714,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 @@ -1044,9 +1049,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 @@ -1058,15 +1060,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 @@ -1218,12 +1222,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 @@ -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">>})).

Expand Down Expand Up @@ -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}) ->
Expand Down Expand Up @@ -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}) ->
Expand Down