Personal MTProto Proxies on Erlang: Policy System and Unique Secrets
MTProto proxies provide access to Telegram under censorship. The traditional model uses a single secret key for all users, leading to issues: lack of access control, inability to analyze user activity, and limited monetization. The solution is personal secrets with a unique SNI domain for each client, implemented in Erlang mtproto_proxy via a policy system.
This implementation scales through O(1) connection validation and runtime-configurable rules. It supports connection limits, access revocation, and activity tracking without server restart.
Advantages of Erlang for High-Load Proxies
Erlang/OTP is ideal for MTProto proxies due to its architecture designed for millions of long-lived connections:
- Process isolation: Each connection is a separate lightweight process (~2 KB). A crash in one does not affect others.
- Auto-scaling: BEAM distributes processes across all CPU cores without manual management.
- Preemptive multitasking: No single client can monopolize a core.
- Bit syntax for parsing: Safe parsing of TLS ClientHello in one line:
parse_client_hello(
<<?TLS_REC_HANDSHAKE, ?TLS_10_VERSION, TlsFrameLen:16,
?TLS_TAG_CLIENT_HELLO, HelloLen:24, ?TLS_12_VERSION,
Random:32/binary,
SessIdLen, SessId:SessIdLen/binary,
CipherSuitesLen:16, CipherSuites:CipherSuitesLen/binary,
CompMethodsLen, CompMethods:CompMethodsLen/binary,
ExtensionsLen:16, Extensions:ExtensionsLen/binary>>
) when TlsFrameLen >= 512, HelloLen >= 508 -> ...
- Live monitoring: Remote console for inspecting tables and states.
- Hot updates: Changing code and configuration without breaking connections.
Total code volume is 3500 lines, less than most alternatives.
SNI and Fake-TLS Secret Format
Server Name Indication (SNI) allows a client to specify a domain in ClientHello before completing the TLS handshake. Fake-TLS mimics TLS 1.3 ClientHello with a legitimate SNI, hiding MTProto inside encrypted fields.
Personal secret format:
0xEE | <16 random bytes> | <SNI domain in UTF-8>
Hex string for t.me/proxy:
ee<32 hex token><hex-encoded SNI>
Validation: HMAC ClientHello with a base secret (one per port). Uniqueness is based on the SNI domain from the extension.
Policy System: Access Rules
Policies are an ordered list of rules for each connection. The first unmet rule rejects the connection.
Rule Types
| Rule | Description |
|------|-------------|
| {in_table, Key, Table} | Key is in Table |
| {not_in_table, Key, Table} | Key is not in Table |
| {max_connections, [Key,...], N} | Connections by keys ≤ N |
Keys from Connection
tls_domain— SNI from fake-TLSport_name— port nameclient_ipv4/client_ipv6— client IP{client_ipv4_subnet, Mask}— subnet
Tables are in-memory hash tables, saved on config reload, lost on restart.
Policy for Personal Proxies
Basic policy:
{policy, [
{in_table, tls_domain, personal_domains},
{max_connections, [tls_domain], 30}
]}
- Whitelist:
tls_domaininpersonal_domains. - Limit: ≤30 simultaneous connections per domain (prevents link sharing).
Additions:
{max_connections, [client_ipv4], 30},
{not_in_table, client_ipv4, ip4_blacklist}
Launching a Standard Personal Proxy
sys.config
{mtproto_proxy, [
{ports, [
#{name => mtp_handler,
listen_ip => "0.0.0.0",
port => 443,
secret => <<"d0d6e111bada5511fcce9584deadbeef">>,
tag => <<"dcbe8f1493fa4cd9ab300891c0b5b326">>}
]},
{allowed_protocols, [mtp_fake_tls]},
{policy, [
{in_table, tls_domain, personal_domains},
{max_connections, [tls_domain], 100}
]}
]}
Domain Registration
mtp_proxy eval '
mtp_policy_table:add(personal_domains, tls_domain, "alice42.example.com").'
Domain activates instantly.
Key Points
- Personal SNI domains enable access control and analytics without changing the base secret.
- Policies are checked in O(1), scaling to thousands of users.
- Erlang ensures connection isolation and zero downtime.
- Policy tables are managed runtime via Erlang RPC.
- A 30-connection limit per domain prevents abuse.
— Editorial Team
No comments yet.