Skip to content

Commit

Permalink
Separate black/whitelists for clients and peers
Browse files Browse the repository at this point in the history
Allow for specifying blacklists/whitelists for TURN clients and TURN
peers separately using the following new options:

- blacklist_clients
- blacklist_peers
- whitelist_clients
- whitelist_peers

The default 'blacklist_peers' list is 'recommended', the other lists are
empty by default.  The special keyword 'default' has been removed.

The old 'blacklist' and 'whitelist' options are deprecated.  For the
moment, any 'blacklist' entries are appended to both the
'blacklist_clients' and 'blacklist_peers' lists, and any 'whitelist'
entries to the 'whitelist_clients' and 'whitelist_peers' list.

Closes #47.
  • Loading branch information
weiss committed Sep 27, 2023
1 parent 469f399 commit eba0cfe
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 55 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,21 @@ All notable changes to this project will be documented in this file. This
project adheres to [Semantic Versioning][SemVer].

## [Unreleased]
### Added
- The new `blacklist_clients` and `blacklist_peers` options may be used to
specify blocklists for TURN clients and TURN peers separately. The old
`blacklist` option that affected both clients and peers has been deprecated.
The same applies to the `whitelist` option, which has been deprecated in favor
of the new `whitelist_clients` and `whitelist_peers` options. By default, the
`blacklist_peers` option is set to a list of networks
[recommended](https://rtcsec.com/article/cve-2020-26262-bypass-of-coturns-access-control-protection/#further-concerns-what-else)
to be blocked. The other three lists are empty by default.

### Changed
- Binary release: Update Erlang/OTP from 26.0.2 to 26.1.
- Binary release: Update OpenSSL from 3.1.2 to 3.1.3.
- Binary release: Update zlib from 1.2.13 to 1.3.
- Binary release: Use new (GCC-13.2-based) version of build toolchain.

### Fixed
- Don't fail to ping the systemd watchdog under certain conditions.
Expand Down
12 changes: 4 additions & 8 deletions config/eturnal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,10 @@ eturnal:
relay_min_port: 49152 # This is the default.
relay_max_port: 65535 # This is the default.

## Reject TURN relaying from/to the following addresses/networks:
blacklist: # This is the default blacklist.
- "127.0.0.0/8" # IPv4 loopback.
- "::1" # IPv6 loopback.
#- recommended # Expands to a number of networks recommended to be
# blocked, but includes private networks. Those
# would have to be 'whitelist'ed if eturnal serves
# local clients/peers within such networks.
## Reject TURN relaying to the following addresses/networks:
blacklist_peers:
- recommended # Expands to various addresses/networks recommended
# to be blocked. This is the default.

## If 'true', close established calls on expiry of temporary TURN credentials:
strict_expiry: false # This is the default.
Expand Down
85 changes: 59 additions & 26 deletions doc/overview.edoc
Original file line number Diff line number Diff line change
Expand Up @@ -112,33 +112,44 @@ behavior of the underlying virtual machine by adding other sections.)
The eturnal server recognizes the following global configuration options.
{@section Module Configuration} options are described below.

=== blacklist ===
=== blacklist_clients ===

<div class="spec">Type: `list of IP addresses/CIDRs' | `default' | `recommended'<br/>
Default: Loopback addresses</div>
<div class="spec">Type: `list of IP addresses/CIDRs'<br/>
Default: `[]'</div>

The `blacklist_clients' option specifies a list of one or more IPv4 and/or IPv6
addresses and/or CIDR blocks. TURN relaying from any of the listed addresses is
refused, unless the address is also matched by a {@section whitelist_clients}
entry.

Example:

```
blacklist_clients:
- "192.0.2.2"
- "203.0.113.0/24"
- "2001:db8::/32"
'''

=== blacklist_peers ===

The `blacklist' option specifies a list of one or more IPv4 and/or IPv6
addresses and/or CIDR blocks. TURN relaying from or to any of the listed
addresses is refused, unless the address is also matched by a {@section
whitelist} entry. The `blacklist' may also contain the special keyword
`default', which expands to the addresses blocked by default, or the special
keyword `recommended', which expands to the addresses <a
<div class="spec">Type: `list of IP addresses/CIDRs' | `recommended'<br/>
Default: `recommended'</div>

The `blacklist_peers' option specifies a list of one or more IPv4 and/or IPv6
addresses and/or CIDR blocks. TURN relaying to any of the listed addresses is
refused, unless the address is also matched by a {@section whitelist_peers}
entry. The list may also contain the special keyword `recommended', which
expands to the addresses <a
href="https://rtcsec.com/article/cve-2020-26262-bypass-of-coturns-access-control-protection/#further-concerns-what-else">suggested</a>
by <a href="https://www.enablesecurity.com">Enable Security</a>. (As the latter
is a superset of the former, there's no point in listing both.) Note that the
`recommended' addresses include private networks. Therefore, if eturnal is
supposed to serve clients within a local, private network, that network must be
exempted from the `recommended' addresses by adding it to the {@section
whitelist}.
by <a href="https://www.enablesecurity.com">Enable Security</a>.

Example:

```
blacklist:
- "::1"
- "127.0.0.0/8"
- "192.0.2.0/24"
- "98.51.100.0/24"
blacklist_peers:
- recommended
- "192.0.2.2"
- "203.0.113.0/24"
- "2001:db8::/32"
'''
Expand Down Expand Up @@ -490,19 +501,41 @@ tls_options:
- cipher_server_preference
'''

=== whitelist ===
=== whitelist_clients ===

<div class="spec">Type: `list of IP addresses/CIDRs'<br/>
Default: `[]'</div>

The `whitelist_clients' option specifies a list of one or more IPv4 and/or IPv6
addresses and/or CIDR blocks. TURN relaying from any of the listed addresses is
permitted even if the address would otherwise be rejected due to being matched
by a {@section blacklist_clients} entry. Note that `0.0.0.0/8', `::/128',
`2001::/32', and `2002::/16' are <em>always</em> blocked and cannot be
whitelisted.

```
whitelist_clients:
- "203.0.113.113"
- "203.0.113.0/26"
- "2001:db8::/64"
'''

=== whitelist_peers ===

<div class="spec">Type: `list of IP addresses/CIDRs'<br/>
Default: `[]'</div>

The `whitelist' option specifies a list of one or more IPv4 and/or IPv6
addresses and/or CIDR blocks. TURN relaying from or to any of the listed
addresses is permitted even if the address would otherwise be rejected due to
being matched by a {@section blacklist} entry.
The `whitelist_peers' option specifies a list of one or more IPv4 and/or IPv6
addresses and/or CIDR blocks. TURN relaying to any of the listed addresses is
permitted even if the address would otherwise be rejected due to being matched
by a {@section blacklist_peers} entry. Note that `0.0.0.0/8', `::/128',
`2001::/32', and `2002::/16' are <em>always</em> blocked and cannot be
whitelisted.

```
whitelist:
whitelist_peers:
- "203.0.113.113"
- "203.0.113.0/26"
- "2001:db8::/64"
'''

Expand Down
6 changes: 3 additions & 3 deletions rebar.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
{<<"prometheus_httpd">>,{pkg,<<"prometheus_httpd">>,<<"2.1.11">>},0},
{<<"quantile_estimator">>,{pkg,<<"quantile_estimator">>,<<"0.2.1">>},2},
{<<"recon">>,{pkg,<<"recon">>,<<"2.5.4">>},0},
{<<"stun">>,{pkg,<<"stun">>,<<"1.2.8">>},0},
{<<"stun">>,{pkg,<<"stun">>,<<"1.2.9">>},0},
{<<"ulitos">>,{pkg,<<"ulitos">>,<<"0.4.0">>},1},
{<<"yval">>,{pkg,<<"yval">>,<<"1.0.10">>},1}]}.
[
Expand All @@ -26,7 +26,7 @@
{<<"prometheus_httpd">>, <<"F616ED9B85B536B195D94104063025A91F904A4CFC20255363F49A197D96C896">>},
{<<"quantile_estimator">>, <<"EF50A361F11B5F26B5F16D0696E46A9E4661756492C981F7B2229EF42FF1CD15">>},
{<<"recon">>, <<"05DD52A119EE4059FA9DAA1AB7CE81BC7A8161A2F12E9D42E9D551FFD2BA901C">>},
{<<"stun">>, <<"620499731F0723E72B97E9967688A7E2992542812EE3CC45E77C7BE42A9BE7AD">>},
{<<"stun">>, <<"ABFAC63693B1CBB2AA318A441BE5B6827730D719177E109326AC99B9224A8E30">>},
{<<"ulitos">>, <<"BCDF0528AF4B59F1CB7D710E4B056688751C600833F31F504FEC4BAA88F0F42B">>},
{<<"yval">>, <<"DE3073183A612C39612EE2970C8B40B465D64A5316D320105E308E011F8646B0">>}]},
{pkg_hash_ext,[
Expand All @@ -41,7 +41,7 @@
{<<"prometheus_httpd">>, <<"0BBE831452CFDF9588538EB2F570B26F30C348ADAE5E95A7D87F35A5910BCF92">>},
{<<"quantile_estimator">>, <<"282A8A323CA2A845C9E6F787D166348F776C1D4A41EDE63046D72D422E3DA946">>},
{<<"recon">>, <<"E9AB01AC7FC8572E41EB59385EFEB3FB0FF5BF02103816535BACAEDF327D0263">>},
{<<"stun">>, <<"5FE531019F7BBB946A752A3D7E5062F8B490DAC06490D78561EBAE549410CE73">>},
{<<"stun">>, <<"22D0CEC7BAC1855820D9449F890D8DEAB0447BBB3799404AFB48F4BCD4AA5BD1">>},
{<<"ulitos">>, <<"AFDCE50A77081EB8D931799A6E015458482EABF9BA8AF367C50CD8660A749B88">>},
{<<"yval">>, <<"0F7EFF50CB24F5F6CC3549FC54BFE4AF58B3FC4553B0829325BAA199C48BA276">>}]}
].
44 changes: 41 additions & 3 deletions src/eturnal.erl
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,9 @@ start_listeners() ->
fun({InKey, OutKey}) ->
opt_filter({OutKey, get_opt(InKey)})
end, opt_map()) ++ [{auth_fun, fun ?MODULE:get_password/2},
{hook_fun, fun ?MODULE:run_hook/2}],
{hook_fun, fun ?MODULE:run_hook/2}]
++ blacklist_opts()
++ whitelist_opts(),
lists:map(
fun({IP, Port, Transport, ProxyProtocol, EnableTURN}) ->
Opts1 = tls_opts(Transport) ++ Opts,
Expand Down Expand Up @@ -460,8 +462,6 @@ opt_map() ->
{max_allocations, turn_max_allocations},
{max_permissions, turn_max_permissions},
{max_bps, shaper},
{blacklist, turn_blacklist},
{whitelist, turn_whitelist},
{realm, auth_realm},
{software_name, server_name}].

Expand All @@ -488,6 +488,40 @@ proxy_opts(true = _ProxyProtocol) ->
proxy_opts(false = _ProxyProtocol) ->
[].

%% This function can be removed in favor of opt_map/0 entries once the
%% 'blacklist' option is removed.
-spec blacklist_opts() -> proplists:proplist().
blacklist_opts() ->
case {eturnal:get_opt(blacklist),
eturnal:get_opt(blacklist_clients),
eturnal:get_opt(blacklist_peers)} of
{[], Clients, Peers} ->
[{turn_blacklist_clients, Clients},
{turn_blacklist_peers, Peers}];
{Blacklist, Clients, Peers} ->
?LOG_WARNING("The 'blacklist' option is deprecated"),
?LOG_WARNING("Use 'blacklist_clients' and/or 'blacklist_peers'"),
[{turn_blacklist_clients, lists:usort(Clients ++ Blacklist)},
{turn_blacklist_peers, lists:usort(Peers ++ Blacklist)}]
end.

%% This function can be removed in favor of opt_map/0 entries once the
%% 'whitelist' option is removed.
-spec whitelist_opts() -> proplists:proplist().
whitelist_opts() ->
case {eturnal:get_opt(whitelist),
eturnal:get_opt(whitelist_clients),
eturnal:get_opt(whitelist_peers)} of
{[], Clients, Peers} ->
[{turn_whitelist_clients, Clients},
{turn_whitelist_peers, Peers}];
{Whitelist, Clients, Peers} ->
?LOG_WARNING("The 'whitelist' option is deprecated"),
?LOG_WARNING("Use 'whitelist_clients' and/or 'whitelist_peers'"),
[{turn_whitelist_clients, lists:usort(Clients ++ Whitelist)},
{turn_whitelist_peers, lists:usort(Peers ++ Whitelist)}]
end.

-spec tls_opts(transport()) -> proplists:proplist().
-ifdef(old_inet_backend).
tls_opts(tls) ->
Expand Down Expand Up @@ -737,6 +771,10 @@ listener_config_changed({Changed, New, Removed} = ConfigChanges) ->
max_bps,
blacklist,
whitelist,
blacklist_clients,
whitelist_clients,
blacklist_peers,
whitelist_peers,
realm,
software_name,
tls_options,
Expand Down
10 changes: 5 additions & 5 deletions src/eturnal_ctl.erl
Original file line number Diff line number Diff line change
Expand Up @@ -270,11 +270,11 @@ filter_sessions(Pred) ->
relay_addr = element(18, State),
perm_addrs = maps:keys(element(12, State)),
peer_addrs = maps:keys(element(10, State)),
sent_bytes = element(30, State),
sent_pkts = element(31, State),
rcvd_bytes = element(28, State),
rcvd_pkts = element(29, State),
start_time = element(32, State)},
sent_bytes = element(32, State),
sent_pkts = element(33, State),
rcvd_bytes = element(30, State),
rcvd_pkts = element(31, State),
start_time = element(34, State)},
case Pred(Session) of
true ->
{true, Session};
Expand Down
25 changes: 15 additions & 10 deletions src/eturnal_yaml.erl
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,9 @@

-include_lib("kernel/include/logger.hrl").

-define(DEFAULT_BLACKLIST,
[{{127, 0, 0, 0}, 8}, % IPv4 loopback.
{{0, 0, 0, 0, 0, 0, 0, 1}, 128}]). % IPv6 loopback.
-define(RECOMMENDED_BLACKLIST,
[{{10, 0, 0, 0}, 8},
[{{127, 0, 0, 0}, 8},
{{10, 0, 0, 0}, 8},
{{100, 64, 0, 0}, 10},
{{169, 254, 0, 0}, 16},
{{172, 16, 0, 0}, 12},
Expand All @@ -43,11 +41,12 @@
{{203, 0, 113, 0}, 24},
{{224, 0, 0, 0}, 4},
{{240, 0, 0, 0}, 4},
{{0, 0, 0, 0, 0, 0, 0, 1}, 128},
{{100, 65435, 0, 0, 0, 0, 0, 0}, 96},
{{256, 0, 0, 0, 0, 0, 0, 0}, 64},
{{64512, 0, 0, 0, 0, 0, 0, 0}, 7},
{{65152, 0, 0, 0, 0, 0, 0, 0}, 10},
{{65280, 0, 0, 0, 0, 0, 0, 0}, 8} | ?DEFAULT_BLACKLIST]).
{{65280, 0, 0, 0, 0, 0, 0, 0}, 8}]).

-type listener() :: {inet:ip_address(), inet:port_number(), eturnal:transport(),
boolean(), boolean()}.
Expand Down Expand Up @@ -78,6 +77,10 @@ validator() ->
end),
blacklist => blacklist_validator(),
whitelist => list_or_single(ip_mask()),
blacklist_clients => list_or_single(ip_mask()),
whitelist_clients => list_or_single(ip_mask()),
blacklist_peers => blacklist_validator(),
whitelist_peers => list_or_single(ip_mask()),
strict_expiry => bool(),
credentials => map(binary(), binary(), [unique, {return, map}]),
realm => non_empty(binary()),
Expand Down Expand Up @@ -105,8 +108,12 @@ validator() ->
max_allocations => 10,
max_permissions => 10,
max_bps => none,
blacklist => ?DEFAULT_BLACKLIST,
blacklist => [],
whitelist => [],
blacklist_clients => [],
whitelist_clients => [],
blacklist_peers => ?RECOMMENDED_BLACKLIST,
whitelist_peers => [],
strict_expiry => false,
credentials => #{},
realm => <<"eturnal.net">>,
Expand All @@ -124,13 +131,11 @@ validator() ->
-spec blacklist_validator() -> yval:validator().
blacklist_validator() ->
and_then(
list_or_single(either(enum([default, recommended]), ip_mask())),
list_or_single(either(recommended, ip_mask())),
fun(L) ->
lists:usort(
lists:flatmap(
fun(default) ->
?DEFAULT_BLACKLIST;
(recommended) ->
fun(recommended) ->
?RECOMMENDED_BLACKLIST;
(Network) ->
[Network]
Expand Down

0 comments on commit eba0cfe

Please sign in to comment.