diff --git a/README.md b/README.md index 32b0136..282148c 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,11 @@ Features * Promoted channels! See `mtproto_proxy_app.src` `tag` option. * "secure" randomized-packet-size protocol (34-symbol secrets starting with 'dd') to prevent detection by DPI +* Secure-only mode (only allow connections with 'dd'-secrets). See `allowed_protocols` option. * Multiple ports with unique secret and promo tag for each port * Automatic configuration reload (no need for restarts once per day) -* Very high performance - can handle tens of thousands connections! +* Most of the configuration options might be updated without service restart +* Very high performance - can handle tens of thousands connections! Scales to all CPU cores. * Small codebase compared to oficial one * A lots of metrics could be exported (optional) @@ -118,10 +120,13 @@ and then re-install proxy by ``` sudo make uninstall && make && sudo make install ``` +There are other ways as well. It's even possible to update configuration options +without service restart / without downtime, but it's a bit trickier. ### Change default port / secret / ad tag -To change default settings, change `mtproto_proxy` section as: +To change default settings, change `mtproto_proxy` section of `prod-sys.config` as: + ``` {mtproto_proxy, %% see src/mtproto_proxy.app.src for examples. @@ -171,6 +176,21 @@ To do so, just add more configs to `ports` section, separated by comma, eg: Each section should have unique `name`! +### Only allow connections with 'dd'-secrets + +It might be useful in Iran, where proxies are detected by DPI. +You should disable all protocols other than `mtp_secure` by providing `allowed_protocols` option: + +``` + {mtproto_proxy, + [ + {allowed_protocols, [mtp_secure]}, + {ports, + [#{name => mtp_handler_1, + <..> +``` + + Helpers ------- diff --git a/src/mtp_handler.erl b/src/mtp_handler.erl index c7da37f..12c96c3 100644 --- a/src/mtp_handler.erl +++ b/src/mtp_handler.erl @@ -226,9 +226,9 @@ handle_upstream_data(<>, #state{stage = init, sta S#state{up_codec = UpCodec, up_acc = Rest, stage_state = undefined}); - {error, unknown_protocol} = Err -> + {error, Reason} = Err -> metric:count_inc([?APP, protocol_error, total], - 1, #{labels => [unknown]}), + 1, #{labels => [Reason]}), Err end; handle_upstream_data(Bin, #state{stage = init, stage_state = <<>>} = S) -> diff --git a/src/mtp_obfuscated.erl b/src/mtp_obfuscated.erl index 8a136be..c0df7ed 100644 --- a/src/mtp_obfuscated.erl +++ b/src/mtp_obfuscated.erl @@ -60,25 +60,25 @@ init_down_encrypt(<<_:8/binary, Key:32/binary, IV:16/binary, _/binary>>) -> %% @doc creates new obfuscated stream (MTProto proxy format) --spec from_header(binary(), binary()) -> {ok, integer(), mtp_layer:codec(), codec()}. +-spec from_header(binary(), binary()) -> {ok, integer(), mtp_layer:codec(), codec()} + | {error, unknown_protocol | disabled_protocol}. from_header(Header, Secret) when byte_size(Header) == 64 -> {EncKey, EncIV} = init_up_encrypt(Header, Secret), {DecKey, DecIV} = init_up_decrypt(Header, Secret), St = new(EncKey, EncIV, DecKey, DecIV), {<<_:56/binary, Bin1:8/binary, _/binary>>, St1} = decrypt(Header, St), - case Bin1 of - <<16#ef, 16#ef, 16#ef, 16#ef, _/binary>> -> - DcId = get_dc(Bin1), - {ok, DcId, mtp_abridged, St1}; - <<16#ee, 16#ee, 16#ee, 16#ee, _/binary>> -> - DcId = get_dc(Bin1), - {ok, DcId, mtp_intermediate, St1}; - <<16#dd, 16#dd, 16#dd, 16#dd, _/binary>> -> - DcId = get_dc(Bin1), - {ok, DcId, mtp_secure, St1}; - _ -> - metric:count_inc([?APP, protocol_error, total], 1, #{labels => [unknown]}), - {error, unknown_protocol} + case get_protocol(Bin1) of + {error, unknown_protocol} = Err -> + Err; + Protocol -> + {ok, AllowedProtocols} = application:get_env(?APP, allowed_protocols), + case lists:member(Protocol, AllowedProtocols) of + true -> + DcId = get_dc(Bin1), + {ok, DcId, Protocol, St1}; + false -> + {error, disabled_protocol} + end end. init_up_encrypt(Bin, Secret) -> @@ -94,6 +94,15 @@ init_up_decrypt(Bin, Secret) -> KeyHash = crypto:hash('sha256', <>), {KeyHash, IV}. +get_protocol(<<16#ef, 16#ef, 16#ef, 16#ef, _/binary>>) -> + mtp_abridged; +get_protocol(<<16#ee, 16#ee, 16#ee, 16#ee, _/binary>>) -> + mtp_intermediate; +get_protocol(<<16#dd, 16#dd, 16#dd, 16#dd, _/binary>>) -> + mtp_secure; +get_protocol(_) -> + {error, unknown_protocol}. + get_dc(<<_:4/binary, DcId:16/signed-little-integer, _/binary>>) -> DcId. diff --git a/src/mtproto_proxy.app.src b/src/mtproto_proxy.app.src index 5d35ad4..c784d08 100644 --- a/src/mtproto_proxy.app.src +++ b/src/mtproto_proxy.app.src @@ -44,7 +44,13 @@ %% tag is what you get from @MTProxybot tag => <<"dcbe8f1493fa4cd9ab300891c0b5b326">>}]}, {num_acceptors, 60}, - {max_connections, 40960} + {max_connections, 40960}, + %% It's possible to forbid connection from telegram client to proxy + %% with some of the protocols. Might be useful to set this to + %% only `{allowed_protocols, [mtp_secure]}` if you want to only allow + %% connections to this proxy with "dd"-secrets. Connections by other + %% protocols will be immediately closed. + {allowed_protocols, [mtp_abridged, mtp_intermediate, mtp_secure]} ]}, {modules, []},