final
This commit is contained in:
parent
99c5340f09
commit
02dad8343e
|
|
@ -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"
|
||||
|
|
|
|||
76
README.md
76
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
|
||||
```
|
||||
|
|
@ -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())
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue