mirror of
https://github.com/seriyps/mtproto_proxy.git
synced 2026-05-13 08:46:46 +00:00
docs: split-mode setup guide, architecture diagrams, cert script, build
README:
- New 'Split-mode setup' section: motivation, firewall rules, step-by-step
instructions for both VPN tunnel and TLS distribution options
- Split-mode bullet added to Features list
- Notes on DPI-resistant tunnels (Shadowsocks, VLESS/XRay, Hysteria2) for
Russian deployment; standard VPN protocols (WireGuard, OpenVPN) may be blocked
- Install instructions updated to use `make init-config` (copies templates,
auto-detects public IP) instead of manual cp; ROLE= documented throughout
- Split-mode Step 4 uses `make ROLE=back/front` so template-change detection
works correctly after `git pull`
Makefile:
- ROLE ?= both variable selects config templates (both/front/back)
- Config prereq rules use $(SYS_CONFIG_SRC) / $(VM_ARGS_SRC) based on ROLE
- New `init-config` target: force-copies templates, auto-detects public IP,
prints edit reminder; replaces manual cp in install workflow
scripts/gen_dist_certs.sh:
- Two-step workflow: `init <dir>` on back server (CA + back cert),
`add-node <dir> <name>` per front server (cert signed by existing CA)
- Generates per-node ssl_dist.<name>.conf with paths substituted (no
NODE_NAME placeholder to edit manually)
- ssl_dist.<name>.conf is now used directly (no rename to ssl_dist.conf);
vm.args examples and README updated to match
config/vm.args.{front,back}.example:
- -ssl_dist_optfile points to role-specific filename (ssl_dist.front.conf /
ssl_dist.back.conf) so cert files can be copied as-is without renaming
AGENTS.md:
- Role-overview Mermaid flowchart showing front/back/both process split
- Data-plane section replaced with links to doc/ (no duplication)
- Supervision tree, key interactions, split-mode config keys updated
doc/handler-downstream-flow.md, doc/migration-flow.md:
- Mermaid box grouping to visually separate FRONT and BACK node participants
- erpc:call reference corrected (was rpc:call)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
42d86517a4
commit
121d8b7413
8 changed files with 487 additions and 62 deletions
118
AGENTS.md
118
AGENTS.md
|
|
@ -29,49 +29,78 @@ Dockerfile Docker image build
|
||||||
| `mtp_secure` | "Secure" randomized-packet-size protocol |
|
| `mtp_secure` | "Secure" randomized-packet-size protocol |
|
||||||
| `mtp_dc_pool` / `mtp_down_conn` | Pooled/multiplexed connections to Telegram DCs |
|
| `mtp_dc_pool` / `mtp_down_conn` | Pooled/multiplexed connections to Telegram DCs |
|
||||||
| `mtp_rpc` | RPC framing protocol between proxy and Telegram |
|
| `mtp_rpc` | RPC framing protocol between proxy and Telegram |
|
||||||
| `mtp_config` | Periodically fetches Telegram DC configuration |
|
| `mtp_config` | Periodically fetches Telegram DC configuration; in split mode exposes `backend_node/0` and remote-aware `get_downstream_pool/1` / `get_default_dc/0` |
|
||||||
| `mtp_policy` / `mtp_policy_table` | Connection limit, blacklist, and whitelist rules |
|
| `mtp_policy` / `mtp_policy_table` | Connection limit, blacklist, and whitelist rules |
|
||||||
| `mtp_codec` / `mtp_aes_cbc` | Codec pipeline (MTProto framing + AES-CBC encryption) |
|
| `mtp_codec` / `mtp_aes_cbc` | Codec pipeline (MTProto framing + AES-CBC encryption) |
|
||||||
| `mtp_abridged` / `mtp_full` / `mtp_intermediate` | MTProto transport codec variants |
|
| `mtp_abridged` / `mtp_full` / `mtp_intermediate` | MTProto transport codec variants |
|
||||||
| `mtp_metric` | Metrics/telemetry |
|
| `mtp_metric` | Metrics/telemetry; `passive_metrics/0` is role-aware |
|
||||||
| `mtp_session_storage` | Replay-attack protection (session deduplication) |
|
| `mtp_session_storage` | Replay-attack protection (session deduplication) |
|
||||||
|
| `mtproto_proxy_sup` | Root supervisor; calls `children(Role)` — children differ by `node_role` |
|
||||||
|
| `mtproto_proxy_app` | OTP application callback; `start/2` and `config_change/3` are role-gated |
|
||||||
|
|
||||||
### Process architecture
|
### Process architecture
|
||||||
|
|
||||||
|
**Role overview** — what starts in each `node_role`:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
subgraph BOTH["node_role=both (single server, default)"]
|
||||||
|
direction TB
|
||||||
|
F0["Ranch listeners\nmtp_handler\nmtp_session_storage\nmtp_policy_*"]
|
||||||
|
B0["mtp_config\nmtp_dc_pool\nmtp_down_conn"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph SPLIT["Split mode (two servers)"]
|
||||||
|
direction TB
|
||||||
|
subgraph FRONT["node_role=front (domestic server)"]
|
||||||
|
F1["Ranch listeners\nmtp_handler\nmtp_session_storage\nmtp_policy_*"]
|
||||||
|
end
|
||||||
|
subgraph BACK["node_role=back (foreign server)"]
|
||||||
|
B1["mtp_config\nmtp_dc_pool\nmtp_down_conn"]
|
||||||
|
end
|
||||||
|
FRONT -- "Erlang distribution\n(TLS or VPN tunnel)" --> BACK
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
OTP supervision tree
|
OTP supervision tree
|
||||||
────────────────────────────────────────────────────────────────────
|
────────────────────────────────────────────────────────────────────
|
||||||
mtproto_proxy_sup (one_for_one)
|
The supervisor is role-parameterised via the `node_role` config key
|
||||||
├── mtp_config (gen_server, singleton)
|
(`front | back | both`, default `both`). Each role starts a different
|
||||||
├── mtp_session_storage (gen_server, singleton)
|
subset of children:
|
||||||
├── mtp_dc_pool_sup (supervisor, simple_one_for_one)
|
|
||||||
│ └── mtp_dc_pool (gen_server, one per DC id, permanent)
|
|
||||||
├── mtp_down_conn_sup (supervisor, simple_one_for_one)
|
|
||||||
│ └── mtp_down_conn (gen_server, one per Telegram TCP conn, temporary)
|
|
||||||
└── Ranch listeners (one per configured port: mtp_ipv4, mtp_ipv6, …)
|
|
||||||
└── mtp_handler (gen_server, one per client TCP conn, transient)
|
|
||||||
|
|
||||||
|
node_role=both (default — single server)
|
||||||
|
├── mtp_config (gen_server, singleton)
|
||||||
|
├── mtp_session_storage (gen_server, singleton)
|
||||||
|
├── mtp_dc_pool_sup (supervisor, simple_one_for_one)
|
||||||
|
│ └── mtp_dc_pool (gen_server, one per DC id, permanent)
|
||||||
|
├── mtp_down_conn_sup (supervisor, simple_one_for_one)
|
||||||
|
│ └── mtp_down_conn (gen_server, one per Telegram TCP conn, temporary)
|
||||||
|
└── Ranch listeners (one per configured port: mtp_ipv4, mtp_ipv6, …)
|
||||||
|
└── mtp_handler (gen_server, one per client TCP conn, transient)
|
||||||
|
|
||||||
Data-plane message flow (one client connection)
|
node_role=front (domestic server — accepts Telegram clients)
|
||||||
────────────────────────────────────────────────────────────────────
|
├── mtp_session_storage
|
||||||
|
├── mtp_policy_table
|
||||||
|
├── mtp_policy_counter
|
||||||
|
└── Ranch listeners → mtp_handler
|
||||||
|
|
||||||
|
node_role=back (foreign server — connects to Telegram DCs)
|
||||||
|
├── mtp_config
|
||||||
|
├── mtp_dc_pool_sup → mtp_dc_pool
|
||||||
|
└── mtp_down_conn_sup → mtp_down_conn
|
||||||
|
|
||||||
|
In split mode, the front node holds `back_node` in its config and
|
||||||
|
addresses back-node processes as `{RegisteredName, BackNode}`.
|
||||||
|
Multiple front nodes can share one back node.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Data-plane message flow** — see [`doc/handler-downstream-flow.md`](doc/handler-downstream-flow.md)
|
||||||
|
for the full sequence diagram (pool lookup, steady-state data exchange, connection release) and
|
||||||
|
[`doc/migration-flow.md`](doc/migration-flow.md) for transparent DC connection rotation.
|
||||||
|
Both diagrams show the front/back node boundary. In `both` mode all processes share the same
|
||||||
|
node and there is no distribution overhead.
|
||||||
|
|
||||||
Telegram client Telegram server
|
|
||||||
│ │
|
|
||||||
│ TCP (fake-TLS / obfuscated / secure) │
|
|
||||||
▼ ▼
|
|
||||||
┌─────────────┐ gen_server:call({send,Data}) ┌──────────────┐ raw TCP ┌──────────────┐
|
|
||||||
│ mtp_handler │ ──────────────────────────────► │ mtp_down_conn│ ────────────► │ Telegram DC │
|
|
||||||
│ (upstream) │ │ (downstream) │ ◄──────────── │ (middle srv) │
|
|
||||||
│ │ ◄────────────────────────────── │ │ └──────────────┘
|
|
||||||
└──────┬──────┘ gen_server:cast({proxy_ans}) └──────────────┘
|
|
||||||
│
|
|
||||||
│ on first data: mtp_config:get_downstream_safe/2 → picks (pool, down_conn)
|
|
||||||
│ on disconnect: cast({return, self()}) → releases slot
|
|
||||||
▼
|
|
||||||
┌─────────────┐
|
|
||||||
│ mtp_dc_pool │ — spawns mtp_down_conn via mtp_down_conn_sup when pool is empty
|
|
||||||
│ (per DC) │
|
|
||||||
└─────────────┘
|
|
||||||
|
|
||||||
> **Naming note:** the terms "upstream" and "downstream" in the current code are the
|
> **Naming note:** the terms "upstream" and "downstream" in the current code are the
|
||||||
> opposite of what one might expect:
|
> opposite of what one might expect:
|
||||||
|
|
@ -80,12 +109,17 @@ Data-plane message flow (one client connection)
|
||||||
> This will be renamed in a future refactor.
|
> This will be renamed in a future refactor.
|
||||||
|
|
||||||
|
|
||||||
Key interactions
|
**Key interactions:**
|
||||||
────────────────────────────────────────────────────────────────────
|
|
||||||
|
```
|
||||||
mtp_handler → mtp_config : get_downstream_safe/2 — resolves DC id to
|
mtp_handler → mtp_config : get_downstream_safe/2 — resolves DC id to
|
||||||
a (pool_pid, down_conn_pid) pair on first
|
a (pool_pid, down_conn_pid) pair on first
|
||||||
upstream data packet
|
upstream data packet.
|
||||||
mtp_handler → mtp_down_conn : send/2 (sync call) — forward client data
|
In split mode returns {PoolName, BackNode};
|
||||||
|
uses erpc:call to check pool existence on
|
||||||
|
the back node.
|
||||||
|
mtp_handler → mtp_down_conn : send/2 (sync call) — forward client data;
|
||||||
|
in split mode this is a cross-node gen_server call
|
||||||
mtp_down_conn → mtp_handler : cast {proxy_ans, …} — forward Telegram reply
|
mtp_down_conn → mtp_handler : cast {proxy_ans, …} — forward Telegram reply
|
||||||
mtp_down_conn → mtp_handler : cast {close_ext, …} — Telegram closed stream
|
mtp_down_conn → mtp_handler : cast {close_ext, …} — Telegram closed stream
|
||||||
mtp_handler → mtp_dc_pool : return/2 (cast) — release slot on disconnect
|
mtp_handler → mtp_dc_pool : return/2 (cast) — release slot on disconnect
|
||||||
|
|
@ -147,9 +181,9 @@ There are three kinds of tests, each with a clear home:
|
||||||
|------|-------|-------------|
|
|------|-------|-------------|
|
||||||
| **EUnit** (unit) | `src/*.erl`, `-ifdef(TEST)` blocks | Pure functions with no I/O: codec encode/decode round-trips, packet parsing helpers, crypto primitives |
|
| **EUnit** (unit) | `src/*.erl`, `-ifdef(TEST)` blocks | Pure functions with no I/O: codec encode/decode round-trips, packet parsing helpers, crypto primitives |
|
||||||
| **PropEr** (property-based) | `test/prop_mtp_<module>.erl` | Codec/parser properties that should hold for *arbitrary* inputs — e.g. encode→decode identity, parser accepts all valid inputs, parser never crashes on random bytes |
|
| **PropEr** (property-based) | `test/prop_mtp_<module>.erl` | Codec/parser properties that should hold for *arbitrary* inputs — e.g. encode→decode identity, parser accepts all valid inputs, parser never crashes on random bytes |
|
||||||
| **Common Test** (integration) | `test/single_dc_SUITE.erl` | End-to-end behaviour involving a real listener + fake DC: protocol negotiation, policy enforcement, error handling visible at the TCP level (alerts sent, connections closed), domain fronting, replay protection |
|
| **Common Test** (integration) | `test/single_dc_SUITE.erl`, `test/split_dc_SUITE.erl` | End-to-end behaviour involving a real listener + fake DC: protocol negotiation, policy enforcement, error handling visible at the TCP level (alerts sent, connections closed), domain fronting, replay protection. `split_dc_SUITE` tests the same paths in split mode (front/back on separate `peer` nodes). |
|
||||||
|
|
||||||
**Rule of thumb:** if the behaviour is observable only over a TCP socket or requires a running application, it belongs in `single_dc_SUITE`. If it is a property of a pure function, add a PropEr property in the matching `prop_mtp_<module>.erl`. If it is a targeted unit case for a specific input, use EUnit.
|
**Rule of thumb:** if the behaviour is observable only over a TCP socket or requires a running application, it belongs in `single_dc_SUITE`. If it requires two nodes communicating over Erlang distribution, it belongs in `split_dc_SUITE`. If it is a property of a pure function, add a PropEr property in the matching `prop_mtp_<module>.erl`. If it is a targeted unit case for a specific input, use EUnit.
|
||||||
|
|
||||||
**What changes need new tests:**
|
**What changes need new tests:**
|
||||||
|
|
||||||
|
|
@ -199,6 +233,18 @@ Workflow:
|
||||||
- All configuration options are documented in `src/mtproto_proxy.app.src`.
|
- All configuration options are documented in `src/mtproto_proxy.app.src`.
|
||||||
- Config can be reloaded without restart: `make update-sysconfig && systemctl reload mtproto-proxy`.
|
- Config can be reloaded without restart: `make update-sysconfig && systemctl reload mtproto-proxy`.
|
||||||
|
|
||||||
|
### Split-mode config keys
|
||||||
|
|
||||||
|
| Key | Node | Meaning |
|
||||||
|
|-----|------|---------|
|
||||||
|
| `node_role` | both | `front \| back \| both` (default `both` — single-server mode) |
|
||||||
|
| `back_node` | front only | Atom name of the back node, e.g. `'back@10.0.0.2'` |
|
||||||
|
| `external_ip` | back | Public IP of the back server (used in the RPC handshake AES key) |
|
||||||
|
|
||||||
|
`mtp_config` is **not started on a front node** — never call `mtp_config:status()` or any
|
||||||
|
`mtp_config` function from code that runs on a front node. `get_downstream_safe/2` is the
|
||||||
|
correct entry point; it is role-aware and dispatches remotely when needed.
|
||||||
|
|
||||||
## Debugging
|
## Debugging
|
||||||
|
|
||||||
### Enabling debug logs for a single module at runtime
|
### Enabling debug logs for a single module at runtime
|
||||||
|
|
|
||||||
31
Makefile
31
Makefile
|
|
@ -6,6 +6,23 @@ EPMD_SERVICE:=$(DESTDIR)/etc/systemd/system/epmd.service
|
||||||
LOGDIR:=$(DESTDIR)/var/log/mtproto-proxy
|
LOGDIR:=$(DESTDIR)/var/log/mtproto-proxy
|
||||||
USER:=mtproto-proxy
|
USER:=mtproto-proxy
|
||||||
|
|
||||||
|
# ROLE selects which config templates are used.
|
||||||
|
# Values: both (default, single server), front (domestic), back (foreign).
|
||||||
|
# For split mode: run `make init-config ROLE=front` / `make init-config ROLE=back`
|
||||||
|
# on each server, edit the resulting config files, then run `make ROLE=front` etc.
|
||||||
|
ROLE ?= both
|
||||||
|
|
||||||
|
ifeq ($(ROLE),front)
|
||||||
|
SYS_CONFIG_SRC := config/sys.config.front.example
|
||||||
|
VM_ARGS_SRC := config/vm.args.front.example
|
||||||
|
else ifeq ($(ROLE),back)
|
||||||
|
SYS_CONFIG_SRC := config/sys.config.back.example
|
||||||
|
VM_ARGS_SRC := config/vm.args.back.example
|
||||||
|
else
|
||||||
|
SYS_CONFIG_SRC := config/sys.config.example
|
||||||
|
VM_ARGS_SRC := config/vm.args.example
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
all: config/prod-sys.config config/prod-vm.args
|
all: config/prod-sys.config config/prod-vm.args
|
||||||
$(REBAR3) as prod release
|
$(REBAR3) as prod release
|
||||||
|
|
@ -19,15 +36,25 @@ test:
|
||||||
$(REBAR3) dialyzer
|
$(REBAR3) dialyzer
|
||||||
$(REBAR3) cover -v
|
$(REBAR3) cover -v
|
||||||
|
|
||||||
config/prod-sys.config: config/sys.config.example
|
config/prod-sys.config: $(SYS_CONFIG_SRC)
|
||||||
[ -f $@ ] && diff -u $@ $^ || true
|
[ -f $@ ] && diff -u $@ $^ || true
|
||||||
cp -i -b $^ $@
|
cp -i -b $^ $@
|
||||||
config/prod-vm.args: config/vm.args.example
|
config/prod-vm.args: $(VM_ARGS_SRC)
|
||||||
[ -f $@ ] && diff -u $@ $^ || true
|
[ -f $@ ] && diff -u $@ $^ || true
|
||||||
cp -i -b $^ $@
|
cp -i -b $^ $@
|
||||||
@IP=$(shell curl -s -4 -m 10 http://ip.seriyps.com || curl -s -4 -m 10 https://digitalresistance.dog/myIp) \
|
@IP=$(shell curl -s -4 -m 10 http://ip.seriyps.com || curl -s -4 -m 10 https://digitalresistance.dog/myIp) \
|
||||||
&& sed -i s/@0\.0\.0\.0/@$${IP}/ $@
|
&& sed -i s/@0\.0\.0\.0/@$${IP}/ $@
|
||||||
|
|
||||||
|
.PHONY: init-config
|
||||||
|
init-config:
|
||||||
|
cp $(SYS_CONFIG_SRC) config/prod-sys.config
|
||||||
|
cp $(VM_ARGS_SRC) config/prod-vm.args
|
||||||
|
@IP=$$(curl -s -4 -m 10 http://ip.seriyps.com || curl -s -4 -m 10 https://digitalresistance.dog/myIp) \
|
||||||
|
&& sed -i s/@0\.0\.0\.0/@$${IP}/ config/prod-vm.args; true
|
||||||
|
@echo ""
|
||||||
|
@echo "Config created from ROLE=$(ROLE) templates."
|
||||||
|
@echo "Edit config/prod-sys.config and config/prod-vm.args, then run: make [ROLE=$(ROLE)]"
|
||||||
|
|
||||||
user:
|
user:
|
||||||
sudo useradd -r $(USER) || true
|
sudo useradd -r $(USER) || true
|
||||||
|
|
||||||
|
|
|
||||||
190
README.md
190
README.md
|
|
@ -37,6 +37,10 @@ Features
|
||||||
* Automatic telegram configuration reload (no need for restarts once per day)
|
* Automatic telegram configuration reload (no need for restarts once per day)
|
||||||
* IPv6 for client connections
|
* IPv6 for client connections
|
||||||
* All configuration options can be updated without service restart
|
* All configuration options can be updated without service restart
|
||||||
|
* Split-mode setup: run the client-facing part (front) on a domestic server and
|
||||||
|
the Telegram-facing part (back) on a foreign server, connected via Erlang distribution
|
||||||
|
(TLS or a censorship-resistant tunnel). Bypasses ISP blocks that target direct
|
||||||
|
domestic→foreign connections. Multiple front servers can share one back server.
|
||||||
* Small codebase compared to official one, code is covered by automated tests
|
* Small codebase compared to official one, code is covered by automated tests
|
||||||
* A lots of metrics could be exported (optional)
|
* A lots of metrics could be exported (optional)
|
||||||
|
|
||||||
|
|
@ -94,7 +98,7 @@ It's ok to provide both `-a dd -a tls` to allow both protocols. If no `-a` optio
|
||||||
### To run with custom config-file
|
### To run with custom config-file
|
||||||
|
|
||||||
1. Get the code `git clone https://github.com/seriyps/mtproto_proxy.git && cd mtproto_proxy/`
|
1. Get the code `git clone https://github.com/seriyps/mtproto_proxy.git && cd mtproto_proxy/`
|
||||||
2. Copy config templates `cp config/{vm.args.example,prod-vm.args}; cp config/{sys.config.example,prod-sys.config}`
|
2. Copy config templates `make init-config` (or manually: `cp config/{vm.args.example,prod-vm.args}; cp config/{sys.config.example,prod-sys.config}`)
|
||||||
3. Edit configs. See [Settings](#settings).
|
3. Edit configs. See [Settings](#settings).
|
||||||
4. Build `docker build -t mtproto-proxy-erl .`
|
4. Build `docker build -t mtproto-proxy-erl .`
|
||||||
5. Start `docker run -d --network=host mtproto-proxy-erl`
|
5. Start `docker run -d --network=host mtproto-proxy-erl`
|
||||||
|
|
@ -112,8 +116,7 @@ You need at least Erlang version 25! Recommended OS is Ubuntu 24.04.
|
||||||
sudo apt install erlang-nox erlang-dev build-essential
|
sudo apt install erlang-nox erlang-dev build-essential
|
||||||
git clone https://github.com/seriyps/mtproto_proxy.git
|
git clone https://github.com/seriyps/mtproto_proxy.git
|
||||||
cd mtproto_proxy/
|
cd mtproto_proxy/
|
||||||
cp config/vm.args.example config/prod-vm.args
|
make init-config # copies templates and auto-detects your server's IP
|
||||||
cp config/sys.config.example config/prod-sys.config
|
|
||||||
# configure your port, secret, ad_tag. See [Settings](#settings) below.
|
# configure your port, secret, ad_tag. See [Settings](#settings) below.
|
||||||
nano config/prod-sys.config
|
nano config/prod-sys.config
|
||||||
make && sudo make install
|
make && sudo make install
|
||||||
|
|
@ -158,7 +161,12 @@ cd mtproto_proxy/
|
||||||
|
|
||||||
### Create config file
|
### Create config file
|
||||||
|
|
||||||
see [Settings](#settings).
|
```bash
|
||||||
|
make init-config # copies sys.config.example → prod-sys.config and vm.args.example → prod-vm.args
|
||||||
|
# also auto-detects your server's public IP
|
||||||
|
```
|
||||||
|
|
||||||
|
Edit `config/prod-sys.config` — see [Settings](#settings) for all options.
|
||||||
|
|
||||||
### Build and install
|
### Build and install
|
||||||
|
|
||||||
|
|
@ -810,3 +818,177 @@ Number of connections
|
||||||
```bash
|
```bash
|
||||||
/opt/mtp_proxy/bin/mtp_proxy eval 'lists:sum([maps:get(all_connections, L) || {_, L} <- maps:to_list(ranch:info())]).'
|
/opt/mtp_proxy/bin/mtp_proxy eval 'lists:sum([maps:get(all_connections, L) || {_, L} <- maps:to_list(ranch:info())]).'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Split-mode setup (front + back)
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
### Why split mode?
|
||||||
|
|
||||||
|
Some censors (e.g. Roskomnadzor) monitor connections from domestic IPs to foreign
|
||||||
|
servers more aggressively than domestic-to-domestic traffic. A common workaround is
|
||||||
|
to split the proxy across two servers:
|
||||||
|
|
||||||
|
- **Front server** — domestic (or neutral) IP, accepts Telegram client connections.
|
||||||
|
- **Back server** — foreign IP, connects to Telegram data centres.
|
||||||
|
|
||||||
|
```
|
||||||
|
Telegram client
|
||||||
|
│
|
||||||
|
▼ (443 / any port)
|
||||||
|
┌────────────┐
|
||||||
|
│ front node │ domestic server (mtp_handler, session storage, policies)
|
||||||
|
└─────┬──────┘
|
||||||
|
│ inter-server link (VPN or TLS)
|
||||||
|
▼
|
||||||
|
┌────────────┐
|
||||||
|
│ back node │ foreign server (DC pool connections to Telegram)
|
||||||
|
└─────┬──────┘
|
||||||
|
│ TCP to Telegram
|
||||||
|
▼
|
||||||
|
Telegram DC
|
||||||
|
```
|
||||||
|
|
||||||
|
Multiple front servers can share one back server — just set the same `back_node`
|
||||||
|
address in each front's config. The DC pools on the back multiplex all client
|
||||||
|
connections regardless of which front they came from.
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Erlang/OTP 25+ installed on **both** servers (same version recommended).
|
||||||
|
- Both servers can reach each other over TCP (the inter-server port, see below).
|
||||||
|
- The back server has outbound TCP access to Telegram's infrastructure (ports
|
||||||
|
are announced dynamically in [Telegram's proxy config](https://core.telegram.org/getProxyConfig).
|
||||||
|
|
||||||
|
### Step 1 — secure the inter-server link
|
||||||
|
|
||||||
|
The two servers communicate using Erlang's built-in distribution protocol, which
|
||||||
|
allows full remote control of the process. **You must restrict access to this
|
||||||
|
channel** to the two proxy servers only. There are two ways to do this:
|
||||||
|
|
||||||
|
#### Option A: Censorship-resistant tunnel (recommended if front is in Russia)
|
||||||
|
|
||||||
|
Standard VPN protocols (WireGuard, OpenVPN) are detectable and blocked on many
|
||||||
|
Russian ISPs. Use a DPI-resistant tunnel instead:
|
||||||
|
|
||||||
|
- **[Shadowsocks](https://shadowsocks.org/)** — widely used, low overhead
|
||||||
|
- **[VLESS/XRay](https://github.com/XTLS/Xray-core)** — highly configurable, very hard to block
|
||||||
|
- **[Hysteria2](https://github.com/apernet/hysteria)** — QUIC-based, good for lossy links
|
||||||
|
|
||||||
|
Set up the tunnel between front and back servers and use the **tunnel interface
|
||||||
|
addresses** in the node names (`front@10.8.0.1`, `back@10.8.0.2`). No extra
|
||||||
|
Erlang config is needed once the tunnel is up.
|
||||||
|
|
||||||
|
> If the front server is **not** in a heavily censored region, WireGuard or
|
||||||
|
> IPsec work equally well and are simpler to set up.
|
||||||
|
|
||||||
|
#### Option B: TLS distribution (no tunnel required)
|
||||||
|
|
||||||
|
If you prefer not to run a separate tunnel, you can secure the distribution link
|
||||||
|
with mutual-TLS certificates. Run on the **back server**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Step 1 — on the back server: initialise CA and generate back cert
|
||||||
|
./scripts/gen_dist_certs.sh init /etc/mtproto-proxy/dist
|
||||||
|
|
||||||
|
# Step 2 — repeat for each front server you add
|
||||||
|
./scripts/gen_dist_certs.sh add-node /etc/mtproto-proxy/dist front
|
||||||
|
# (use a distinct name per front, e.g. front1, front2, …)
|
||||||
|
```
|
||||||
|
Copy to each server (paths already substituted — no editing needed):
|
||||||
|
|
||||||
|
* back server: ca.pem back.pem back.key ssl_dist.back.conf
|
||||||
|
* front server: ca.pem front.pem front.key ssl_dist.front.conf
|
||||||
|
|
||||||
|
Place all files in `/etc/mtproto-proxy/dist/`.
|
||||||
|
Then uncomment `-proto_dist` and `-ssl_dist_optfile` in `vm.args` on each server.
|
||||||
|
|
||||||
|
|
||||||
|
Reference: [Erlang TLS distribution docs](https://www.erlang.org/doc/apps/ssl/ssl_distribution.html)
|
||||||
|
|
||||||
|
### Step 2 — configure the back server
|
||||||
|
|
||||||
|
Run on the **back server**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make init-config ROLE=back
|
||||||
|
```
|
||||||
|
|
||||||
|
This copies `config/sys.config.back.example` → `config/prod-sys.config` and
|
||||||
|
`config/vm.args.back.example` → `config/prod-vm.args`. Edit them:
|
||||||
|
|
||||||
|
- In `vm.args`: set the **back** server's IP: `-name back@<BACK_IP>` and choose
|
||||||
|
a strong cookie string (`-setcookie ...`).
|
||||||
|
- In `sys.config`: set `external_ip` to the back server's public IP, or leave
|
||||||
|
`ip_lookup_services` to auto-detect it.
|
||||||
|
- If using TLS distribution (Option B): uncomment the `-proto_dist` /
|
||||||
|
`-ssl_dist_optfile` lines in `vm.args`.
|
||||||
|
|
||||||
|
### Step 3 — configure the front server
|
||||||
|
|
||||||
|
Run on the **front server**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make init-config ROLE=front
|
||||||
|
```
|
||||||
|
|
||||||
|
This copies `config/sys.config.front.example` → `config/prod-sys.config` and
|
||||||
|
`config/vm.args.front.example` → `config/prod-vm.args`. Edit them:
|
||||||
|
|
||||||
|
- In `vm.args`: set the **front** server's IP: `-name front@<FRONT_IP>` and the
|
||||||
|
**same** cookie string as the back.
|
||||||
|
- In `sys.config`: set `back_node` to the back node name you chose above
|
||||||
|
(e.g. `'back@10.8.0.2'`), configure `ports` / `secret` / `tag` as usual.
|
||||||
|
- If using TLS distribution: uncomment `-proto_dist` / `-ssl_dist_optfile` here too.
|
||||||
|
|
||||||
|
### Step 4 — start in order
|
||||||
|
|
||||||
|
Always **start the back server first**. The front server connects to it on
|
||||||
|
startup; if the back is not yet up the front will log a warning and retry
|
||||||
|
automatically on the next client connection.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# On back server:
|
||||||
|
make ROLE=back && sudo make install && systemctl start mtproto-proxy
|
||||||
|
|
||||||
|
# On front server (after back is up):
|
||||||
|
make ROLE=front && sudo make install && systemctl start mtproto-proxy
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Why `ROLE=` on every `make`?** After `git pull`, if a new release updates
|
||||||
|
> `config/sys.config.back.example`, `make ROLE=back` detects the change (target
|
||||||
|
> is older than its prerequisite), shows a diff, and prompts before overwriting
|
||||||
|
> your `prod-sys.config`. Plain `make` (default `ROLE=both`) compares against
|
||||||
|
> `sys.config.example` instead and silently misses changes to the back/front
|
||||||
|
> templates.
|
||||||
|
|
||||||
|
### Step 5 — verify
|
||||||
|
|
||||||
|
On the front server, check that the back node is visible:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/opt/personal_mtproxy/bin/mtproto_proxy remote_console
|
||||||
|
# In the Erlang shell:
|
||||||
|
nodes(). % should list the back node
|
||||||
|
```
|
||||||
|
|
||||||
|
On the back server, verify DC pools are running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/opt/personal_mtproxy/bin/mtproto_proxy remote_console
|
||||||
|
# In the Erlang shell:
|
||||||
|
mtp_config:status().
|
||||||
|
```
|
||||||
|
|
||||||
|
### Firewall rules
|
||||||
|
|
||||||
|
| Server | Port | Allow from |
|
||||||
|
|--------|-----------------------|---------------|
|
||||||
|
| back | 4369 (EPMD) | front IP only |
|
||||||
|
| back | 9199 (dist) | front IP only |
|
||||||
|
| front | 4369 (EPMD) | back IP only |
|
||||||
|
| front | 9199 (dist) | back IP only |
|
||||||
|
| front | 443 / your proxy port | anywhere |
|
||||||
|
|
||||||
|
If you used a fixed dist port in `vm.args` (`inet_dist_listen_min/max 9199`),
|
||||||
|
only port 9199 needs to be open; otherwise allow the full 9199–9254 range plus
|
||||||
|
EPMD (4369). **Never expose the distribution port to the public internet.**
|
||||||
|
|
|
||||||
|
|
@ -30,15 +30,16 @@
|
||||||
## Option B: TLS distribution (no tunnel required)
|
## Option B: TLS distribution (no tunnel required)
|
||||||
## - Generate certificates: scripts/gen_dist_certs.sh init /etc/mtproto-proxy/dist
|
## - Generate certificates: scripts/gen_dist_certs.sh init /etc/mtproto-proxy/dist
|
||||||
## - Place ca.pem, back.pem, back.key in /etc/mtproto-proxy/dist/
|
## - Place ca.pem, back.pem, back.key in /etc/mtproto-proxy/dist/
|
||||||
## - Deploy ssl_dist.back.conf as /etc/mtproto-proxy/dist/ssl_dist.conf
|
## - Place ssl_dist.back.conf in /etc/mtproto-proxy/dist/ on the back server.
|
||||||
## - On each front node run: scripts/gen_dist_certs.sh add-node /etc/mtproto-proxy/dist front
|
## - On each front node run: scripts/gen_dist_certs.sh add-node /etc/mtproto-proxy/dist front
|
||||||
|
## - Uncomment the lines below:
|
||||||
##
|
##
|
||||||
## -proto_dist inet_tls
|
# -proto_dist inet_tls
|
||||||
## -ssl_dist_optfile /etc/mtproto-proxy/dist/ssl_dist.conf
|
# -ssl_dist_optfile /etc/mtproto-proxy/dist/ssl_dist.back.conf
|
||||||
##
|
##
|
||||||
## Firewall: allow TCP on the distribution port (9199 below) only between
|
## Firewall: allow TCP on the distribution port (9199 below) only between
|
||||||
## the front and back servers, never to the public internet.
|
## the front and back servers, never to the public internet.
|
||||||
##
|
##
|
||||||
## -kernel inet_dist_listen_min 9199
|
# -kernel inet_dist_listen_min 9199
|
||||||
## -kernel inet_dist_listen_max 9199
|
# -kernel inet_dist_listen_max 9199
|
||||||
## -----------------------------------------------------------------------
|
## -----------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -28,17 +28,18 @@
|
||||||
## - If front is NOT in a censored region, WireGuard/IPsec work fine too.
|
## - If front is NOT in a censored region, WireGuard/IPsec work fine too.
|
||||||
##
|
##
|
||||||
## Option B: TLS distribution (no tunnel required)
|
## Option B: TLS distribution (no tunnel required)
|
||||||
## - On back server: scripts/gen_dist_certs.sh init /etc/mtproto-proxy/dist
|
## - On back server: `scripts/gen_dist_certs.sh init /etc/mtproto-proxy/dist`
|
||||||
## - Run per front: scripts/gen_dist_certs.sh add-node /etc/mtproto-proxy/dist front
|
## - Run per front: `scripts/gen_dist_certs.sh add-node /etc/mtproto-proxy/dist front`
|
||||||
## - Place ca.pem, front.pem, front.key in /etc/mtproto-proxy/dist/ here.
|
## - Place ca.pem, front.pem, front.key in /etc/mtproto-proxy/dist/ here.
|
||||||
## - Deploy ssl_dist.front.conf as /etc/mtproto-proxy/dist/ssl_dist.conf
|
## - Place ssl_dist.front.conf in /etc/mtproto-proxy/dist/ on the front server.
|
||||||
|
## - Uncomment the lines below:
|
||||||
##
|
##
|
||||||
## -proto_dist inet_tls
|
# -proto_dist inet_tls
|
||||||
## -ssl_dist_optfile /etc/mtproto-proxy/dist/ssl_dist.conf
|
# -ssl_dist_optfile /etc/mtproto-proxy/dist/ssl_dist.front.conf
|
||||||
##
|
##
|
||||||
## Firewall: allow TCP on the distribution port (9199 below) only between
|
## Firewall: allow TCP on the distribution port (9199 below) only between
|
||||||
## the front and back servers, never to the public internet.
|
## the front and back servers, never to the public internet.
|
||||||
##
|
##
|
||||||
## -kernel inet_dist_listen_min 9199
|
# -kernel inet_dist_listen_min 9199
|
||||||
## -kernel inet_dist_listen_max 9199
|
# -kernel inet_dist_listen_max 9199
|
||||||
## -----------------------------------------------------------------------
|
## -----------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -9,19 +9,30 @@ and the steady-state data flow that follows.
|
||||||
- `mtp_down_conn` — multiplexed TCP connection to a Telegram DC
|
- `mtp_down_conn` — multiplexed TCP connection to a Telegram DC
|
||||||
- `Telegram DC` — the upstream Telegram data-centre server
|
- `Telegram DC` — the upstream Telegram data-centre server
|
||||||
|
|
||||||
|
In **split mode** (`node_role = front / back`) `mtp_handler` runs on the front
|
||||||
|
node and `mtp_dc_pool` / `mtp_down_conn` run on the back node. The pool is
|
||||||
|
addressed as `{mtp_dc_pool_N, BackNode}` — Erlang distribution makes the
|
||||||
|
`gen_server:call` and all subsequent casts transparent across nodes. Multiple
|
||||||
|
front nodes can share the same back node; the pools multiplex over all
|
||||||
|
upstream connections regardless of origin.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
participant Client as Telegram client
|
participant Client as Telegram client
|
||||||
participant Handler as mtp_handler
|
box LightBlue "FRONT node"
|
||||||
participant Pool as mtp_dc_pool
|
participant Handler as mtp_handler
|
||||||
participant Down as mtp_down_conn
|
end
|
||||||
|
box LightGreen "BACK node"
|
||||||
|
participant Pool as mtp_dc_pool
|
||||||
|
participant Down as mtp_down_conn
|
||||||
|
end
|
||||||
participant TG as Telegram DC
|
participant TG as Telegram DC
|
||||||
|
|
||||||
Client->>Handler: TCP connect + Hello bytes
|
Client->>Handler: TCP connect + Hello bytes
|
||||||
|
|
||||||
Note over Handler: decode protocol headers<br/>(fake-TLS / obfuscated / secure)<br/>stage: hello → tunnel
|
Note over Handler: decode protocol headers<br/>(fake-TLS / obfuscated / secure)<br/>stage: hello → tunnel
|
||||||
|
|
||||||
Note over Handler: resolve pool: whereis(dc_to_pool_name(DcId))<br/>(registered name lookup; falls back to default DC from mtp_config if not found)
|
Note over Handler: resolve pool:<br/>single-node: whereis(dc_to_pool_name(DcId))<br/>split mode: erpc:call(BackNode, erlang, whereis, [PoolName])<br/>→ returns {PoolName, BackNode}<br/>(falls back to default DC from mtp_config if not found)
|
||||||
Handler->>Pool: mtp_dc_pool:get(Pool, self(), Opts) [sync]
|
Handler->>Pool: mtp_dc_pool:get(Pool, self(), Opts) [sync]
|
||||||
Pool-->>Down: upstream_new(Handler, Opts) [cast]
|
Pool-->>Down: upstream_new(Handler, Opts) [cast]
|
||||||
Pool->>Handler: Downstream pid
|
Pool->>Handler: Downstream pid
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,26 @@ surviving (or freshly-started) DC connection transparently.
|
||||||
- `mtp_handler` — one process per connected Telegram client
|
- `mtp_handler` — one process per connected Telegram client
|
||||||
- `mtp_down_conn (new)` — replacement downstream spawned by the pool
|
- `mtp_down_conn (new)` — replacement downstream spawned by the pool
|
||||||
|
|
||||||
|
**Split-mode note:** in `front/back` split mode `mtp_handler` lives on the
|
||||||
|
front node and `mtp_dc_pool` / `mtp_down_conn` live on the back node. Every
|
||||||
|
message in the diagram below that crosses the front↔back boundary (the
|
||||||
|
`migrate` cast, `upstream_new` cast, `Pool->>Handler` reply, etc.) is carried
|
||||||
|
transparently by Erlang distribution — no code changes are needed because
|
||||||
|
Erlang PIDs and `gen_server` calls work across nodes unchanged. Process
|
||||||
|
monitors also fire on node disconnection, so a back-node restart causes all
|
||||||
|
affected front-node handlers to exit cleanly.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
participant TG as Telegram
|
participant TG as Telegram
|
||||||
participant OldDown as mtp_down_conn (old)
|
box LightGreen "BACK node"
|
||||||
participant Pool as mtp_dc_pool
|
participant OldDown as mtp_down_conn (old)
|
||||||
participant Handler as mtp_handler
|
participant Pool as mtp_dc_pool
|
||||||
participant NewDown as mtp_down_conn (new)
|
participant NewDown as mtp_down_conn (new)
|
||||||
|
end
|
||||||
|
box LightBlue "FRONT node"
|
||||||
|
participant Handler as mtp_handler
|
||||||
|
end
|
||||||
|
|
||||||
TG->>OldDown: TCP close
|
TG->>OldDown: TCP close
|
||||||
|
|
||||||
|
|
|
||||||
144
scripts/gen_dist_certs.sh
Executable file
144
scripts/gen_dist_certs.sh
Executable file
|
|
@ -0,0 +1,144 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# gen_dist_certs.sh — generate TLS certificates for Erlang distribution
|
||||||
|
#
|
||||||
|
# This script is designed to be run in TWO steps:
|
||||||
|
#
|
||||||
|
# Step 1 — run on the BACK server (once, to set up the CA and back cert):
|
||||||
|
# ./scripts/gen_dist_certs.sh init <output_dir>
|
||||||
|
#
|
||||||
|
# Step 2 — run on the BACK server for each FRONT server you add:
|
||||||
|
# ./scripts/gen_dist_certs.sh add-node <output_dir> <node_name>
|
||||||
|
#
|
||||||
|
# Example — back server and two front servers:
|
||||||
|
# ./scripts/gen_dist_certs.sh init /etc/mtproto-proxy/dist
|
||||||
|
# ./scripts/gen_dist_certs.sh add-node /etc/mtproto-proxy/dist front1
|
||||||
|
# ./scripts/gen_dist_certs.sh add-node /etc/mtproto-proxy/dist front2
|
||||||
|
#
|
||||||
|
# Output files (all in <output_dir>):
|
||||||
|
# ca.pem — CA certificate (copy to every server)
|
||||||
|
# back.pem / back.key — back node cert/key (keep on back server)
|
||||||
|
# <name>.pem / <name>.key — per-front cert/key (copy to that front server)
|
||||||
|
# ssl_dist.back.conf — ready-to-use Erlang TLS config for back
|
||||||
|
# (copy to /etc/mtproto-proxy/dist/ on back server)
|
||||||
|
# ssl_dist.<name>.conf — ready-to-use Erlang TLS config for that front
|
||||||
|
# (copy to /etc/mtproto-proxy/dist/ on that front server)
|
||||||
|
#
|
||||||
|
# After setup:
|
||||||
|
# On back server: ca.pem back.pem back.key ssl_dist.back.conf
|
||||||
|
# On each front: ca.pem <name>.pem <name>.key ssl_dist.<name>.conf
|
||||||
|
# Uncomment -proto_dist and -ssl_dist_optfile in vm.args on each server.
|
||||||
|
#
|
||||||
|
# Reference: https://www.erlang.org/doc/apps/ssl/ssl_distribution.html
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "Usage:" >&2
|
||||||
|
echo " $0 init <output_dir> # Step 1: CA + back cert" >&2
|
||||||
|
echo " $0 add-node <output_dir> <node_name> # Step 2: add a front cert" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[ $# -lt 2 ] && usage
|
||||||
|
|
||||||
|
CMD="$1"
|
||||||
|
OUTDIR="$2"
|
||||||
|
DAYS=3650 # 10 years
|
||||||
|
|
||||||
|
mkdir -p "$OUTDIR"
|
||||||
|
|
||||||
|
# ---- shared: ensure CA exists ----
|
||||||
|
ensure_ca() {
|
||||||
|
if [ ! -f "$OUTDIR/ca.key" ]; then
|
||||||
|
echo "==> Generating CA key and certificate..."
|
||||||
|
openssl req -new -x509 -days "$DAYS" \
|
||||||
|
-keyout "$OUTDIR/ca.key" \
|
||||||
|
-out "$OUTDIR/ca.pem" \
|
||||||
|
-nodes \
|
||||||
|
-subj "/CN=mtproto-proxy-dist-ca"
|
||||||
|
chmod 600 "$OUTDIR/ca.key"
|
||||||
|
else
|
||||||
|
echo "==> Using existing CA: $OUTDIR/ca.key"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- shared: generate one node cert signed by the CA ----
|
||||||
|
gen_node_cert() {
|
||||||
|
local NAME="$1"
|
||||||
|
echo "==> Generating certificate for node: $NAME"
|
||||||
|
openssl req -new \
|
||||||
|
-keyout "$OUTDIR/$NAME.key" \
|
||||||
|
-out "$OUTDIR/$NAME.csr" \
|
||||||
|
-nodes \
|
||||||
|
-subj "/CN=$NAME"
|
||||||
|
openssl x509 -req -days "$DAYS" \
|
||||||
|
-in "$OUTDIR/$NAME.csr" \
|
||||||
|
-CA "$OUTDIR/ca.pem" \
|
||||||
|
-CAkey "$OUTDIR/ca.key" \
|
||||||
|
-CAcreateserial \
|
||||||
|
-out "$OUTDIR/$NAME.pem"
|
||||||
|
rm -f "$OUTDIR/$NAME.csr"
|
||||||
|
chmod 600 "$OUTDIR/$NAME.key"
|
||||||
|
echo " -> $OUTDIR/$NAME.pem $OUTDIR/$NAME.key"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---- shared: write a ready-to-use ssl_dist.<name>.conf for one node ----
|
||||||
|
write_ssl_conf() {
|
||||||
|
local NAME="$1"
|
||||||
|
local CONF="$OUTDIR/ssl_dist.$NAME.conf"
|
||||||
|
cat > "$CONF" << EOF
|
||||||
|
%% Erlang TLS distribution config for node: $NAME
|
||||||
|
%% Copy this file to /etc/mtproto-proxy/dist/ on the '$NAME' server.
|
||||||
|
[
|
||||||
|
{server,
|
||||||
|
[{certfile, "/etc/mtproto-proxy/dist/$NAME.pem"},
|
||||||
|
{keyfile, "/etc/mtproto-proxy/dist/$NAME.key"},
|
||||||
|
{cacertfile, "/etc/mtproto-proxy/dist/ca.pem"},
|
||||||
|
{verify, verify_peer},
|
||||||
|
{fail_if_no_peer_cert, true}]},
|
||||||
|
{client,
|
||||||
|
[{certfile, "/etc/mtproto-proxy/dist/$NAME.pem"},
|
||||||
|
{keyfile, "/etc/mtproto-proxy/dist/$NAME.key"},
|
||||||
|
{cacertfile, "/etc/mtproto-proxy/dist/ca.pem"},
|
||||||
|
{verify, verify_peer}]}
|
||||||
|
].
|
||||||
|
EOF
|
||||||
|
echo " -> $CONF"
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$CMD" in
|
||||||
|
init)
|
||||||
|
ensure_ca
|
||||||
|
gen_node_cert "back"
|
||||||
|
write_ssl_conf "back"
|
||||||
|
echo ""
|
||||||
|
echo "==> Done. Back server setup complete."
|
||||||
|
echo ""
|
||||||
|
echo "Next:"
|
||||||
|
echo " 1. On the back server, copy to /etc/mtproto-proxy/dist/:"
|
||||||
|
echo " ca.pem back.pem back.key"
|
||||||
|
echo " 2. Copy ssl_dist.back.conf to /etc/mtproto-proxy/dist/"
|
||||||
|
echo " 3. Uncomment -proto_dist / -ssl_dist_optfile in vm.args."
|
||||||
|
echo " 4. For each front server, run:"
|
||||||
|
echo " $0 add-node $OUTDIR <front_name>"
|
||||||
|
;;
|
||||||
|
add-node)
|
||||||
|
[ $# -lt 3 ] && usage
|
||||||
|
NAME="$3"
|
||||||
|
if [ ! -f "$OUTDIR/ca.key" ]; then
|
||||||
|
echo "ERROR: CA not found in $OUTDIR. Run '$0 init $OUTDIR' first." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
gen_node_cert "$NAME"
|
||||||
|
write_ssl_conf "$NAME"
|
||||||
|
echo ""
|
||||||
|
echo "==> Done. Certificate for '$NAME' generated."
|
||||||
|
echo ""
|
||||||
|
echo "Copy to the '$NAME' front server:"
|
||||||
|
echo " ca.pem $NAME.pem $NAME.key"
|
||||||
|
echo " ssl_dist.$NAME.conf (copy to /etc/mtproto-proxy/dist/ on that server)"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
usage
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Loading…
Add table
Add a link
Reference in a new issue