mirror of
https://gitee.com/xmz7891/ykxmz.git
synced 2026-10-18 13:40:45 +00:00
用户报了三件事,一起处理。
== 一、发行版介绍里泄漏了服务器公网 IP ==
_build/CHANGELOG.md 的 2.1.3 那节写了服务器公网 IP,而发布脚本
把这个文件当"更新说明"传给 Gitee,于是发行版介绍里就带出去了。
为什么 IP 不该公开:直接打 IP 就绕过 Cloudflare 了 ——
没有 CDN、没有防护、没有速率限制。源站本来就不该被人知道是谁
(这也是为什么 nginx 那层只认域名、中转只绑 127.0.0.1)。
处理:
* 已发的 v2.1.3 发行版介绍用 API 改掉(扫了全部 11 个发行版,只它一个)
* 仓库里 16 个文件里的 IP 全部换成域名或占位符
* 运维脚本改成从环境变量读,公开仓库里不留具体地址
顺手发现**更严重的一处**:旧管理员密码硬编码在
docs/AFTER-INSTALL.txt(会打进服务端 zip)和
installer/license-combined.txt(**安装包的许可页,每个装的人都能看到**)里。
而用户当前密码就含这个串,等于公开了一半。一并清掉,
公开材料里改成"密码在你服务器上"的说明。
== 二、手表端填了地址却连不上 ==
用户截图:填域名(没写协议头)-> 点「检查服务器」报
MalformedURLException: no protocol: <域名>/v1/health
根因:补协议头的逻辑只写在连接那条路(RelayTransport 的构造函数)上,
checkServer() 直接拿原始字符串拼 URL —— 同一个地址,连接能用、检查却崩。
修法:把规范化收到 relayUrl() 这**唯一一个入口**,所有调用方都从那里取。
规则和电脑端一致:IP / localhost / *.local -> http,域名 -> https。
顺带补了 _tests/test_relay_url_normalize.py 盯住它(含 9 个用例实算)。
== 三、Gitee 的 README 被判"可能含有违规信息" ==
Gitee 挂的是软性公告(可自行修改或申诉),但确实挂在那。
逐项看了 README,改掉几处**不准确**且容易误判的表述:
* 原文写"看看学生机是不是在打游戏" —— 这是**错的**:
被控端只装在一台电脑上(讲台那台),根本看不到别的机器。
既不准确又像监控软件,改成"作用于被控端那一台"
* 课堂那几项功能如实说明:改的是本机 hosts、结束的是本机进程,
都可逆,下课点「一键恢复全部」就还原
* "免杀对抗""伪装成系统进程"这类词(虽然是在说"我们不做这些")
本身就在审核词库里,换成正面的说法
* 成品文件名从 1.8.2 更新到当前版本,并且以后发版自动跟着改
(把 README 加进了 bump 脚本;以前一直停在 1.8.2,
用户照着 README 去 releases 根本找不到那个文件)
== 四、做这件事时自己踩的坑(都写进注释了)==
* 批量替换时**没排除 gitignore 的文件**,把本地密码文件也改了
* 替换时往字典字面量中间塞注释,**弄坏了 5 个文件的语法**
* 修的时候手写"坏文件列表",漏了 2 个 ——
改成全仓库扫模式(列表是观察到的,不是穷尽的)
* 清理工具自己又成了泄漏点(脚本里写了要匹配的 IP/密码),
加了 _build/precommit_leak_gate.py 当闸门才抓到
教训:批量替换要先排除不该碰的文件,而且不能往表达式中间塞东西。
== 按用户要求:测完再提交 ==
全量回归 41 个阶段全 PASS(2 项因锁屏 SKIP)
601 个 Python 文件语法通过
pyflakes 干净
安装包完整周期 20/20
对外材料清洁度 15/15
提交前泄漏闸门通过
(这条信息是补写的:上一个提交因为 heredoc 把提交说明打到屏幕而不是
文件,信息变成空的 "remove temp" 了,内容本身是对的。)
205 lines
6.8 KiB
Python
205 lines
6.8 KiB
Python
# -*- coding: utf-8 -*-
|
||
r'''把 package_remote.py 里的 RELAY_README 整块换成新版本。
|
||
|
||
为什么按块换而不是用 edit 工具:
|
||
那段是个 f-string 三引号大块,里面有几百行、
|
||
还有各种全角空格和竖线字符,逐字符对齐很容易失败。
|
||
定位「RELAY_README = f"""」到下一个单独的 `"""` 行更稳。
|
||
'''
|
||
from __future__ import annotations
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
SRC = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote"
|
||
r"\_build\package_remote.py")
|
||
BOM = b"\xef\xbb\xbf"
|
||
|
||
NEW_BODY = '''RELAY_README = f"""========================================================
|
||
服务器端(随身 WiFi / Linux) 版本 {VERSION}
|
||
========================================================
|
||
|
||
作用:让**不在同一个网络**的电脑和手表连上。
|
||
|
||
手表 ──HTTPS──┐
|
||
├──> Cloudflare ──> 本机 :8080
|
||
电脑 ──HTTPS──┘
|
||
|
||
本程序是**纯标准库**的 Python,不装任何第三方包。
|
||
内存占用约 20MB,加 cloudflared 共约 60MB —— 512MB 的小机器跑得动。
|
||
|
||
|
||
【装:一条命令,全自动】
|
||
|
||
把本文件夹拷到设备上,然后:
|
||
|
||
bash deploy.sh
|
||
|
||
它会一口气做完这些,不用你一步步敲:
|
||
|
||
1. 检查并安装 cloudflared(已经装了就用现成的)
|
||
2. 把中转程序装到 /root/lanremote-relay/
|
||
3. 网络调优(keepalive / MSS / MTU / 开机加速)
|
||
4. 注册并启动 4 个 systemd 服务(开机自启)
|
||
5. 配好 Cloudflare 隧道
|
||
6. 自检,然后把**公网地址**打印出来
|
||
|
||
── 三种常见情形 ──
|
||
|
||
① 机器上已经有隧道凭据(/root/.cloudflared/*.json)
|
||
直接: bash deploy.sh
|
||
|
||
② 有 Cloudflare 给的隧道 Token
|
||
TUNNEL_TOKEN=eyJhIjoi... bash deploy.sh
|
||
(用环境变量而不是 --token,Token 就不会留在 shell 历史里)
|
||
|
||
③ 换域名 / 只装中转不要隧道
|
||
bash deploy.sh --hostname relay.你的域名
|
||
bash deploy.sh --skip-tunnel
|
||
|
||
看全部参数: bash deploy.sh --help
|
||
|
||
|
||
**重跑 deploy.sh = 升级**,不会把已有配置冲掉:
|
||
设备数据、配对码、管理密码、隧道凭据、手工改过的 config.yml
|
||
都会原样保留。
|
||
|
||
|
||
【常用命令】
|
||
|
||
lanremote-status 看整体状态(服务 / 端口 / 公网 / 内存)
|
||
lanremote-url 打印当前公网地址
|
||
lanremote-key 打印管理钥匙
|
||
systemctl status lanremote-relay
|
||
systemctl status lanremote-tunnel
|
||
journalctl -u lanremote-relay -f
|
||
|
||
|
||
【关于 install.sh(旧版脚本,仍在包里)】
|
||
|
||
装匿名快速隧道,公网地址每次重启都变。
|
||
新机器建议用 deploy.sh(具名隧道,地址固定)。
|
||
install.sh 保留是为了兼容已经用它装过的机器。
|
||
|
||
|
||
【地址老变怎么办?记住一个局域网地址就行】
|
||
|
||
匿名隧道的公网地址每次重启都会变。查当前地址:
|
||
|
||
浏览器打开 http://<随身WiFi的IP>:8080/url
|
||
|
||
它会直接返回当前的公网地址,纯文本,不用登录。
|
||
|
||
具名隧道(deploy.sh 装的)地址是固定的,不用管这个。
|
||
|
||
|
||
【管理】
|
||
|
||
官网: <公网地址>/
|
||
管理页: <公网地址>/?key=<lanremote-key 的输出>
|
||
管理密码: <改从环境变量读>(存在 data/admin_password.txt,可改)
|
||
|
||
能看到:设备列表、在线状态、配对码、流量、会话数
|
||
能操作:踢会话、抹掉配对码绑定
|
||
|
||
|
||
【网络调优:这些参数都是实测调出来的】
|
||
|
||
deploy-tune.sh 会做这五件事,改动之前先看它开头的说明:
|
||
|
||
① TCP keepalive 30/10/3
|
||
这条 4G 线路会**静默丢连接**(不发明文 FIN/RST)。
|
||
Linux 默认要 7200 秒才发现死链,表现就是"过几分钟突然 502"。
|
||
改成 30 秒首发探测之后,最快 60 秒就能发现并重建。
|
||
|
||
② tcp_mtu_probing = 1
|
||
接口写着 MTU 1500,但实际路径扛不住(1472 通、1500 全丢)。
|
||
不打开探测的话,新建 TLS 连接约 30% 失败。
|
||
|
||
③ MSS 钳制到 1360
|
||
只限制 TCP 段,不动链路层。
|
||
实测:不夹 87%,夹到 1360/1400 都是 100%。
|
||
|
||
④ 接口 MTU 1400
|
||
**这一条是专门给 QUIC 隧道的**:MSS 钳制走 iptables 的
|
||
TCPMSS 目标,只对 TCP 生效;QUIC 跑 UDP,完全不受它保护。
|
||
QUIC 的包超过路径 MTU 就会被丢,而 UDP 丢包不回 ICMP,
|
||
只能干等握手超时(日志里那句
|
||
"failed to dial to edge with quic: timeout: no recent network activity")。
|
||
实测把 MTU 从 1500 降到 1400:建齐 6 条连接 28 秒 -> 13 秒。
|
||
|
||
⑤ mask 掉 systemd-networkd-wait-online
|
||
这台机器 wlan0 归 NetworkManager 管,
|
||
systemd-networkd-wait-online 傻等一个不归它的网络,
|
||
干等 2 分钟超时,把 relay / tunnel 全拖住。
|
||
实测:掉电重启后公网恢复 2 分 10 秒 -> 24 秒。
|
||
|
||
|
||
【隧道的两套配置】
|
||
|
||
deploy.sh 会装两份隧道服务,但**只启用一份** ——
|
||
两条隧道同时跑会互相争抢,实测反而更不稳。
|
||
|
||
lanremote-tunnel 主用(默认 quic)
|
||
lanremote-tunnel-h2 备用(http2)
|
||
|
||
想切换:
|
||
systemctl disable --now lanremote-tunnel
|
||
systemctl enable --now lanremote-tunnel-h2
|
||
|
||
为什么默认 quic:尾部延迟好得多(实测 P99 2.99 秒 vs 13.9 秒)。
|
||
什么时候换 http2:这条线路对 UDP 特别不友好的时候
|
||
(日志里大量 "Failed to dial a quic connection")。
|
||
|
||
|
||
【省内存】
|
||
|
||
默认给服务设了 MemoryMax(relay 128M / tunnel 160M)。
|
||
要再省就把用不上的服务停掉:
|
||
systemctl stop mariadb php8.2-fpm apache2
|
||
systemctl disable mariadb php8.2-fpm apache2
|
||
"""'''
|
||
|
||
|
||
def main() -> int:
|
||
raw = SRC.read_bytes()
|
||
had_bom = raw.startswith(BOM)
|
||
text = raw.decode("utf-8-sig")
|
||
|
||
start = text.find('RELAY_README = f"""')
|
||
if start < 0:
|
||
print(" ✗ 找不到 RELAY_README 的开头")
|
||
return 1
|
||
|
||
# 结束:从开头往后找第一个「单独一行 """」的结束引号
|
||
body_start = start + len('RELAY_README = f"""')
|
||
end = text.find('\n"""', body_start)
|
||
if end < 0:
|
||
print(" ✗ 找不到 RELAY_README 的结束引号")
|
||
return 1
|
||
end += len('\n"""') # 把结束引号一起吃掉
|
||
|
||
old = text[start:end]
|
||
print(" 原块 %d 行 -> 新块 %d 行"
|
||
% (old.count("\n") + 1, NEW_BODY.count("\n") + 1))
|
||
|
||
if old.strip() == NEW_BODY.strip():
|
||
print(" 内容已经一样了,不动")
|
||
return 0
|
||
|
||
text = text[:start] + NEW_BODY + text[end:]
|
||
data = text.encode("utf-8")
|
||
if had_bom:
|
||
data = BOM + data
|
||
SRC.write_bytes(data)
|
||
|
||
# 语法自检
|
||
import ast
|
||
ast.parse(SRC.read_text(encoding="utf-8-sig"))
|
||
print(" ✓ 已替换,语法检查通过")
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|