MoreRSS

site iconLiHan | 李寒修改

研究生,中国传媒大学
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

LiHan | 李寒的 RSS 预览

京东云无线宝 AX6600 雅典娜刷 OpenWrt/LibWrt 记录

2026-06-28 14:32:55

Featured image of post 京东云无线宝 AX6600 雅典娜刷 OpenWrt/LibWrt 记录

购买注意

  • 本次设备:京东云无线宝 AX6600 雅典娜,型号 RE-CS-02,配置 1G+128G
  • 购买渠道:京东。
  • 到手价:294 元。
  • 本文记录设备数据、开 SSH、刷 U-Boot、刷 GPT/ROM、首次进入 LibWrt、分区格式化和挂载过程。
  • 本文是公开博客草稿,SN、MAC、后台 token、宽带账号密码、私钥等真实信息一律不写入正文或截图。

设备型号数据

基本信息

项目 记录
商品名 京东云无线宝 AX6600 雅典娜
英文/社区名 JDCloud AX6600 Athena
设备型号 RE-CS-02
本机版本 1G RAM + 128G eMMC
设备类型 Wi-Fi 6 路由器
OpenWrt target qualcommax/ipq60xx
OpenWrt device id jdcloud_re-cs-02
当前 OpenWrt 支持状态 官方 Techdata 记录为 snapshot,正式 release 支持情况刷机前再查
Bootloader U-Boot
电源 12V/3A 圆口电源

硬件参数

项目 参数
SoC Qualcomm IPQ6010
CPU 4 核 A53,1.8GHz
RAM 1024MB
存储 eMMC,本机为 128GB
交换芯片 Qualcomm Atheros QCA8075
2.5G PHY Qualcomm QCA8081
网口 4 个千兆口 + 1 个 2.5G 口
VLAN 支持
无线芯片 Qualcomm QCN5022 / QCN5052 / QCN9024
2.4GHz b/g/n/ax,2x2 MIMO
5GHz a/n/ac/ax,一组 4x4 MIMO,一组 2x2 MIMO
USB 1 个 USB 3.0
LED / 按键 3 个 LED,3 个按键

待实机确认

  • 外壳标签、SN 开头、出厂日期。
  • 原厂 ROM 版本:1.5.50.r2204, 0251ebd85+r49254
  • 后台入口地址、默认 LAN 网段:192.168.68.1
  • 2.5G 口在原厂系统和 OpenWrt 中默认归属 WAN 还是 LAN。
  • cat /proc/cpuinfolsblkip link 的实际输出。
  • 本机能否免拆开 SSH;如果 ROM 版本太新,可能需要走不同路径。

刷机

参考

0. 开始前检查

  • 先拍外壳标签和后台版本页,打码后再放图。
  • 下载到本地的固件、U-Boot、GPT 或线刷包都要记录来源、文件名、大小和校验值。
  • 刷写 U-Boot、GPT、分区表前先备份,不能只靠网上的通用包。
  • /tmp 是内存盘,上传大文件和备份分区时注意空间。
  • 开箱第一次进入原厂后台时,先关闭自动更新,避免固件更新到不好开 SSH 或不好刷的版本。

1. 连接并进入 SSH

这一步参考 JDCloud AX6600(雅典娜) 刷机记录 的流程,目标是在原厂系统里启动 dropbear,先拿到 root shell。

  1. 用网线把雅典娜接到家里的路由器,保证操作电脑和雅典娜在同一个局域网下,并且雅典娜可以访问互联网。
  2. 操作电脑连接雅典娜自己的 Wi-Fi。
  3. 访问 jdcloudwifi.com,进入京东云无线宝后台,注册并登录。
  4. 确认后台 IP。本机默认后台地址是 192.168.68.1,后续命令都按这个地址写;如果实际后台 IP 不同,以浏览器地址栏为准。

登录后台后,按 F12 打开浏览器开发者工具,在 Application / 应用 里找到当前后台地址对应的 Cookies,复制 sessionid 的值。

在浏览器开发者工具中复制 sessionid

sessionid 填入本地脚本 open-ssh.py

1
2
url = "http://192.168.68.1/jdcapi"
sessionid = "这里填浏览器 Cookie 里的 sessionid"

脚本核心是通过 /jdcapi 调用 uci,把 dhcp.odhcpd.leasetrigger 设置为 /usr/sbin/dropbear,再提交 dhcp 配置:

1
2
uci set dhcp.odhcpd.leasetrigger=/usr/sbin/dropbear
uci commit dhcp

运行脚本:

1
python open-ssh.py

正常情况下应该返回两次成功结果,关键是里面有两个 0。这时配置已经写入,但还需要触发一次 IPv6 相关流程让 dropbear 启动。

进入后台的 路由设置 -> 上网设置,找到 IPV6网络设置

  • 打开 IPv6 开关。
  • 上网方式 选择 NAT6
  • 保存并重新应用更改。

开启 IPv6 NAT6

保存后路由器会重启。重启过程中电脑可能自动切到其他 Wi-Fi,导致后续连不上雅典娜;等路由器起来后,重新连接雅典娜 Wi-Fi。

随后在电脑命令行尝试 SSH:

密码和京东云无线宝管理后台密码相同。

如果本机 SSH 客户端比较新,可能会遇到:

1
Unable to negotiate with 192.168.68.1 port 22: no matching host key type found. Their offer: ssh-rsa

这是因为原厂系统的 SSH 服务端只提供了旧的 ssh-rsa host key。临时兼容可以加参数:

1
ssh -oHostKeyAlgorithms=+ssh-rsa [email protected]

成功后会进入 JDBox Router 的 BusyBox shell。本机进入后显示原厂系统版本为 1.5.50.r2204uname -a 显示内核为 Linux JDBox_Athena 4.4.60 ... armv7l GNU/Linux

SSH 连接成功

2. 分区检查与备份计划

进入 SSH 后先检查系统版本和 eMMC 分区布局。

1
2
3
4
5
uname -a
blkid
cat /proc/partitions
mount
df -h

本机原厂系统信息:

1
2
3
4
BusyBox v1.30.1
JDBox Router
Firmware: 1.5.50.r2204, 0251ebd85+r49254
Kernel: Linux JDBox_Athena 4.4.60 SMP PREEMPT JDCloud Technologies armv7l GNU/Linux

blkid 显示整盘是 eMMC GPT:

1
/dev/mmcblk0: PTUUID="98101b32-bbe2-4bf2-a06e-2bb33d000c20" PTTYPE="gpt"

关键分区如下:

分区 PARTLABEL 大小 作用
/dev/mmcblk0p1 0:SBL1 768 KiB 早期启动链
/dev/mmcblk0p2 0:BOOTCONFIG 256 KiB 启动配置
/dev/mmcblk0p3 0:BOOTCONFIG1 256 KiB 启动配置备份
/dev/mmcblk0p4 0:QSEE 1792 KiB Qualcomm secure environment
/dev/mmcblk0p5 0:QSEE_1 1792 KiB QSEE 备份
/dev/mmcblk0p10 0:CDT 256 KiB hardware config / device tree 相关配置
/dev/mmcblk0p11 0:CDT_1 256 KiB CDT 备份
/dev/mmcblk0p12 0:APPSBLENV 256 KiB U-Boot 环境变量
/dev/mmcblk0p13 0:APPSBL 640 KiB U-Boot 主分区
/dev/mmcblk0p14 0:APPSBL_1 640 KiB U-Boot 备份分区
/dev/mmcblk0p15 0:ART 512 KiB 无线校准 / MAC 等设备校准数据,必须备份
/dev/mmcblk0p16 0:HLOS 6144 KiB kernel slot
/dev/mmcblk0p17 0:HLOS_1 6144 KiB kernel backup slot
/dev/mmcblk0p18 rootfs 61440 KiB 原厂 rootfs
/dev/mmcblk0p19 0:WIFIFW 4096 KiB Wi-Fi firmware
/dev/mmcblk0p20 rootfs_1 61440 KiB 备用 rootfs
/dev/mmcblk0p21 0:WIFIFW_1 4096 KiB 备用 Wi-Fi firmware
/dev/mmcblk0p22 rootfs_data 20480 KiB overlay
/dev/mmcblk0p23 0:ETHPHYFW 512 KiB Ethernet PHY firmware
/dev/mmcblk0p24 plugin 89344 KiB 原厂插件区
/dev/mmcblk0p25 log 1048576 KiB 日志区
/dev/mmcblk0p26 swap 524288 KiB swap
/dev/mmcblk0p27 storage 118996958 KiB 大容量存储区

/dev/disk/by-partlabel 在原厂系统里没有输出,所以后续不要照抄依赖 PARTLABEL symlink 的命令;如果要按标签定位,需要用 blkid 或固定分区号反查。

当前挂载情况:

1
2
3
4
5
6
/dev/root on /rom type squashfs
/dev/mmcblk0p22 on /overlay type ext4
/dev/mmcblk0p19 on /lib/firmware/IPQ6018/WIFI_FW type squashfs
/dev/mmcblk0p25 on /opt type ext4
/dev/mmcblk0p24 on /log type ext4
/dev/mmcblk0p27 on /mnt/mmcblk0p27 type ext4

/tmp 只有约 410 MiB 可用,但 /mnt/mmcblk0p27 有约 110 GiB 可用。备份可先写到 /mnt/mmcblk0p27/backup-ax6600-athena/,再用 scp 拉回电脑保存。刷写分区表或完整镜像前,不能只把备份留在路由器本机。

3. 刷入 U-Boot

下载参考帖提供的 U-Boot:

1
curl -LO https://250.ac.cn/file/uboot-JDC_AX1800_Pro-AX6600_Athena-20240510.bin

本地先校验文件。本文这次下载到的文件信息:

1
2
3
文件名: uboot-JDC_AX1800_Pro-AX6600_Athena-20240510.bin
大小: 655360 bytes
sha256: d8b5e9d181500643bc17369f9c8d88a4c5cca51794ce1a9f322d97b2c2e0620c

上传到路由器 /root/。原厂系统的 SSH 比较老,新版 scp 直接传会遇到两个问题:

  • no matching host key type found. Their offer: ssh-rsa
  • ash: /usr/libexec/sftp-server: not found

第一个问题需要允许 ssh-rsa,第二个问题是新版 scp 默认走 SFTP,而原厂系统没有 sftp-server。因此要加 -O 强制使用旧版 SCP 协议:

1
2
3
scp -O -oHostKeyAlgorithms=+ssh-rsa \
 ./uboot-JDC_AX1800_Pro-AX6600_Athena-20240510.bin \
 [email protected]:/root/

使用 scp -O 上传 U-Boot

进入路由器后再次检查文件完整性:

1
2
ls -lh /root/uboot-JDC_AX1800_Pro-AX6600_Athena-20240510.bin
sha256sum /root/uboot-JDC_AX1800_Pro-AX6600_Athena-20240510.bin

确认大小和 hash 无误后刷入 U-Boot。此步骤需要谨慎,刷错可能导致路由器无法启动;如果文件不完整也可能导致路由器无法启动。

1
2
dd if=/root/uboot-JDC_AX1800_Pro-AX6600_Athena-20240510.bin of=$(blkid -t PARTLABEL=0:APPSBL -o device) conv=fsync
dd if=/root/uboot-JDC_AX1800_Pro-AX6600_Athena-20240510.bin of=$(blkid -t PARTLABEL=0:APPSBL_1 -o device) conv=fsync

返回写入记录即为成功:

刷入 U-Boot

4. 进入 U-Boot Web

刷完 U-Boot 后拔掉电源下电。

找个牙签顶住 reset 按键,再插上电源。路由器启动时会从红灯闪烁变成持续蓝灯,等出现稳定不变的蓝灯后松开 reset

用网线连接电脑和雅典娜路由器,最好接 LAN 口。电脑网卡手动配置 IPv4:

1
2
3
4
IP: 192.168.1.2
Mask: 255.255.255.0
Gateway: 192.168.1.1
DNS: 8.8.8.8

手动配置电脑 IPv4

保存后用浏览器访问:

1
http://192.168.1.1

即可进入 U-Boot Web 刷机页面。这里建议使用无痕浏览器窗口,避免之前访问过其他 192.168.1.1 路由器留下缓存干扰。

5. 刷 GPT 分区和 ROM

这一步最容易传错入口,入口对应关系如下:

页面 用途
http://192.168.1.1/ 刷 ROM / factory 固件
http://192.168.1.1/img.html 刷 GPT 分区表或完整 eMMC 镜像
http://192.168.1.1/uboot.html 刷 U-Boot
http://192.168.1.1/uimage.html 启动 initramfs uImage

先刷 GPT 分区。注意必须打开:

1
http://192.168.1.1/img.html

然后上传:

1
gpt-JDC_AX6600_Athena_dual-boot_rootfs2048M_no-last-partition.bin

下载地址:

1
https://250.ac.cn/file/gpt-JDC_AX6600_Athena_dual-boot_rootfs2048M_no-last-partition.bin

我第一次把 GPT 文件错传到了 http://192.168.1.1/ 根路径。根路径是刷 ROM 的,不是刷 GPT 的;好在应该被 U-Boot 自动校验拦截失败了。遇到这种情况不要重复乱刷,等页面进度条消失,确认 http://192.168.1.1/http://192.168.1.1/img.html 都还能打开后,再回到正确的 /img.html 重新刷 GPT。

GPT 刷完后等两分钟。教程说此时路由器会长亮红灯。随后断电,按住 reset 再插电,看到红灯闪烁后变成蓝灯常亮再松开 reset,重新进入:

1
http://192.168.1.1

然后刷 ROM。本次使用的是 ZqinKing/wrt_release 里的 LibWrt/QWrt 固件,注意选择 jdcloud_re-cs-02 对应型号:

1
https://github.com/ZqinKing/wrt_release/releases/download/26.05.18_21.17.46_jdcloud_ipq60xx_libwrt/libwrt-qualcommax-ipq60xx-jdcloud_re-cs-02-squashfs-factory.bin

上传到 http://192.168.1.1/ 根路径刷入。刷完等待路由器亮绿灯。

6. 首次进入 LibWrt

路由器亮绿灯后,拔掉电脑和雅典娜之间的网线,把电脑网卡恢复为自动 DHCP。

连接雅典娜 Wi-Fi:

1
Wi-Fi 密码: 12345678

访问后台:

1
http://192.168.1.1

默认登录:

1
2
用户名: root
密码: 12345678

进入后可以看到 LibWrt 后台:

LibWrt 后台

由于 192.168.1.1 很容易和家里其他路由器冲突,建议第一时间把 LAN IP 改成不冲突的网段,例如 192.168.101.1

1
2
3
uci set network.lan.ipaddr='192.168.101.1'
uci commit network
/etc/init.d/network restart

网络重启后当前连接会断开,电脑重新获取 IP 后访问:

1
http://192.168.101.1

7. 分区格式化和挂载

前面刷入的 GPT 文件名里带 no-last-partition,意思是没有预先创建最后那个大容量数据分区。刷完 LibWrt 后,系统能正常运行,但大约 100GB+ 的 eMMC 空间还没有被使用。

先检查当前状态:

1
2
3
4
5
df -h
cat /proc/partitions
blkid
block info 2>/dev/null
ls /dev/mmcblk0p*

本机刷完后可以看到:

1
/dev/loop0 1.9G 98.0M 1.8G 5% /overlay

说明系统当前 overlay 已经有约 1.9G,可以正常使用;但 /proc/partitions 里只有 mmcblk0p1mmcblk0p26,没有原厂系统里的大容量 mmcblk0p27

安装分区和文件系统工具:

1
2
opkg update
opkg install cfdisk e2fsprogs block-mount

进入分区工具:

1
cfdisk /dev/mmcblk0

只操作最下面的 Free space

1
2
3
4
5
New
大小使用默认值
Write
输入 yes
Quit

这一步不是重做整个盘的分区表,而是在最后未分配空间里新建一个数据分区。退出后先确认新增了 /dev/mmcblk0p27,不要急着格式化:

1
2
3
cat /proc/partitions
ls /dev/mmcblk0p*
blkid /dev/mmcblk0p27 2>/dev/null || echo "p27 has no filesystem yet"

确认分区号正确后,后续格式化和挂载直接用 LibWrt 后台 UI 做。

在后台磁盘页面拖动横向滚动条,找到新建分区对应行右侧的 编辑

磁盘页面找到编辑

进入编辑后选择格式化。

选择格式化

文件系统选择 ext4,确认后等待格式化完成。大分区格式化需要等一会儿,不要中途断电。

格式化为 ext4

挂载点这里有三个容易混淆的选项:

选项 含义 本次选择
根文件系统 / 直接挂到系统根目录,会遮住当前系统目录 不选
作为外部 overlay /overlay 把大分区变成 OpenWrt 的系统可写层 暂时不选
自定义 自己指定挂载点,例如 /opt 选择这个

本次最终选择自定义挂载到 /opt。这样系统原来的 /overlay 不动,大分区用于 Docker、iStore、下载、NAS、插件数据等应用场景,风险比直接迁移 /overlay 小。

进入挂载点页面,点击 添加

添加挂载点

挂载点选择 自定义,输入 /opt,也就是挂载到 /opt。然后保存并应用,最后重启路由器。

自定义挂载到 /opt

重启后检查:

1
2
3
4
5
6
df -h
mount | grep -E '(/opt|mmcblk0p27)'
blkid /dev/mmcblk0p27
cat /etc/config/fstab
block info | grep -E '(mmcblk0p27|/opt)'
ls -lah /opt

本机验证结果:

1
2
3
/dev/mmcblk0p27 109.2G 32.0K 103.6G 0% /opt
/dev/mmcblk0p27 on /opt type ext4 (rw,relatime)
/dev/mmcblk0p27: UUID="4007a8a3-c2cf-48bc-b2be-2b79ff0fa7d4" BLOCK_SIZE="4096" TYPE="ext4"

/etc/config/fstab 里也有对应的自动挂载项:

1
2
3
4
config mount
 option enabled '1'
 option uuid '4007a8a3-c2cf-48bc-b2be-2b79ff0fa7d4'
 option target '/opt'

block info 显示:

1
/dev/mmcblk0p27: UUID="4007a8a3-c2cf-48bc-b2be-2b79ff0fa7d4" VERSION="1.0" MOUNT="/opt" TYPE="ext4"

说明重启后 /dev/mmcblk0p27 已经自动挂载到 /opt,大容量空间可用。

APlayer 歌词不显示?先把 LRC 时间戳规范化

2026-06-04 18:43:00

Featured image of post APlayer 歌词不显示?先把 LRC 时间戳规范化

一次性的小坑记录,面向人阅读。结论先放这:APlayer 歌词某些行不显示,十有八九是时间戳格式不对,不是歌词内容缺了。

症状

给博客的 APlayer 播放器挂歌词(.lrc)后,有些歌一切正常,有些歌却有几行死活不显示 / 不滚动——可歌词文件里那几行明明在。控制台也不报错,就是静悄悄地少了几句。

根因

APlayer 的 LRC 解析器对时间戳挑食:它只稳定接受 [mm:ss.xx][mm:ss.xxx](两位或三位毫秒)这种"规整"写法。遇到下面这些松散写法,它会直接跳过整行

  • [02:54.4] —— 只有一位小数
  • [2:54.40] —— 分钟只有一位
  • [02:54] 之外各种位数不齐的组合

而很多歌词来源(网易云导出、第三方站点、手抄)恰恰就是这种松散格式。于是同一份歌单里,规整的歌好好的,松散的歌就缺行——表现得很"随机",其实规律就是时间戳。

修复:发布前规范化时间戳

思路很简单:把每个时间戳重算成总秒数,再补零、把毫秒补齐到两/三位。核心就一段正则替换:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import re
from decimal import Decimal, ROUND_HALF_UP

LRC_TS = re.compile(r"\[(\d{1,2}):(\d{1,2})(?:\.(\d+))?\]")

def _normalize(m: re.Match) -> str:
 mm, ss, frac = m.group(1), m.group(2), m.group(3)
 total = int(mm) * 60 + int(ss)
 if frac is None:
 return f"[{total // 60:02d}:{total % 60:02d}]"
 if len(frac) in (2, 3): # 已规整
 return f"[{total // 60:02d}:{total % 60:02d}.{frac}]"
 if len(frac) == 1: # [02:54.4] -> .400
 ms = int(frac) * 100
 else: # 四位以上,四舍五入到毫秒
 ms = int((Decimal(f"0.{frac}") * 1000).quantize(Decimal("1"), ROUND_HALF_UP))
 if ms >= 1000:
 total, ms = total + 1, 0
 return f"[{total // 60:02d}:{total % 60:02d}.{ms:03d}]"

def normalize_lrc(text: str) -> str:
 return "\n".join(LRC_TS.sub(_normalize, line) for line in text.splitlines())

[02:54.4][02:54.400][2:5][02:05],APlayer 就都认了。

现在:已经自动化,不用再手动改

这一步已经接进 lihan3238/music 仓库的 CI——lrc_compat.py 里的 normalize_lrc_filetest.py 调用,music-sync.ymllrc/** 变更时自动跑,发布前自动规范化。所以现在往歌单里丢任意来源的 .lrc,流水线会自己把时间戳修好,前端再也不会缺行。

一句话

APlayer 歌词不显示,先查时间戳是不是 [mm:ss.xx] / [mm:ss.xxx],别先怀疑歌词内容或播放器本身。

Hugo Stack v4 迁移地图

2026-06-03 00:00:00

Featured image of post Hugo Stack v4 迁移地图

本文是一次真实迁移的完整走查记录(一次性、面向人阅读)。可被 AI 直接调用、 带可复用 verify 的精简版,见知识卡片 hugo-stack-v4-migration-map/ai/cards/hugo-stack-v4-migration-map.md)。

背景

把本博客从 Stack v3 升级到 v4。验证于 2026-06-03 UTC(北京时间 2026-06-04), 环境为 WSL。适用场景:升级本博客或其他基于 Stack 主题的 Hugo 站点到 v4,尤其 当出现 CI 卡住、自定义 partial 消失、link-card 页面在构建期抓取远程图片、或旧 frontmatter 不再影响页面可见性时。

迁移地图

  • Hugo 版本下限:Stack v4 要求 Hugo >=0.157.0;本博客 CI 使用 0.162.1
  • 模块路径:导入 github.com/CaiJimmy/hugo-theme-stack/v4,并在 go.mod 中 require github.com/CaiJimmy/hugo-theme-stack/v4 v4.x
  • Partial 覆盖:Stack v4 使用 layouts/_partials/**,而非 layouts/partials/**。把自定义 partial 移到那里。
  • 语言键:简体中文是 zh,不是 zh-cn;使用 locale = "zh"defaultContentLanguage = "zh"
  • Favicon:设置 favicon = "favicon.ico" 并把文件放到 assets/ 下; 仅为了向后兼容旧 URL 才保留 static/favicon.ico
  • 侧栏头像:v4 期望 [sidebar] avatar = "img/avatar.jpg",而非 v3 的 [sidebar.avatar] enabled/local/src 对象。
  • 图片处理:v4 把 imageProcessing.cover 改名为 imageProcessing.thumbnail;内容图片处理仍保留 [imageProcessing.content]
  • 隐藏页面:v4 移除了 Stack 的 hidden frontmatter 行为。对必须不出现在 列表中的页面,改用 Hugo 构建选项,例如 build.list = "never"
  • 菜单图标:使用菜单 params.icon;不要再依赖 .Pre
  • OpenGraph 默认图:v4 去掉了旧的默认回退配置;移除 [defaultImage.opengraph]
  • 排序:v4 支持 SortBy = "lastmod"。为了 CI 行为稳定,配合 enableGitInfo = true、完整的 checkout 历史,以及 [frontmatter].lastmod = ["lastmod", "modified", ":git", "date", "publishDate"]
  • Link-card 图片:v4 原生 links 组件会在 Hugo 构建期抓取远程 link 图片。 对本博客,覆盖 article/components/links.html,直接渲染 <img src="...">, 让浏览器加载 favicon,CI 不依赖远程站点。

可用 v4 原生能力替换

  • Markdown alerts 通过 [article.alertIcon] 原生支持;普通 note/tip/warning 块不再需要自定义 admonition shortcode。
  • Mermaid 代码块通过 [article.mermaid] 原生支持;图表不再需要自定义 Mermaid shortcode 或页面级加载器。
  • 文章内容中的外部图片现在参与 Photoswipe v5 画廊/灯箱。
  • 当默认布局够用时,通用 taxonomy 组件可替换定制的 category/tag 组件。
  • 当目标是“最近编辑”排序时,SortBy = "lastmod" 可替换本地的列表 hack。
  • Comentario 与 GDPR cookie consent 是原生选项,但仅在站点有相应隐私/评论 政策时才启用。

需要保留的自定义

  • APlayer 音乐播放器,包括自动列表注入与固定播放 UX。
  • 自定义搜索 query-param 行为。
  • Mastodon 想法时间线。
  • 分享按钮。
  • 密码保护的公开文章包装。
  • CI 友好的、构建期稳定的 link-card 图片渲染。

迁移清单

  1. 在逐个修报错之前,先读官方 v4 升级指南与本地 v4 默认配置。
  2. 更新 Hugo、模块路径、go.modgo.sum、语言键、favicon、头像、图片处理, 以及被移除的配置块。
  3. 把自定义 partial 从 layouts/partials/** 移到 layouts/_partials/**; 同步更新任何 workflow 路径(例如 music sync)。
  4. 扫描 content/frontmatter 中的 hidden: trueimageProcessing.coverlayouts/partials/.Pre,以及被误写成 Markdown 图片的普通链接。
  5. 用接近空的缓存、以 CI 的 Hugo 版本构建;远程抓取警告通常意味着某个非图片 URL 被当作图片,或 links partial 在构建期做了远程工作。
  6. 用 Playwright MCP 走查首页、搜索、关键自定义页面、link-card 页面、一篇文章 页面,以及一个移动端视口。

验证

1
2
HUGO_ENVIRONMENT=production hugo --minify --baseURL https://lihan3238.github.io/ \
 && actionlint && git diff --check

期望:Hugo >=0.157 无警告构建;workflow 通过 lint;diff 无空白错误;合并前 PR 的 Pages 构建通过。若失败,检查模块路径、partial 覆盖位置、 avatar/favicon/imageProcessing 键、隐藏 frontmatter,以及构建期远程资源抓取。

基于 OpenWrt 和 WireGuard 的远程组网与运维方案

2026-06-02 20:00:00

Featured image of post 基于 OpenWrt 和 WireGuard 的远程组网与运维方案

基于 OpenWrt 和 WireGuard 的远程组网与运维方案

1. 目标

用一台有公网入口的 Rocky Linux VPS 做 WireGuard Hub,把宿舍 OpenWrt、工位 Linux、工位 Windows 等设备接入同一个私有网段,并通过宿舍 OpenWrt 远程唤醒宿舍 Windows PC。

本文不是 WireGuard 原理课,而是一份部署和维护手册。以后忘了命令、要新增 peer、要排查握手和路由问题时,优先按本文检查。

当前策略:

  • VPS 只做 WireGuard Hub 和转发中心。
  • 宿舍 OpenWrt 接入 WireGuard,并代表宿舍 LAN 192.168.1.0/24
  • 工位 Linux/Windows 作为单设备 peer 接入,只宣告自己的 /32 地址。
  • 不再尝试在 RT-AC88U 386.14_2 上强行部署 WireGuard 主链路。
  • 旧 OpenVPN 可暂时保留,继续管理旧网段,等 WireGuard 稳定后再逐步替换。

注意:本文是公开博客版本,所有私钥、公钥、MAC 地址、VPS 公网 IP 都用占位符表示。真实值只应保存在对应设备本机配置中,不要放进 GitHub、截图或聊天记录。


2. 当前拓扑

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
VPS / Rocky Linux
 wg0: 10.77.0.1/24
 UDP: 51820
 PublicKey: VPS_PUBLIC_KEY

宿舍 OpenWrt
 wg0: 10.77.0.2/32
 LAN: 192.168.1.0/24
 LAN gateway: 192.168.1.1
 LAN device: br-lan
 PublicKey: DORM_OPENWRT_PUBLIC_KEY

工位 Linux / ThinkStation
 wg0: 10.77.0.101/32
 PublicKey: OFFICE_LINUX_PUBLIC_KEY

工位 Windows
 建议地址: 10.77.0.102/32
 类型: 单设备 peer,不宣告整个 192.168.50.0/24

旧 OpenVPN
 10.88.0.0/24
 192.168.50.0/24 可暂时继续由 OpenVPN 管理

推荐访问方式:

1
2
3
4
5
6
7
8
访问 VPS: 10.77.0.1
访问宿舍 OpenWrt: 10.77.0.2 或 192.168.1.1
访问工位 Linux: 10.77.0.101
访问工位 Windows: 10.77.0.102
访问宿舍 PC: 192.168.1.x

远程唤醒宿舍 PC:
 ssh [email protected] /root/wake-dorm-pc.sh

3. 地址规划与设计原则

3.1 网段规划

当前网段:

1
2
3
4
WireGuard 主网段: 10.77.0.0/24
旧 OpenVPN 网段: 10.88.0.0/24
宿舍 LAN: 192.168.1.0/24
工位 LAN: 192.168.50.0/24

后续如果有条件统一调整 LAN,建议改成不容易冲突的编号:

1
2
宿舍 LAN: 192.168.201.0/24
工位 LAN: 192.168.202.0/24

但当前阶段不强行修改 LAN。远程组网最怕“边调边失联”,先把 WireGuard 跑稳,再做地址治理。

3.2 Hub-and-Spoke,而不是全互联

本方案采用:

1
所有 peer -> VPS Hub -> 其他 peer / 宿舍 LAN

理由:

  • VPS 有公网入口,最适合做固定 Endpoint。
  • 宿舍 OpenWrt 通常在 NAT 后面,主动连 VPS 更稳定。
  • 工位 Linux/Windows 作为单机 peer 管理更清晰,不把工位 LAN 路由责任塞给普通终端。
  • 新增 peer 只需要改 VPS 和新设备,不需要每台设备互相加配置。

3.3 AllowedIPs 的核心规则

WireGuard 的 AllowedIPs 同时承担两件事:

1
2
1. 这个 peer 允许使用哪些源 IP
2. 哪些目标 IP 应该发给这个 peer

因此它既像访问控制列表,又像路由表。配置错误时,经常表现为“有 handshake,但 ping 不通”。

规则:

  • VPS 上:哪个 peer 真正负责某个网段,哪个 peer 才能宣告这个网段。
  • 宿舍 OpenWrt:可以宣告 10.77.0.2/32192.168.1.0/24
  • 工位 Linux/Windows:只宣告自己的 10.77.0.x/32
  • 工位 Linux/Windows 不宣告 192.168.50.0/24,除非它真的是工位网关。
  • OpenWrt 客户端侧的 AllowedIPs 写“远端目标”,不要把自己的本地 LAN 写进去。

错误示例:

1
2
3
4
5
6
7
[Peer]
# dorm-openwrt
AllowedIPs = 10.77.0.2/32, 192.168.1.0/24

[Peer]
# dorm-laptop
AllowedIPs = 10.77.0.30/32, 192.168.1.0/24

这里两个 peer 同时宣告 192.168.1.0/24,会造成路由冲突。

正确示例:

1
2
3
4
5
6
7
[Peer]
# dorm-openwrt
AllowedIPs = 10.77.0.2/32, 192.168.1.0/24

[Peer]
# dorm-laptop
AllowedIPs = 10.77.0.30/32

4. VPS / Rocky Linux 服务端

4.1 安装与检查

1
2
3
4
5
sudo dnf install -y wireguard-tools

sudo modprobe wireguard
lsmod | grep wireguard
wg --version

正常应看到:

1
2
wireguard ...
wireguard-tools v...

4.2 生成服务端密钥

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
sudo install -d -m 700 /etc/wireguard

sudo bash -c '
set -e
umask 077

if [ ! -f /etc/wireguard/server_private.key ]; then
 wg genkey > /etc/wireguard/server_private.key
fi

wg pubkey < /etc/wireguard/server_private.key > /etc/wireguard/server_public.key

chmod 600 /etc/wireguard/server_private.key
chmod 644 /etc/wireguard/server_public.key
'

查看公钥:

1
sudo cat /etc/wireguard/server_public.key

注意:

1
2
3
server_private.key 只留在 VPS。
server_public.key 复制给各客户端。
如果私钥贴到聊天、文档或 GitHub,直接重新生成该 peer 的密钥。

4.3 VPS 配置 /etc/wireguard/wg0.conf

1
sudo nano /etc/wireguard/wg0.conf

推荐模板:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
[Interface]
# Rocky VPS WireGuard Hub
Address = 10.77.0.1/24
ListenPort = 51820
PrivateKey = VPS_PRIVATE_KEY
SaveConfig = false

[Peer]
# dorm-openwrt
PublicKey = DORM_OPENWRT_PUBLIC_KEY
AllowedIPs = 10.77.0.2/32, 192.168.1.0/24

[Peer]
# office-linux-thinkstation
PublicKey = OFFICE_LINUX_PUBLIC_KEY
AllowedIPs = 10.77.0.101/32

# [Peer]
# # office-windows
# PublicKey = OFFICE_WINDOWS_PUBLIC_KEY
# AllowedIPs = 10.77.0.102/32

建议固定:

1
SaveConfig = false

原因是配置文件才是真源。否则运行时的临时变更可能被 wg-quick 写回文件,长期维护时容易混乱。

4.4 开启 IPv4 转发

1
2
3
4
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system

sysctl net.ipv4.ip_forward

期望:

1
net.ipv4.ip_forward = 1

4.5 配置 firewalld

放行 WireGuard 端口:

1
2
3
4
sudo firewall-cmd --add-port=51820/udp --permanent
sudo firewall-cmd --reload

sudo firewall-cmd --list-ports

建议把 wg0 放入 trusted zone,减少 VPN 内部转发被 firewalld 拦截的概率:

1
2
3
4
sudo firewall-cmd --permanent --zone=trusted --add-interface=wg0
sudo firewall-cmd --reload

sudo firewall-cmd --get-active-zones

如果 VPS 还有云厂商安全组,也要放行:

1
入站 UDP 51820

4.6 启动与维护

启动:

1
sudo systemctl enable --now wg-quick@wg0

重启:

1
sudo systemctl restart wg-quick@wg0

停止:

1
sudo systemctl stop wg-quick@wg0

查看状态:

1
2
3
sudo systemctl status wg-quick@wg0 --no-pager
sudo wg show
ip addr show wg0

查看关键路由:

1
ip route | grep -E '10\.77|10\.88|192\.168\.1|192\.168\.50|default'

正常状态应至少包含:

1
2
3
default via ... dev ens...
10.77.0.0/24 dev wg0 proto kernel scope link src 10.77.0.1
192.168.1.0/24 dev wg0

如果旧 OpenVPN 仍在,可能还会看到:

1
2
10.88.0.0/24 via ... dev tun0
192.168.50.0/24 via ... dev tun0

5. 宿舍 OpenWrt 配置

5.1 当前接口确认

宿舍 OpenWrt 当前 LAN:

1
2
3
4
5
6
7
network.lan.device='br-lan'
network.lan.ipaddr='192.168.1.1'
network.lan.netmask='255.255.255.0'

br-lan: 192.168.1.1/24
wg0: 10.77.0.2/32
wan: pppoe-wan

后续 WOL 必须从 br-lan 发,不要从 wg0pppoe-wan 发。

5.2 安装 WireGuard

1
2
3
4
opkg update
opkg install wireguard-tools kmod-wireguard luci-proto-wireguard

wg --version

5.3 生成 OpenWrt 密钥

1
2
3
4
5
6
umask 077
wg genkey > /etc/wireguard_openwrt_private.key
wg pubkey < /etc/wireguard_openwrt_private.key > /etc/wireguard_openwrt_public.key

chmod 600 /etc/wireguard_openwrt_private.key
cat /etc/wireguard_openwrt_public.key

将输出的 DORM_OPENWRT_PUBLIC_KEY 填到 VPS 的 dorm-openwrt peer 中。

5.4 用 UCI 配置 wg0

先设置变量:

1
2
3
4
OPENWRT_PRIV="$(cat /etc/wireguard_openwrt_private.key)"
VPS_PUB="VPS_PUBLIC_KEY"
VPS_HOST="VPS_PUBLIC_IP_OR_DOMAIN"
VPS_PORT="51820"

配置接口和 peer:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
uci -q delete network.wg0
uci -q delete network.wg0_peer_vps

uci set network.wg0='interface'
uci set network.wg0.proto='wireguard'
uci set network.wg0.private_key="$OPENWRT_PRIV"
uci add_list network.wg0.addresses='10.77.0.2/32'

uci set network.wg0_peer_vps='wireguard_wg0'
uci set network.wg0_peer_vps.description='rocky-vps'
uci set network.wg0_peer_vps.public_key="$VPS_PUB"
uci set network.wg0_peer_vps.endpoint_host="$VPS_HOST"
uci set network.wg0_peer_vps.endpoint_port="$VPS_PORT"
uci set network.wg0_peer_vps.persistent_keepalive='25'
uci set network.wg0_peer_vps.route_allowed_ips='1'
uci add_list network.wg0_peer_vps.allowed_ips='10.77.0.0/24'

uci commit network
/etc/init.d/network reload

关键点:

1
2
3
OpenWrt 客户端侧 AllowedIPs 写远端目标。
宿舍本地 LAN 192.168.1.0/24 不写在 OpenWrt 对 VPS 的 AllowedIPs 里。
VPS 端才写:AllowedIPs = 10.77.0.2/32, 192.168.1.0/24

5.5 修改 OpenWrt AllowedIPs

查看:

1
uci show network.wg0_peer_vps

只保留 WireGuard 网段:

1
2
3
4
5
6
7
uci delete network.wg0_peer_vps.allowed_ips 2>/dev/null

uci add_list network.wg0_peer_vps.allowed_ips='10.77.0.0/24'
uci set network.wg0_peer_vps.route_allowed_ips='1'

uci commit network
/etc/init.d/network reload

如果将来某远端子网也通过 VPS 可达,再按需添加,例如:

1
2
3
uci add_list network.wg0_peer_vps.allowed_ips='192.168.50.0/24'
uci commit network
/etc/init.d/network reload

当前不建议把工位 192.168.50.0/24 交给 WireGuard,除非工位网关也接入 WireGuard。

5.6 配置 OpenWrt 防火墙

创建 vpn zone,并允许 lan <-> vpn

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
uci -q delete firewall.vpn
uci -q delete firewall.lan_to_vpn
uci -q delete firewall.vpn_to_lan

uci set firewall.vpn='zone'
uci set firewall.vpn.name='vpn'
uci add_list firewall.vpn.network='wg0'
uci set firewall.vpn.input='ACCEPT'
uci set firewall.vpn.output='ACCEPT'
uci set firewall.vpn.forward='ACCEPT'
uci set firewall.vpn.masq='0'
uci set firewall.vpn.mtu_fix='1'

uci set firewall.lan_to_vpn='forwarding'
uci set firewall.lan_to_vpn.src='lan'
uci set firewall.lan_to_vpn.dest='vpn'

uci set firewall.vpn_to_lan='forwarding'
uci set firewall.vpn_to_lan.src='vpn'
uci set firewall.vpn_to_lan.dest='lan'

uci commit firewall
/etc/init.d/firewall restart

检查:

1
uci show firewall | grep -E 'vpn|wg0'

5.7 OpenWrt 测试

在 OpenWrt 上:

1
2
3
4
5
wg show
ip addr show wg0
ip route | grep 10.77

ping -c 4 10.77.0.1

从 VPS 测:

1
2
ping -c 4 10.77.0.2
ping -c 4 192.168.1.1

如果 10.77.0.2 通,但 192.168.1.1 不通,优先查:

1
2
3
VPS 上 dorm-openwrt 的 AllowedIPs 是否包含 192.168.1.0/24
OpenWrt 防火墙是否允许 vpn -> lan
VPS 是否开启 net.ipv4.ip_forward

6. 工位 Linux 单设备 Peer

6.1 安装

Ubuntu / Debian:

1
2
sudo apt update
sudo apt install -y wireguard

Rocky / RHEL / Fedora:

1
sudo dnf install -y wireguard-tools

6.2 生成密钥

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
sudo install -d -m 700 /etc/wireguard

sudo bash -c '
set -e
umask 077

if [ ! -f /etc/wireguard/client_private.key ]; then
 wg genkey > /etc/wireguard/client_private.key
fi

wg pubkey < /etc/wireguard/client_private.key > /etc/wireguard/client_public.key

chmod 600 /etc/wireguard/client_private.key
chmod 644 /etc/wireguard/client_public.key
'

查看公钥:

1
sudo cat /etc/wireguard/client_public.key

把输出的 OFFICE_LINUX_PUBLIC_KEY 加到 VPS。

6.3 Linux 配置 /etc/wireguard/wg0.conf

1
sudo nano /etc/wireguard/wg0.conf

配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[Interface]
# office-linux-thinkstation
Address = 10.77.0.101/32
PrivateKey = OFFICE_LINUX_PRIVATE_KEY
SaveConfig = false

[Peer]
# rocky-vps
PublicKey = VPS_PUBLIC_KEY
Endpoint = VPS_PUBLIC_IP_OR_DOMAIN:51820
AllowedIPs = 10.77.0.0/24, 192.168.1.0/24
PersistentKeepalive = 25

说明:

  • 10.77.0.0/24 用于访问 WireGuard 内所有 peer。
  • 192.168.1.0/24 用于访问宿舍 LAN。
  • 如果只需要访问 WireGuard peer,不需要访问宿舍 LAN,可以删掉 192.168.1.0/24
  • 如果短期只在同一校园网内测试,可以临时把 Endpoint 写成 VPS 当前内网可达 IP;长期使用应改成 VPS 公网 IP 或稳定域名。

权限:

1
sudo chmod 600 /etc/wireguard/wg0.conf

启动:

1
sudo systemctl enable --now wg-quick@wg0

重启:

1
sudo systemctl restart wg-quick@wg0

检查:

1
2
3
sudo wg show
ip addr show wg0
ip route | grep -E '10\.77|192\.168\.1'

测试:

1
2
3
ping -c 4 10.77.0.1
ping -c 4 10.77.0.2
ping -c 4 192.168.1.1

7. Windows 单设备 Peer

7.1 WireGuard 客户端配置

安装 Windows 官方 WireGuard 客户端后:

1
Add Tunnel -> Add empty tunnel

客户端会自动生成 PrivateKeyPublicKey

Windows 配置模板:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[Interface]
# office-windows
PrivateKey = OFFICE_WINDOWS_PRIVATE_KEY
Address = 10.77.0.102/32

[Peer]
# rocky-vps
PublicKey = VPS_PUBLIC_KEY
Endpoint = VPS_PUBLIC_IP_OR_DOMAIN:51820
AllowedIPs = 10.77.0.0/24, 192.168.1.0/24
PersistentKeepalive = 25

VPS 上添加:

1
2
3
4
[Peer]
# office-windows
PublicKey = OFFICE_WINDOWS_PUBLIC_KEY
AllowedIPs = 10.77.0.102/32

应用 VPS 配置:

1
sudo systemctl restart wg-quick@wg0

7.2 Windows 测试

PowerShell:

1
2
3
ping 10.77.0.1
ping 10.77.0.2
ping 192.168.1.1

注意:

1
2
3
Windows 防火墙经常默认拦 ICMP。
别人 ping Windows 的 10.77.0.102 不通,不一定代表 WireGuard 错。
RDP、SSH、HTTP 等实际服务端口测试更可靠。

8. Clash / v2ray TUN 与 WireGuard 共存

8.1 问题现象

TUN 模式代理可能截获 WireGuard 外层 UDP 流量:

1
本机 -> VPS_PUBLIC_IP:51820/udp

如果这条流量被代理接管,常见现象:

1
2
3
4
WireGuard latest handshake 不更新
transfer 只有 sent,没有 received
ping 10.77.0.1 不通
开 TUN 就断,关 TUN 就恢复

8.2 规则原则

必须 DIRECT

1
2
3
4
5
6
7
VPS_PUBLIC_IP/32
UDP 51820
10.77.0.0/24
10.88.0.0/24
192.168.1.0/24
192.168.50.0/24
RFC1918 私网

Clash 规则示例:

1
2
3
4
5
6
7
8
rules:
 - IP-CIDR,VPS_PUBLIC_IP/32,DIRECT,no-resolve
 - IP-CIDR,10.77.0.0/24,DIRECT
 - IP-CIDR,10.88.0.0/24,DIRECT
 - IP-CIDR,192.168.1.0/24,DIRECT
 - IP-CIDR,192.168.50.0/24,DIRECT
 - GEOIP,PRIVATE,DIRECT
 - MATCH,PROXY

建议 WireGuard Endpoint 用 IP,不用域名:

1
Endpoint = VPS_PUBLIC_IP:51820

这样可以避免 DNS 查询被 TUN 代理影响。若必须用域名,则 DNS 也要确认不被代理规则错误接管。


9. Wake-on-LAN 远程唤醒宿舍 PC

9.1 正确链路

推荐链路:

1
2
3
4
5
6
7
远端设备
 -> WireGuard
 -> VPS
 -> WireGuard
 -> 宿舍 OpenWrt 10.77.0.2
 -> OpenWrt 在 br-lan 发送 WOL 魔术包
 -> 唤醒宿舍 Windows PC

不要优先尝试远端直接向 192.168.1.255 广播。WireGuard 是三层隧道,广播不一定穿透。最稳的是让 OpenWrt 在宿舍 LAN 本地发 WOL。

9.2 Windows PC 设置

BIOS / UEFI:

1
2
3
Wake on LAN: Enabled
Power On By PCI-E / Resume By PCI-E Device: Enabled
ErP / EuP / Deep Sleep: Disabled

MSI 主板进 BIOS:

1
2
开机连续按 Delete
或 Windows 执行:shutdown /r /fw /t 0

Windows 网卡设置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
设备管理器 -> 网络适配器 -> 有线网卡 -> 属性

电源管理:
 允许此设备唤醒计算机:开启
 只允许魔术封包唤醒计算机:开启

高级:
 Wake on Magic Packet:Enabled
 Shutdown Wake-On-Lan:Enabled
 Wake from S5:Enabled
 Wake on Pattern Match:建议关闭或不依赖

如果要从关机状态唤醒,建议关闭 Fast Startup:

1
控制面板 -> 电源选项 -> 选择电源按钮的功能 -> 更改当前不可用设置 -> 取消“启用快速启动”

如果只从睡眠唤醒,可以先不关 Fast Startup,先把最小链路测通。

9.3 OpenWrt 安装 WOL 工具

1
2
opkg update
opkg install etherwake luci-app-wol

只用命令行:

1
opkg install etherwake

9.4 查询 Windows PC MAC

Windows:

1
ipconfig /all

找有线网卡:

1
Physical Address / 物理地址

OpenWrt 上也可以查:

1
2
cat /tmp/dhcp.leases
ip neigh show dev br-lan

MAC 格式统一写成:

1
AA:BB:CC:DD:EE:FF

9.5 手动唤醒

OpenWrt 上执行:

1
etherwake -i br-lan AA:BB:CC:DD:EE:FF

宿舍 LAN 接口是:

1
br-lan

不要从 wg0pppoe-wan 发 WOL。

9.6 LuCI WOL 设置

如果使用 LuCI:

1
2
3
4
服务 -> Wake on LAN
Interface: br-lan
MAC: Windows 有线网卡 MAC
Send to broadcast address: 开启

建议开启 Send to broadcast address

理由:

1
2
PC 睡眠或关机后,OpenWrt 可能没有它的 ARP 记录。
广播发送更容易被网卡收到。

不要做:

1
2
不要从 WAN 端口转发 UDP 7/9 到 LAN
不要允许公网直接发 WOL 广播

9.7 固定唤醒脚本

1
2
3
4
5
6
cat > /root/wake-dorm-pc.sh <<'EOF'
#!/bin/sh
etherwake -i br-lan AA:BB:CC:DD:EE:FF
EOF

chmod +x /root/wake-dorm-pc.sh

远程唤醒:

1
ssh [email protected] /root/wake-dorm-pc.sh

宿舍内网唤醒:

1
ssh [email protected] /root/wake-dorm-pc.sh

9.8 WOL 排障顺序

先测试睡眠唤醒:

1
2
Windows PC 睡眠
OpenWrt 执行 etherwake

再测试关机唤醒。

如果睡眠能醒、关机不能醒,优先检查:

1
2
3
4
5
BIOS ErP 是否关闭
BIOS Resume By PCI-E 是否开启
Windows Fast Startup 是否关闭
网卡高级选项 Shutdown Wake-On-Lan 是否开启
PC 是否使用有线网卡

如果完全不能醒,优先检查:

1
2
3
4
MAC 是否是有线网卡 MAC
PC 是否接在 br-lan 下
网口关机后灯是否亮
网线、交换机、路由器 LAN 口是否正常

10. 常用排障命令

10.1 WireGuard 通用检查

1
2
3
sudo wg show
ip addr show wg0
ip route

重点看:

1
2
3
4
latest handshake: 是否更新
transfer: 是否双向增长
allowed ips: 是否正确
endpoint: 是否是正确 VPS 地址

典型问题:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
只有 sent,没有 received:
 对方没添加 peer
 公钥不匹配
 Endpoint 不通
 防火墙或云安全组没放行 UDP 51820

有 handshake,但 ping 不通:
 路由或 AllowedIPs 错
 IP 转发没开
 firewalld / OpenWrt 防火墙拦截
 Windows 防火墙拦 ICMP

10.2 VPS 检查

1
2
3
4
5
6
sudo wg show
ip route | grep -E '10\.77|10\.88|192\.168\.1|192\.168\.50|default'
sysctl net.ipv4.ip_forward
sudo firewall-cmd --list-ports
sudo firewall-cmd --get-active-zones
sudo systemctl status wg-quick@wg0 --no-pager

日志:

1
sudo journalctl -u wg-quick@wg0 -n 100 --no-pager

应用配置:

1
sudo systemctl restart wg-quick@wg0

10.3 OpenWrt 检查

1
2
3
4
5
6
wg show
ip addr show wg0
ip route | grep -E '10\.77|192\.168\.1'
uci show network.wg0
uci show network.wg0_peer_vps
uci show firewall | grep -E 'vpn|wg0'

重载网络:

1
/etc/init.d/network reload

重启防火墙:

1
/etc/init.d/firewall restart

10.4 Linux peer 检查

1
2
3
4
5
6
sudo wg show
ip addr show wg0
ip route | grep -E '10\.77|192\.168\.1'
ping -c 4 10.77.0.1
ping -c 4 10.77.0.2
ping -c 4 192.168.1.1

重启:

1
sudo systemctl restart wg-quick@wg0

10.5 连通性测试顺序

从工位 Linux 测:

1
2
3
ping -c 4 10.77.0.1 # VPS
ping -c 4 10.77.0.2 # 宿舍 OpenWrt WG
ping -c 4 192.168.1.1 # 宿舍 OpenWrt LAN

从 VPS 测:

1
2
3
ping -c 4 10.77.0.2
ping -c 4 192.168.1.1
ping -c 4 10.77.0.101

从宿舍 OpenWrt 测:

1
2
ping -c 4 10.77.0.1
ping -c 4 10.77.0.101

判断顺序:

1
2
3
1. 先确认 peer 到 VPS 的 10.77.0.1 通。
2. 再确认 peer 之间的 10.77.0.x 通。
3. 最后确认跨到宿舍 LAN 的 192.168.1.0/24 通。

不要一开始就测 RDP、SSH 或 WOL。先把三层连通性验证完。


11. 新增 peer 标准流程

11.1 选择 IP

建议:

1
2
3
4
手机: 10.77.0.10/32
笔记本: 10.77.0.11/32
工位 Linux: 10.77.0.101/32
工位 Win: 10.77.0.102/32

11.2 在新设备生成密钥

Linux:

1
2
3
umask 077
wg genkey > private.key
wg pubkey < private.key > public.key

Windows / macOS / 手机:

1
2
WireGuard App -> Add empty tunnel
自动生成 PrivateKey / PublicKey

11.3 VPS 添加 peer

1
2
3
4
[Peer]
# device-name
PublicKey = DEVICE_PUBLIC_KEY
AllowedIPs = 10.77.0.x/32

重启 VPS:

1
sudo systemctl restart wg-quick@wg0

11.4 客户端配置

单设备客户端模板:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[Interface]
Address = 10.77.0.x/32
PrivateKey = DEVICE_PRIVATE_KEY
SaveConfig = false

[Peer]
PublicKey = VPS_PUBLIC_KEY
Endpoint = VPS_PUBLIC_IP:51820
AllowedIPs = 10.77.0.0/24, 192.168.1.0/24
PersistentKeepalive = 25

如果只想访问 WireGuard peer,不访问宿舍 LAN:

1
AllowedIPs = 10.77.0.0/24

如果想全局代理,才写:

1
AllowedIPs = 0.0.0.0/0, ::/0

当前不建议默认全局代理。这个方案的目标是远程组网和运维,不是替代所有代理流量。


12. 安全注意事项

12.1 私钥管理

1
2
3
4
PrivateKey 永远只留在本机。
PublicKey 可以复制给 VPS。
不要把 PrivateKey 发到聊天、GitHub、博客、截图。
如果私钥泄露,重新生成该 peer 的密钥,并更新 VPS 上的 PublicKey。

12.2 公网暴露面

VPS 只需要暴露:

1
UDP 51820

不要为了 WOL 暴露:

1
2
3
UDP 7
UDP 9
192.168.1.255 广播转发

WOL 只从 OpenWrt 的 br-lan 本地发。公网只允许进入 WireGuard。

12.3 普通 LAN 设备是否需要安装 WireGuard

宿舍 192.168.1.0/24 下的普通设备不需要安装 WireGuard,也可以访问 10.77.0.0/24,前提是:

1
2
3
4
默认网关是 192.168.1.1
OpenWrt route_allowed_ips=1
OpenWrt 防火墙允许 lan -> vpn
VPS 允许转发

如果普通设备自己也安装 WireGuard,可以,但它只能宣告自己的 /32,不能宣告整个 LAN。

12.4 不要让普通终端假装网关

工位 Linux/Windows 如果只是单设备 peer,就只写:

1
2
AllowedIPs = 10.77.0.101/32
AllowedIPs = 10.77.0.102/32

不要写:

1
AllowedIPs = 192.168.50.0/24

除非这台设备真的是工位 LAN 的默认网关,并且已经配置好 IP 转发、防火墙和回程路由。


13. 当前推荐状态总结

应保持:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
VPS:
 10.77.0.1/24
 peer dorm-openwrt: 10.77.0.2/32, 192.168.1.0/24
 peer office-linux: 10.77.0.101/32
 后续 peer office-windows: 10.77.0.102/32

OpenWrt:
 wg0 10.77.0.2/32
 LAN 192.168.1.1/24 on br-lan
 AllowedIPs to VPS: 10.77.0.0/24
 firewall: lan <-> vpn

Office Linux:
 wg0 10.77.0.101/32
 AllowedIPs: 10.77.0.0/24, 192.168.1.0/24

Office Windows:
 wg0 10.77.0.102/32
 AllowedIPs: 10.77.0.0/24, 192.168.1.0/24

WOL:
 OpenWrt br-lan 发 etherwake
 远程命令: ssh [email protected] /root/wake-dorm-pc.sh

不要做:

1
2
3
4
5
6
不要让 RT-AC88U 386.14_2 强行跑 WireGuard 主链路。
不要把 192.168.50.0/24 交给工位 Linux/Windows 单设备 peer。
不要让 Clash/v2ray TUN 代理 VPS:51820 的 WireGuard 外层流量。
不要从公网转发 UDP 7/9 做 WOL。
不要在多个 peer 上重复宣告同一个 LAN 网段。
不要把 PrivateKey、真实 MAC、VPS 公网 IP 随手贴到公开文档。

14. 一页速查

VPS:

1
2
3
4
5
6
sudo wg show
sudo systemctl restart wg-quick@wg0
ip route | grep -E '10\.77|192\.168\.1|default'
sysctl net.ipv4.ip_forward
sudo firewall-cmd --list-ports
sudo firewall-cmd --get-active-zones

OpenWrt:

1
2
3
4
5
6
wg show
ip route | grep -E '10\.77|192\.168\.1'
uci show network.wg0_peer_vps
uci show firewall | grep -E 'vpn|wg0'
/etc/init.d/network reload
/etc/init.d/firewall restart

Linux peer:

1
2
3
4
5
sudo wg show
ip route | grep -E '10\.77|192\.168\.1'
ping -c 4 10.77.0.1
ping -c 4 10.77.0.2
ping -c 4 192.168.1.1

WOL:

1
ssh [email protected] /root/wake-dorm-pc.sh

如果只记一个排障原则:

1
2
3
4
先看 latest handshake 和 transfer 是否双向增长;
再看 VPS AllowedIPs;
再看 IP 转发;
最后看两端防火墙和本机代理 TUN 规则。

随想 260529_AI

2026-05-29 06:45:00

Featured image of post 随想 260529_AI

失眠与熬夜:红温

敲下了标题,已是凌晨 6:47,一夜未眠,先去吃个早饭。

忘了拍照,雪菜粉丝包和豆腐脑还是比北京的好吃多了,倒是鸡蛋灌饼没冬天热乎好吃了,应该是换了厨子。

经典睡不着红温,索性下床学习通宵算了。好吧,倒也没有那么经典,上次还是大三吧,不过这次不能下床就趴在桌前敲笔记本,得跑去工位了。

其实说实话有点傻逼真的,自以为摆脱思想桎梏的人现在天天晚上为世俗的前途焦虑地睡不着觉,最喜欢玩的人迷上了 Vibe Coding 后一个月没打开过游戏。归根结底,还是被困在了这个毫无希望的学校里。

AI:再谈 AI

回归正题,敲完这篇就回去睡觉了。

对大模型的接触自然要追溯到 2022 年末的 ChatGPT 了,它从 11 月 30 日发布到突破 100 万用户只用了短短五天。我的第一个账号也是在当时注册的,一篇知乎让我花了一个半小时从接码到注册用上了大模型。

ChatGPT

ChatGPT

用上 GPT 后的第一个“工程实践”就震惊了我,它不仅能够根据 Java 老师的作业要求给出能跑的代码,甚至还会授人以渔和代码纠错,这对当时只会机械执行一行行代码的机器人般的我来说简直是个奇迹!

ChatGPT

说实话,后面的一年里,GPT 和其他大模型于我只有一个作用:水掉作业。于是在疫情宅家的日子里,我不是在打游戏就是在打游戏,与此同时 DaleChu 同学似乎在那段时间深耕了键政和计算机技术。

时间一晃来到了 2023 年的九月开学,在 lihan 开始模仿 DaleChu 的博客时,对大模型的使用也慢吞吞地进入了下一个阶段——代码补全。申请了 GitHub Copilot 的学生会员后,代码补全 + ChatGPT 的组合一方面让刚准备开始发奋学习技术的我得以更快上手,另一方面也让我第一次意识到似乎手搓代码就跟提笔写字一样要被淘汰了。

copilot

自此之后,一直到研一,我对大模型的使用与研究便停滞于此了,从 GPT-4 到 GPT-5,对我而言不过是一次次版本号的迭代,以及模型能力的日常提升。除了越来越多人开始使用大模型,越来越多的垂类产品也开始涌现之外,并没有什么特别的感觉。当然,这也源于我对当时那些所谓的“Prompt 工程”的不屑一顾了。如今再看,真的很难想象自己居然纯靠网页 ChatGPT 完成我的 DSSE 毕业设计。

AI 的热情再度被点燃是在研一的十月份了,在 lyx 的教导下,我开始试着接触科研和实验。第一次看到他用 Copilot Vibe Coding 写代码时我简直像极了面对流水线鲜花饼产线的老手艺人——还能这样?

现在说什么都来不及了,Vibe Coding 沾上了就戒不掉了,纵使一开始对失去项目与代码掌控权的疑虑到现在还没完全打消,我已然是无法回到最开始的手搓代码了。

lemoncchi 说,大模型害惨了我们这些没跟上这趟科研快车的程序员。诚然,倘若没有大模型的出现,我们仍然能够凭借编程的技术,在刷 LeetCode、背面经后,一路实习、面试,最终找到一份凭借着剩余价值转移得以维持高薪的码农工作。但现在,可见的未来,简历上的“熟悉 C++”几乎和“熟悉 WPS”要画上等号了,更可恶的是,精心(也许精心吧嘿嘿)维护的博客里的技术贴已经完全失去意义了,也再不会有无知美少女抱着电脑一脸崇拜地请教你某个小技术问题了(说实话感觉主要是 DaleChu 和 lemoncchi 在经历这个,我只能给壮汉讲呜呜呜)。

但倘若不从功利的角度出发,大模型的出现几乎是一个让人美梦成真的奇迹!今年初开始让老程序员们重燃青春的 OpenClaw 和 Hermes,让无数像家父和我导师这样的老程序员们上头了几个月还没退烧,纵使一个月几千的 Token 账单贴在脸前,一个真的能把你的想法实现的魔法机器摆在面前,谁能不心动呢?是啊,记得小时候,看着网上所谓“脚本小子”为游戏制作外挂,听起来好像得学好多年才能到这个地步,现如今十几分钟就能让大模型给我用 YOLOv8 跑起来挂机脚本了;困扰了我两年的博客音乐列表全自动更新功能,也不过是一杯咖啡的功夫就能给我完美实现了。大模型的出现对于不考虑以此谋生的程序员来说,真的是原神了。

中转站:又上头了

导师其实早就紧跟时代了,给我们发模型让我们用 ClaudeCode 做横向项目了,从他的 GitLab 记录来看,过年期间他也上了 OpenClaw 这趟车,直到今天还在兴致满满地安排着他的 Agents 用 Vibe Coding 做各种项目。相比之下,我这个老古董学生倒是在 Copilot 报废后,才勉强开始把他发的 GLM 接入 ClaudeCode 使用。

用 ClaudeCode 的第一天就把卡了我几周的 Bug 轻松秒掉了。

今年三月左右开始,让我白嫖了多年的 Copilot 开始抠搜了,先是旗舰模型被移入更高价位的套餐,然后是使用次数被一次又一次限制,月限、周限、五小时限,一直不想“自费打工”的我总算在四月尾巴坚持不住,开始转向 Codex 和 ClaudeCode 了。

2026 年 4 月 27 日,面对额度耗尽的 GLM,lihan 打开了当年偷瞄 DaleChu 屏幕看到的那个神奇论坛——LinuxDO,在通宵达旦地高强度刷了一天帖子后,终于跟上了这波“中转站 - Vibe Coding - AI Workflow”浪潮。在几个中转站里摸爬滚打了一两天后,从“福利羊毛”区的大善人们的投喂和“跳蚤市场”区的低价 GPT Team 车开始,走上了一趟狂热的 Vibe Coding 之旅。

linuxdo

在牺牲了原本备考数学期末的时间、用 Vibe Coding 给开源项目提交 PR 后,我很快意识到不能把精力砸在抠搜上——强大先进的生产力应当不计代价地立刻投入使用!于是在考完后的当天,我就开始通宵搭建我这个基于 New API + CliProxyAPI 的中转站 lihan API 了。

一扫以往买域名、买服务器、买节点的犹豫,花了五天时间,我便部署好中转站投入使用了。从一开始三天报销掉三个 GPT Plus 的周限,到现在后台额度波澜不惊,即便小小地给周围同学朋友推广使用、企图回血,也很少再出现额度不足的情况了,我这才开始缓慢地真正掌握 Coder Agent 这把尚方宝剑。

cpa

AI 随想:我不管,我就是对的

在 Vibe Coding 中的摸爬滚打难以言喻,总结下来,不过是以下几点:

  • 大模型是革命性的生产力提升,早点投降开始 Vibe Coding 才是正道
  • Vibe Coding 拉低了 Idea 实现的门槛,什么垃圾想法都能硬控我浪费时间精力和 Token,一定要让时间精力花在真正的好想法上
  • 不要在 Vibe Coding 中迷失原本的需求,用 AI 工具要最快最专业最轻量地实现你的需求
  • Vibe Coding 不是门槛级别的技术,长期的投入仍然要聚焦在某一点上
  • 传统开发岗 G 了,搞 Agent 才能真正算是程序员了
  • 始终将社区最好的解决方案、最好的模型拿来就用,不要自己重复造轮子
  • 一个自己的 AI Workflow 是必要的

结语

希望年末我能笑着去香港喜提 MacBook Pro M6 Pro 和 iPhone 18 Pro Max!

Codex 从 Mac 远连 Windows——绕过 pwsh,走 WSL 的 sshd

2026-05-21 00:00:00

Featured image of post Codex 从 Mac 远连 Windows——绕过 pwsh,走 WSL 的 sshd

本文是一次真实配置的完整走查记录(一次性、面向人阅读)。这套环境配好一次就长期 复用,我大概率不会再从头踩坑,所以收成博文而不是知识卡片。

背景与问题

想用 Codex 的 remote-connect 功能从 Mac 驱动一台 Windows 机器。连上之后,Codex 在远端跑 bootstrap 一上来就失败:报 pwsh 看不懂的 token、&&$(...) 之类的 错误,或者客户端报远端 shell 没返回预期的握手。

根因是 shell 不匹配

  • Codex 的远端 bootstrap 假定远端是 POSIX shell(sh / bash)。
  • Windows 自带的 OpenSSH server 默认把 pwsh(或 cmd)当登录 shell,所以那些 一行式 bootstrap 命令在 Codex 启动之前就先炸了。
  • 同一台 Windows 上的 WSL Ubuntu 里有真正的 bash。解法就是把 SSH 会话落进 WSL,而不是落在 Windows 本体。

适用场景:用 Mac 通过 Codex remote-connect(或其他假定 sh 的远程开发工具,比如 JetBrains Gateway、bootstrap 脚本只认 sh 的 VS Code Remote-SSH)去驱动一台装了 WSL 的 Windows 机器,且连接在 bootstrap 阶段挂在 shell 不兼容 / pwsh / command-not-found 上。

不适用:远端本身就是原生 Linux(没有 shell 不匹配)、远程工具明确支持 Windows 侧 pwsh、或者那台 Windows 上压根没装 WSL 发行版。

四步配置

第 1–3 步每台 Windows 主机一次性;第 4 步每台 Mac 一次性。

1. 在 WSL Ubuntu 里装 sshd

在 Windows 上打开 WSL Ubuntu:

1
2
3
sudo apt update
sudo apt install -y openssh-server
sudo service ssh start

确认:

1
2
which sshd
sudo service ssh status

2. 把 Mac 公钥写进 WSL 的 authorized_keys

在 Mac 上:

1
cat ~/.ssh/id_ed25519.pub

复制整行。进到 WSL 里:

1
2
3
4
mkdir -p ~/.ssh
chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys # 粘贴 Mac 公钥,保存
chmod 600 ~/.ssh/authorized_keys

3. 把 Windows 的 :2222 转发到 WSL 的 :22

在 Windows 上以管理员 PowerShell 执行:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
$wslIp = (wsl hostname -I).Trim().Split()[0]

netsh interface portproxy add v4tov4 `
 listenaddress=0.0.0.0 `
 listenport=2222 `
 connectaddress=$wslIp `
 connectport=22

New-NetFirewallRule `
 -DisplayName "WSL SSH 2222" `
 -Direction Inbound `
 -Action Allow `
 -Protocol TCP `
 -LocalPort 2222

4. 在 Mac 的 SSH config 里加一个 Host 条目

编辑 Mac 上的 ~/.ssh/config

1
2
3
4
5
Host lihan_pc_02_wsl
 HostName <windows-host-lan-ip> # 例如 10.88.0.6
 Port 2222
 User <wsl-username>
 IdentityFile ~/.ssh/id_ed25519

然后在 Codex 里把 remote-connect 指向 Host 别名 lihan_pc_02_wsl——Codex 看到 的是一个 POSIX 远端,bootstrap 就能干净跑通。

踩坑点

  • WSL IP 每次重启都会漂。 第 3 步把 $wslIp 当成一个快照写死了。一旦 wsl --shutdown 或 Windows 重启,要重跑 netsh interface portproxy add (先用 netsh interface portproxy delete v4tov4 listenaddress=0.0.0.0 listenport=2222 删掉旧的)。可以把它写成一个开机自动跑的计划任务。
  • 别把代理塞进 git config。 这台机器上别处用的 HTTP 代理 (10.88.0.6:10808)跟这条 SSH 路径无关,别混在一起。
  • 连进 WSL,不是连进 Windows。 如果 Codex 客户端直接落在 Windows 本体的 22 端口,你又回到 pwsh 失败的原点了。整套方案的意义就是用 2222 → WSL:22 这一跳,强制把会话顶进 WSL。

怎么确认连通

在 Mac 上:

1
ssh lihan_pc_02_wsl -o BatchMode=yes -o ConnectTimeout=5 'uname -a'

返回一个 Linux 内核串(形如 Linux ... microsoft-standard-WSL2)且退出码为 0,就说明这台 Host 上的 Codex remote-connect 能用了。

连不上时按顺序排查:

  1. WSL sshd 起没起:sudo service ssh status
  2. Windows portproxy 是否还指向当前 WSL IP:对比 netsh interface portproxy show v4tov4wsl hostname -I (每次 WSL 重启 IP 都会漂);
  3. 防火墙规则 WSL SSH 2222 是否存在;
  4. ~/.ssh/authorized_keys 里有没有 Mac 公钥,且权限是 600。