Files
ykxmz/_build/verify_cleartext.py
xmz7891 5fc2f97be7 2.1.3:搬到 Lightsail 真服务器 + 支持"没有证书的中转"
用户买了 AWS Lightsail(2G 内存 / 2 vCPU / 60G SSD / 公网 IP
13.54.30.196),中转从随身 WiFi 那台小机器搬过去。

== 为什么这次搬迁解决了一堆老问题 ==

原机器(Debian aarch64、460MB、走 4G)一直有几件搞不定的:

  * 线路对 Cloudflare 隧道的 **7844 端口间歇性阻断**(443 通、7844 不通),
    隧道每 3~10 分钟掉一次、公网跟着 530 —— 查了很久才从
    cloudflared 自己的 precheck 里看清
  * 只有 460MB 内存
  * 会掉电硬挂(实测 2026-10-08 18:42 一次非正常关机)

新机器**有公网 IP**,中转直接监听,不经隧道、不过 7844。
实测公网延迟从 **1000~2000 ms 降到 1.1 ms**。

== 代码改动 ==

1. **relay.py 的 DATA_DIR 默认值不再写死**
   原来是 /root/lanremote-relay。新机器上服务以 admin 普通用户跑
   (它没理由要 root),而 /root 是 0700 —— 部署时直接
   PermissionError: '/root/lanremote-relay'。
   改成"本脚本所在目录下的 data/",搬到哪都跟着走。

2. **客户端支持"没有证书的中转"**
   新服务器是明文 HTTP(没签证书),而两个客户端原来都往 https 走:
     * 手表端:裸 IP 会被强制补 https:// -> 永远连不上,
       报的还是 SSL 错误,很容易让人白查半天证书
     * Android 9+ 默认禁止明文流量,没 usesCleartextTraffic
       请求会被系统直接掐掉
   Windows 端本来就是对的(正则里把任意纯 IP 都当"用明文")。
   现在手表端规则和它一致,并加了 usesCleartextTraffic。

3. 清掉电脑端一段函数内的重复代码(return 之后还有一份拷贝,
   缩进仍在函数体内所以不报错,但读起来很迷惑)。

== 新服务器上的部署 ==

    /opt/ykxmz-relay/            服务端(relay/admin/landing/showcase)
    ykxmz-relay.service          监听 0.0.0.0:13245
    ykxmz-monitor.service/.timer 每 60 秒查公网,连续 3 次不通就叫预案
    rescue-lightsail.sh          四级阶梯抢救(重启中转 / 强杀重拉 /
                                 查防火墙 / 重启整机[默认关])

  预案针对新架构重写过:原来围着隧道转(重启隧道、重施 MSS/MTU),
  现在没有隧道,改成重启中转 + 查端口/防火墙。

全量回归 46 个阶段全部通过。
2026-10-08 22:29:56 +08:00

46 lines
1.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
r'''验证 APK 里真的开了明文流量。
Android 的 usesCleartextTraffic 是编译进 binary manifest 的,
光看源码不算数 —— 万一构建脚本没把新清单打进去,源码改对了也没用。
所以直接解 APK 看里面的 AndroidManifest。
'''
from __future__ import annotations
import re
import sys
import zipfile
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
def main() -> int:
apk = ROOT / "dist" / "ykxmz-watch-2.1.2.apk"
if not apk.is_file():
print(" ✗ 找不到 %s" % apk)
return 1
print(" APK: %s(%d 字节)" % (apk.name, apk.stat().st_size))
with zipfile.ZipFile(apk) as z:
blob = z.read("AndroidManifest.xml")
# binary manifest 里字符串以 UTF-16LE 存在字符串池里
# 简单办法:直接在字节里找关键词
text = blob.decode("utf-16-le", "ignore")
for key in ("usesCleartextTraffic", "uses-permission", "INTERNET",
"com.lanremote.watch"):
print(" 含 %-24s %s" % (key, "✓" if key in text else ""))
ok = "usesCleartextTraffic" in text
# 属性名在池里,但值(true)是 0xFFFFFFFF 的布尔,不好直接找;
# 属性名出现就说明清单里有这条
print()
print(" %s 清单里带 usesCleartextTraffic"
% ("✅" if ok else "❌ 没有 —— Android 9+ 还是会拒绝 http://"))
return 0 if ok else 1
if __name__ == "__main__":
sys.exit(main())