diff --git a/Dockerfile b/Dockerfile index dae44d2..fe00866 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,6 +22,7 @@ FROM python:3.12-slim AS runtime ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PATH=/opt/venv/bin:$PATH \ + PYTHONPATH="/opt/venv/lib/python3.12/site-packages" \ TG_WS_PROXY_HOST=0.0.0.0 \ TG_WS_PROXY_PORT=1080 \ TG_WS_PROXY_DC_IPS="2:149.154.167.220 4:149.154.167.220" diff --git a/README.md b/README.md index 8e93b94..9562075 100644 --- a/README.md +++ b/README.md @@ -125,3 +125,79 @@ pyinstaller packaging/windows.spec ## Лицензия [MIT License](LICENSE) + +## FORK +### Linux/NAS +```bash +sudo apt update && sudo apt install git +git clone https://github.com/borisovmsw/tg-ws-proxy.git +cd tg-ws-proxy +docker build -t tg-proxy . +docker run -d --name tg-proxy -p 1080:1080 tg-proxy:latest -u userx -P 123456 +``` +### Подключаемся и проверяем +#### Локально +``` +tg://socks/?server=127.0.0.1&port=1080&user=userx&pass=123456 +``` +#### Удаленно +``` +tg://socks/?server=192.168.1.139&port=1080&user=userx&pass=123456 +``` + +### Openwrt ARM64 +#### Настраиваем компьютер на компиляцию под ARM64 процессор роутера +```bash +docker run --privileged --rm tonistiigi/binfmt --install all\n +docker buildx create --name mybuilder --use +docker buildx inspect --bootstrap +``` +#### Собираем образ для роутера +```bash +docker buildx build --platform linux/arm64 -t tg-proxy-flint:latest --load .\n +``` +#### Сохраняем образ в файл +```bash +docker save tg-proxy-flint:latest -o tg-proxy-flint.tar +``` +#### Закидываем образ на роутер +```bash +scp -O tg-proxy-flint.tar root@192.168.1.1:/tmp/ +``` + +#### Подключаемся к роутеру +```bash +ssh root@192.168.1.1 +``` +#### Загружаем образ и удалям его +```bash +docker load -i /tmp/tg-proxy-flint.tar && rm /tmp/tg-proxy-flint.tar +``` +#### Настройки роутера +``` + Заходим в настройки Firewall - Traffic Rules + Создаем Rule с именем Docker, + Source zone - docker, + Destination zone - WAN + на закладке Advanced Settings оставляем только ip4 +``` +#### Далее самое главное!!! Включаем WAN для Docker, информации крайне мало про эту срочку, времени на ее поиск ушло не мало +```bash +sed -i "s/^\([[:space:]]*\)list blocked_interfaces 'wan'/#\1&/" /etc/config/dockerd +``` +#### Перезагружаем роутер или же выполняем команду +```bash +/etc/init.d/dockerd restart +``` +#### Запускаем контейнер только на внутреннем IP +```bash +docker run -d --name tg-proxy -p 192.168.1.1:1080:1080 tg-proxy-flint:latest -u userx -P 123456 +``` +#### Смотрим логи, практически все INFO на каждое соединение заменил на DEBUG, чтобы не тратить ресурс постоянной памяти +```bash +docker logs tg-proxy +``` +#### Тестируем +```bash +tg://socks/?server=192.168.1.1&port=1080&user=userx&pass=123456 +``` \ No newline at end of file diff --git a/proxy/tg_ws_proxy.py b/proxy/tg_ws_proxy.py index 65f7ac3..dbab17b 100644 --- a/proxy/tg_ws_proxy.py +++ b/proxy/tg_ws_proxy.py @@ -670,7 +670,7 @@ async def _bridge_ws(reader, writer, ws: RawWebSocket, label, except BaseException: pass elapsed = asyncio.get_event_loop().time() - start_time - log.info("[%s] %s (%s) WS session closed: " + log.debug("[%s] %s (%s) WS session closed: " "^%s (%d pkts) v%s (%d pkts) in %.1fs", label, dc_tag, dst_tag, _human_bytes(up_bytes), up_packets, @@ -765,7 +765,7 @@ async def _tcp_fallback(reader, writer, dst, port, init, label, rr, rw = await asyncio.wait_for( asyncio.open_connection(dst, port), timeout=10) except Exception as exc: - log.warning("[%s] TCP fallback connect to %s:%d failed: %s", + log.debug("[%s] TCP fallback connect to %s:%d failed: %s", label, dst, port, exc) return False @@ -823,7 +823,7 @@ async def _handle_client(reader, writer): client_password = (await reader.readexactly(plen)).decode('utf-8', errors='ignore') if client_username != _auth_user or client_password != _auth_password: - log.warning("[%s] auth failed with creds: %s/%s", label, client_username, client_password) + log.debug("[%s] auth failed with creds: %s/%s", label, client_username, client_password) writer.write(b'\x01\x01') await writer.drain() writer.close() @@ -866,7 +866,7 @@ async def _handle_client(reader, writer): port = struct.unpack('!H', await reader.readexactly(2))[0] if ':' in dst: - log.error( + log.debug( "[%s] IPv6 address detected: %s:%d — " "IPv6 addresses are not supported; " "disable IPv6 to continue using the proxy.", @@ -884,7 +884,7 @@ async def _handle_client(reader, writer): rr, rw = await asyncio.wait_for( asyncio.open_connection(dst, port), timeout=10) except Exception as exc: - log.warning("[%s] passthrough failed to %s: %s: %s", label, dst, type(exc).__name__, str(exc) or "(no message)") + log.debug("[%s] passthrough failed to %s: %s: %s", label, dst, type(exc).__name__, str(exc) or "(no message)") writer.write(_socks5_reply(0x05)) await writer.drain() writer.close() @@ -937,7 +937,7 @@ async def _handle_client(reader, writer): init_patched = True if dc is None or dc not in _dc_opt: - log.warning("[%s] unknown DC%s for %s:%d -> TCP passthrough", + log.debug("[%s] unknown DC%s for %s:%d -> TCP passthrough", label, dc, dst, port) await _tcp_fallback(reader, writer, dst, port, init, label) return @@ -954,7 +954,7 @@ async def _handle_client(reader, writer): ok = await _tcp_fallback(reader, writer, dst, port, init, label, dc=dc, is_media=is_media) if ok: - log.info("[%s] DC%d%s TCP fallback closed", + log.debug("[%s] DC%d%s TCP fallback closed", label, dc, media_tag) return @@ -967,7 +967,7 @@ async def _handle_client(reader, writer): ok = await _tcp_fallback(reader, writer, dst, port, init, label, dc=dc, is_media=is_media) if ok: - log.info("[%s] DC%d%s TCP fallback closed", + log.debug("[%s] DC%d%s TCP fallback closed", label, dc, media_tag) return @@ -980,12 +980,12 @@ async def _handle_client(reader, writer): ws = await _ws_pool.get(dc, is_media, target, domains) if ws: - log.info("[%s] DC%d%s (%s:%d) -> pool hit via %s", + log.debug("[%s] DC%d%s (%s:%d) -> pool hit via %s", label, dc, media_tag, dst, port, target) else: for domain in domains: url = f'wss://{domain}/apiws' - log.info("[%s] DC%d%s (%s:%d) -> %s via %s", + log.debug("[%s] DC%d%s (%s:%d) -> %s via %s", label, dc, media_tag, dst, port, url, target) try: ws = await RawWebSocket.connect(target, domain, @@ -996,14 +996,14 @@ async def _handle_client(reader, writer): _stats.ws_errors += 1 if exc.is_redirect: ws_failed_redirect = True - log.warning("[%s] DC%d%s got %d from %s -> %s", + log.debug("[%s] DC%d%s got %d from %s -> %s", label, dc, media_tag, exc.status_code, domain, exc.location or '?') continue else: all_redirects = False - log.warning("[%s] DC%d%s WS handshake: %s", + log.debug("[%s] DC%d%s WS handshake: %s", label, dc, media_tag, exc.status_line) except Exception as exc: _stats.ws_errors += 1 @@ -1011,32 +1011,32 @@ async def _handle_client(reader, writer): err_str = str(exc) if ('CERTIFICATE_VERIFY_FAILED' in err_str or 'Hostname mismatch' in err_str): - log.warning("[%s] DC%d%s SSL error: %s", + log.debug("[%s] DC%d%s SSL error: %s", label, dc, media_tag, exc) else: - log.warning("[%s] DC%d%s WS connect failed: %s", + log.debug("[%s] DC%d%s WS connect failed: %s", label, dc, media_tag, exc) # -- WS failed -> fallback -- if ws is None: if ws_failed_redirect and all_redirects: _ws_blacklist.add(dc_key) - log.warning( + log.debug( "[%s] DC%d%s blacklisted for WS (all 302)", label, dc, media_tag) elif ws_failed_redirect: _dc_fail_until[dc_key] = now + _DC_FAIL_COOLDOWN else: _dc_fail_until[dc_key] = now + _DC_FAIL_COOLDOWN - log.info("[%s] DC%d%s WS cooldown for %ds", + log.debug("[%s] DC%d%s WS cooldown for %ds", label, dc, media_tag, int(_DC_FAIL_COOLDOWN)) - log.info("[%s] DC%d%s -> TCP fallback to %s:%d", + log.debug("[%s] DC%d%s -> TCP fallback to %s:%d", label, dc, media_tag, dst, port) ok = await _tcp_fallback(reader, writer, dst, port, init, label, dc=dc, is_media=is_media) if ok: - log.info("[%s] DC%d%s TCP fallback closed", + log.debug("[%s] DC%d%s TCP fallback closed", label, dc, media_tag) return @@ -1060,7 +1060,7 @@ async def _handle_client(reader, writer): splitter=splitter) except asyncio.TimeoutError: - log.warning("[%s] timeout during SOCKS5 handshake", label) + log.debug("[%s] timeout during SOCKS5 handshake", label) except asyncio.IncompleteReadError: log.debug("[%s] client disconnected", label) except asyncio.CancelledError: @@ -1116,7 +1116,7 @@ async def _run(port: int, dc_opt: Dict[int, Optional[str]], bl = ', '.join( f'DC{d}{"m" if m else ""}' for d, m in sorted(_ws_blacklist)) or 'none' - log.info("stats: %s | ws_bl: %s", _stats.summary(), bl) + log.debug("stats: %s | ws_bl: %s", _stats.summary(), bl) asyncio.create_task(log_stats())