From 6dfe7bacd3be89814b5299cc0341cd340d9f87cb Mon Sep 17 00:00:00 2001 From: Danila Vershinin Date: Mon, 9 Mar 2026 10:46:43 +0800 Subject: [PATCH 1/2] fix: allow binding to localhost and all RFC 1918 addresses (#32) Expand PID validation to accept 127.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16 in addition to 10.0.0.0/8. Skip binding outgoing connections to loopback so Telegram DC traffic uses the default interface. Closes #32 --- engine/engine.c | 2 +- net/net-events.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/engine.c b/engine/engine.c index c30a3821..83fc1b05 100644 --- a/engine/engine.c +++ b/engine/engine.c @@ -234,7 +234,7 @@ void engine_init (const char *const pwd_filename, int do_not_open_port) { if (E->settings_addr.s_addr) { ipv4 = ntohl (E->settings_addr.s_addr); - if ((ipv4 >> 24) != 10) { + if ((ipv4 >> 24) != 10 && (ipv4 >> 24) != 127 && (ipv4 >> 20) != (172 << 4 | 1) && (ipv4 >> 16) != (192 << 8 | 168)) { kprintf ("Bad binded IP address " IP_PRINT_STR ", search in ifconfig\n", IP_TO_PRINT (ipv4)); ipv4 = 0; } diff --git a/net/net-events.c b/net/net-events.c index 7408b48b..12351428 100644 --- a/net/net-events.c +++ b/net/net-events.c @@ -668,7 +668,7 @@ int client_socket (in_addr_t in_addr, int port, int mode) { if (!(mode & SM_IPV6)) { engine_t *E = engine_state; - if (E && E->settings_addr.s_addr) { + if (E && E->settings_addr.s_addr && (ntohl(E->settings_addr.s_addr) >> 24) != 127) { struct sockaddr_in localaddr; memset (&localaddr, 0, sizeof (localaddr)); From c51603e8159693ab83bf1d6d6d906b16353956c9 Mon Sep 17 00:00:00 2001 From: Danila Vershinin Date: Tue, 17 Mar 2026 19:22:28 +0800 Subject: [PATCH 2/2] fix: allow stats endpoint access from all RFC 1918 private networks (#35) The /stats HTTP endpoint was hardcoded to only respond to 127.0.0.1. When running in Docker, host requests arrive via the bridge network (172.17.0.1), causing the stats endpoint to reject them. Replace the localhost-only check with an is_private_ip() helper that accepts all RFC 1918 ranges (10/8, 172.16/12, 192.168/16) plus loopback (127/8). --- CHANGELOG.md | 5 +++++ README.md | 2 +- mtproto/mtproto-proxy.c | 9 ++++++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6658b87..7610148f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ All notable changes to this project will be documented in this file. +## [3.0.11] - 2026-03-17 + +### Fixed +- Stats endpoint (`--http-stats`) now accessible from all RFC 1918 private networks, not just loopback ([#35](https://github.com/GetPageSpeed/MTProxy/issues/35)). Fixes stats being unreachable from Docker host via bridge network. + ## [3.0.10] - 2026-02-16 ### Fixed diff --git a/README.md b/README.md index c738c7bc..85fe5cb1 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ head -c 16 /dev/urandom | xxd -ps ... where: - `nobody` is the username. `mtproto-proxy` calls `setuid()` to drop privilegies. - `443` is the port, used by clients to connect to the proxy. -- `8888` is the local port for statistics (requires `--http-stats`). Like `curl http://localhost:8888/stats`. You can only get this stat via loopback. +- `8888` is the local port for statistics (requires `--http-stats`). Like `curl http://localhost:8888/stats`. Stats are accessible from private networks (loopback, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) but not from public IPs. - `` is the secret generated at step 3. Also you can set multiple secrets: `-S -S `. - `proxy-secret` and `proxy-multi.conf` are obtained at steps 1 and 2. - `1` is the number of workers. You can increase the number of workers, if you have a powerful server. diff --git a/mtproto/mtproto-proxy.c b/mtproto/mtproto-proxy.c index a5e0fdb5..08033caa 100644 --- a/mtproto/mtproto-proxy.c +++ b/mtproto/mtproto-proxy.c @@ -1380,6 +1380,13 @@ int http_query_job_run (job_t job, int op, struct job_thread *JT) { } } +static inline int is_private_ip (unsigned ip) { + return (ip >> 24) == 127 // 127.0.0.0/8 + || (ip >> 24) == 10 // 10.0.0.0/8 + || (ip >> 20) == 0xAC1 // 172.16.0.0/12 + || (ip >> 16) == 0xC0A8; // 192.168.0.0/16 +} + int hts_stats_execute (connection_job_t c, struct raw_message *msg, int op) { struct hts_data *D = HTS_DATA(c); @@ -1392,7 +1399,7 @@ int hts_stats_execute (connection_job_t c, struct raw_message *msg, int op) { D->query_flags &= ~QF_KEEPALIVE; return -501; } - if (CONN_INFO(c)->remote_ip != 0x7f000001) { + if (!is_private_ip(CONN_INFO(c)->remote_ip)) { return -404; }