一套跑在 Debian 13 (trixie) 上的软路由管理系统,提供 43 个功能页面的 Web 管理面板:WAN 接入、PPPoE 多拨、DHCP/DNS、IPv6、防火墙、QoS 限速、 访问控制、DDNS、文件共享、Docker 管理等。 核心设计原则:不重新实现任何协议 PPPoE 交给 ppp,DHCP 交给 dnsmasq,IPv6 RA 交给 radvd,NAT 交给 nftables。 Web 面板只做三件事:渲染原生配置文件 → 语法预检 → 原子写入并 reload。 面板删掉后,系统仍是一台配置完好的 Debian 路由器。 安全默认值 · 安装后不接管网络:不改默认路由、不开 IP 转发、不启动 DHCP 服务 · 所有改网络的动作都需要用户在面板上亲手点「应用」 · 避开 5900 端口,不触碰 VNC / X 会话 · 配置写入前先做语法预检,失败则回滚 架构 · backend/ Python 3 标准库,零第三方依赖(9 个模块) · web/ 原生 JS/CSS,无构建步骤 · 常驻 helper 通过 Unix socket 提供 75 个动作,socket 不可用时回退 subprocess · 鉴权走 X-Token 请求头 打包 · packaging/build-deb.sh → drouter_1.0.0_all.deb(含 6 个 systemd 单元) · scripts/make-offline-deps.sh → 236 个依赖 .deb + 本地仓库索引 · packaging/build-offline-bundle.sh → 自包含离线包,目标机零外网可装 · packaging/build-docker.sh → 容器镜像(体验用) 测试 40 套单元测试 + 3 个前端检查脚本(菜单映射 / 离线渲染 / 手机适配), 全部挂在 devtools/sync.sh 的预检链里。 许可:MIT
429 lines
17 KiB
Bash
429 lines
17 KiB
Bash
#!/bin/bash
|
||
# Debian13 主路由系统 —— 部署脚本
|
||
# 将后端、前端、systemd 服务、sudoers 白名单安装到位。
|
||
# 不会启动任何网络服务,不会修改网络配置。
|
||
set -e
|
||
|
||
# 源码目录可用 DROUTER_SRC 覆盖(一键脚本 drouter-ctl.sh 会传入实际路径)
|
||
SRC="${DROUTER_SRC:-/tmp/drouter-src}"
|
||
OPT=/opt/drouter
|
||
UNIT_WEB=drouter-web
|
||
|
||
echo "=== 1. 创建目录结构 ==="
|
||
mkdir -p $OPT/backend $OPT/web $OPT/data $OPT/snapshots $OPT/certs $OPT/bin $OPT/docs
|
||
mkdir -p /etc/drouter/generated
|
||
mkdir -p /etc/nftables.d
|
||
mkdir -p /var/log/drouter
|
||
mkdir -p /etc/dnsmasq.d
|
||
mkdir -p /var/lib/drouter/trash # 文件管理器"回收站"
|
||
mkdir -p $OPT/dpi # DPI 识别规则库与源码(#25)
|
||
mkdir -p /etc/ppp/ip-up.d /etc/ppp/ip-down.d # PPPoE 多拨策略路由钩子(#23)
|
||
# helper 私有临时目录:/run 为 tmpfs(重启自清),且不归低权用户所有,
|
||
# 避开 /tmp、/var/tmp 的 sticky 位与 fs.protected_regular=2 写保护。
|
||
mkdir -p /run/drouter-helper
|
||
chmod 700 /run/drouter-helper
|
||
|
||
sha_of() { [ -f "$1" ] && sha1sum < "$1" | cut -d' ' -f1 || echo "-"; }
|
||
# shelld 是常驻守护,代码在内存里,不重启就是旧行为。
|
||
# 判断依据必须是「本次源码指纹」vs「上次真正重启过的指纹」(记在 mark 文件里),
|
||
# 不能拿 $SRC 和 $OPT 比 —— install 之后两边同源,cmp 恒等,永远不重启。
|
||
# 也不能拿 install 前的 $OPT 比 —— 若上一轮已经把新文件拷进去了但没重启成功,
|
||
# 这一轮两边又相等,漏网。
|
||
SHELLD_NEW=$(sha_of "$SRC/backend/drouter-shelld.py")
|
||
SHELLD_MARK=/var/lib/drouter/shelld.deployed.sha
|
||
|
||
echo "=== 2. 安装后端 ==="
|
||
install -m 0644 $SRC/backend/render.py $OPT/backend/render.py
|
||
install -m 0644 $SRC/backend/drouter-helper.py $OPT/backend/drouter-helper.py
|
||
install -m 0644 $SRC/backend/drouter-web.py $OPT/backend/drouter-web.py
|
||
install -m 0644 $SRC/backend/theme.py $OPT/backend/theme.py
|
||
# drouter-logd:统一日志采集守护。helper 写出的 drouter-logd.service 直接
|
||
# ExecStart 这个文件,漏装会导致日志定时器起不来(此前确实漏了,务必保留)。
|
||
install -m 0644 $SRC/backend/drouter-logd.py $OPT/backend/drouter-logd.py
|
||
install -m 0644 $SRC/backend/drouter-snapshotd.py $OPT/backend/drouter-snapshotd.py
|
||
install -m 0644 $SRC/backend/drouter-rescue.py $OPT/backend/drouter-rescue.py
|
||
# drouter-shelld:Web 真·PTY 终端的会话守护。它必须由常驻进程持有 PTY
|
||
# (helper 每次都是跑完就退出的新进程,PTY fd 跨不了请求),漏装则终端连不上。
|
||
install -m 0644 $SRC/backend/drouter-shelld.py $OPT/backend/drouter-shelld.py
|
||
install -m 0644 $SRC/backend/drouter-helpd.py $OPT/backend/drouter-helpd.py
|
||
|
||
# 主题之家(#12):自定义主题目录 + 生效主题标记
|
||
mkdir -p /etc/drouter/themes
|
||
echo "default" > /etc/drouter/active-theme
|
||
chmod 644 /etc/drouter/active-theme
|
||
|
||
echo "=== 3. 安装前端 ==="
|
||
install -m 0644 $SRC/web/index.html $OPT/web/index.html
|
||
install -m 0644 $SRC/web/app.css $OPT/web/app.css
|
||
install -m 0644 $SRC/web/app.js $OPT/web/app.js
|
||
if [ -f "$SRC/web/logo.svg" ]; then
|
||
install -m 0644 $SRC/web/logo.svg $OPT/web/logo.svg
|
||
fi
|
||
|
||
echo "=== 3.5 安装运维脚本(自检/回归用)==="
|
||
if [ -d "$SRC/scripts" ]; then
|
||
install -d -m 0755 $OPT/scripts
|
||
for s in "$SRC"/scripts/*.sh; do
|
||
[ -f "$s" ] || continue
|
||
install -m 0755 "$s" "$OPT/scripts/$(basename "$s")"
|
||
done
|
||
echo "已安装脚本:$(ls $OPT/scripts | tr '\n' ' ')"
|
||
fi
|
||
|
||
echo "=== 3.6 安装文档(systemd unit 的 Documentation= 指向这里)==="
|
||
if [ -d "$SRC/docs" ]; then
|
||
for d in "$SRC"/docs/*; do
|
||
[ -f "$d" ] || continue
|
||
install -m 0644 "$d" "$OPT/docs/$(basename "$d")"
|
||
done
|
||
echo "已安装文档:$(ls $OPT/docs | tr '\n' ' ')"
|
||
fi
|
||
|
||
echo "=== 4. 创建专用低权用户 drouter ==="
|
||
if ! id drouter >/dev/null 2>&1; then
|
||
useradd -r -s /usr/sbin/nologin -d $OPT -M drouter
|
||
echo "已创建系统用户 drouter"
|
||
else
|
||
echo "用户 drouter 已存在"
|
||
fi
|
||
|
||
echo "=== 5. 权限设置 ==="
|
||
chown -R drouter:drouter $OPT/data $OPT/snapshots $OPT/certs
|
||
chmod 750 $OPT/data $OPT/snapshots $OPT/certs
|
||
chmod +x $OPT/backend/drouter-helper.py
|
||
chmod +x $OPT/backend/drouter-snapshotd.py
|
||
chmod +x $OPT/backend/drouter-rescue.py
|
||
chown root:root $OPT/backend/drouter-helper.py
|
||
chown -R root:root $OPT/backend
|
||
chown -R root:root $OPT/web
|
||
chmod 755 /var/log/drouter
|
||
chmod 755 /etc/drouter/generated
|
||
chown -R drouter:drouter /var/lib/drouter 2>/dev/null || true
|
||
chmod -R 750 /var/lib/drouter 2>/dev/null || true
|
||
|
||
echo "=== 6. sudoers 白名单(仅放行 helper,且限定参数形态)==="
|
||
cat > /etc/sudoers.d/drouter <<'SUDO'
|
||
# drouter:后端仅可通过该脚本以 root 执行白名单操作
|
||
# 参数被严格限制为 action + JSON 字符串
|
||
drouter ALL=(root) NOPASSWD: /usr/bin/python3 /opt/drouter/backend/drouter-helper.py *
|
||
SUDO
|
||
chmod 0440 /etc/sudoers.d/drouter
|
||
visudo -c -f /etc/sudoers.d/drouter >/dev/null && echo "sudoers 语法检查通过"
|
||
|
||
echo "=== 7. 日志轮转配置 ==="
|
||
cat > /etc/logrotate.d/drouter <<'LR'
|
||
/var/log/drouter/*.log /var/log/drouter/*.jsonl {
|
||
daily
|
||
rotate 7
|
||
missingok
|
||
notifempty
|
||
compress
|
||
delaycompress
|
||
copytruncate
|
||
create 0644 root root
|
||
}
|
||
LR
|
||
|
||
echo "=== 8. systemd 服务单元 ==="
|
||
cat > /etc/systemd/system/drouter-web.service <<'UNIT'
|
||
[Unit]
|
||
Description=drouter 主路由管理后台
|
||
Documentation=file:/opt/drouter/docs/
|
||
After=network-online.target
|
||
Wants=network-online.target
|
||
Before=shutdown.target
|
||
|
||
[Service]
|
||
Type=simple
|
||
User=drouter
|
||
Group=drouter
|
||
WorkingDirectory=/opt/drouter
|
||
# systemd 默认 PATH 不含 /usr/sbin,而 nft/dnsmasq/radvd 都在那儿。
|
||
# 后端虽已自行归一化 PATH,这里再显式声明一次,避免任何遗漏路径静默失败。
|
||
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||
ExecStart=/usr/bin/python3 /opt/drouter/backend/drouter-web.py
|
||
Restart=on-failure
|
||
RestartSec=5
|
||
# 资源限制:适配 2 核 / 3.9G 小内存环境
|
||
MemoryMax=160M
|
||
MemoryHigh=120M
|
||
CPUQuota=40%
|
||
Nice=5
|
||
IOSchedulingClass=best-effort
|
||
IOSchedulingPriority=5
|
||
# 安全加固
|
||
# 说明:后端以低权用户 drouter 运行,写 /etc 由 root helper 完成(白名单 action)。
|
||
# 注意:这里必须 ProtectSystem=false —— 「依赖自检 / 一键安装」需要通过 apt/dpkg
|
||
# 往 /usr/bin、/var/cache/apt、/var/lib/dpkg 写入;若把 /usr 挂为只读,
|
||
# apt 会报 "只读文件系统" 而安装失败。安全边界由 helper 的 action 白名单保证:
|
||
# 非白名单动作一律拒绝,且所有参数逐个强校验。
|
||
NoNewPrivileges=false
|
||
PrivateTmp=false
|
||
ProtectSystem=false
|
||
ProtectHome=read-only
|
||
ReadWritePaths=/opt/drouter/data /opt/drouter/snapshots /opt/drouter/certs /var/log/drouter
|
||
# 额外保护:这些路径不给写(即使 helper 被滥用也改不到)
|
||
InaccessiblePaths=/boot
|
||
StandardOutput=journal
|
||
StandardError=journal
|
||
SyslogIdentifier=drouter-web
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
UNIT
|
||
|
||
echo "=== 8.5 自动快照定时器 + 紧急救援通道服务单元 ==="
|
||
# 自动快照:默认「开启」,每 6 小时一次。真实参数由界面写入
|
||
# /etc/drouter/snapshot.conf,helper 保存策略时会重建本单元。
|
||
cat > /etc/systemd/system/drouter-snapshot.service <<'SVC'
|
||
[Unit]
|
||
Description=drouter 自动配置快照
|
||
After=network.target
|
||
|
||
[Service]
|
||
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||
Type=oneshot
|
||
User=root
|
||
ExecStart=/usr/bin/python3 /opt/drouter/backend/drouter-snapshotd.py
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
SVC
|
||
|
||
cat > /etc/systemd/system/drouter-snapshot.timer <<'TMR'
|
||
[Unit]
|
||
Description=drouter 自动快照定时器
|
||
|
||
[Timer]
|
||
OnBootSec=10min
|
||
OnUnitActiveSec=6h
|
||
Persistent=true
|
||
Unit=drouter-snapshot.service
|
||
|
||
[Install]
|
||
WantedBy=timers.target
|
||
TMR
|
||
|
||
# 紧急救援通道:默认「关闭」。只有用户在界面上勾选开启后,
|
||
# helper 才会创建虚拟网卡别名并 enable/start 本服务。
|
||
cat > /etc/systemd/system/drouter-rescue.service <<'RSC'
|
||
[Unit]
|
||
Description=drouter 紧急救援通道(独立于 WAN/LAN 的应急恢复入口)
|
||
After=network.target
|
||
Wants=network.target
|
||
|
||
[Service]
|
||
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||
Type=simple
|
||
User=root
|
||
ExecStart=/usr/bin/python3 /opt/drouter/backend/drouter-rescue.py
|
||
Restart=always
|
||
RestartSec=5
|
||
StandardOutput=journal
|
||
StandardError=journal
|
||
NoNewPrivileges=false
|
||
ProtectSystem=false
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
RSC
|
||
|
||
# 救援通道默认关闭:不 enable、不 start(避免影响现有局域网)
|
||
echo "救援通道单元已就位(默认关闭,需在界面显式开启)"
|
||
|
||
# 常驻执行守护(#7 性能优化):把「每个请求 spawn 一次 python3+sudo」的
|
||
# 236ms 固定开销去掉。以 root 运行,但只监听 Unix socket(0660 root:drouter),
|
||
# 不占任何 TCP 端口;drouter-web 连不上它时会自动回退到 subprocess,所以它
|
||
# 挂掉只会变慢、不会让面板不可用。
|
||
cat > /etc/systemd/system/drouter-helpd.service <<'HPD'
|
||
[Unit]
|
||
Description=drouter 特权动作常驻执行守护
|
||
Documentation=file:/opt/drouter/docs/
|
||
After=network.target
|
||
|
||
[Service]
|
||
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||
Type=simple
|
||
User=root
|
||
Group=root
|
||
# 刻意不用 RuntimeDirectory:见 drouter-shelld.service 里的说明。
|
||
# /run/drouter 由守护自己 mkdir 并 chmod 0755(drouter-web 要能进目录连
|
||
# helper.sock,而 socket 自身是 0660 root:drouter,权限并没有放宽)。
|
||
ExecStart=/usr/bin/python3 /opt/drouter/backend/drouter-helpd.py
|
||
Restart=on-failure
|
||
RestartSec=2
|
||
# 常驻 import 了整套 helper,比其它守护占得多;上限防内存泄漏拖垮路由器
|
||
MemoryMax=300M
|
||
# 故意不设 CPUQuota:它要跑格式化大硬盘这类子进程,限 CPU 会把这些操作拖慢
|
||
StandardOutput=journal
|
||
StandardError=journal
|
||
SyslogIdentifier=drouter-helpd
|
||
ProtectHome=read-only
|
||
PrivateTmp=false
|
||
NoNewPrivileges=false
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
HPD
|
||
echo "常驻执行守护单元已就位"
|
||
|
||
# Web 终端守护(#4 重做):真 PTY 会话必须由常驻进程持有。
|
||
# 说明为什么不用 socket 激活:helper 在连不上时会自动 systemctl start 拉起,
|
||
# 但那要等几秒;预启动能让用户点「连接」时立刻进入 shell。
|
||
# 它以 root 运行(要开 root shell),但只监听 0600 的 Unix socket,不占任何端口。
|
||
cat > /etc/systemd/system/drouter-shelld.service <<'SHD'
|
||
[Unit]
|
||
Description=drouter Web 终端 PTY 会话守护
|
||
Documentation=file:/opt/drouter/docs/
|
||
After=network.target
|
||
|
||
[Service]
|
||
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||
Type=simple
|
||
User=root
|
||
Group=root
|
||
# 刻意不用 RuntimeDirectory=drouter:systemd 会在「最后一个声明它的单元停止」
|
||
# 时把整个目录删掉重建。两个守护共用 /run/drouter,于是 restart helpd 的瞬间
|
||
# systemd 重建目录,把 shelld 的 shell.sock 一起删了 —— shelld 依旧 active,
|
||
# socket 却没了,用户点终端只看到「守护未运行」。改成两个守护各自 mkdir。
|
||
ExecStart=/usr/bin/python3 /opt/drouter/backend/drouter-shelld.py
|
||
Restart=on-failure
|
||
RestartSec=3
|
||
# 只是转字节流,用不了多少内存;上限是防异常输出把内存吃光
|
||
MemoryMax=80M
|
||
CPUQuota=15%
|
||
StandardOutput=journal
|
||
StandardError=journal
|
||
SyslogIdentifier=drouter-shelld
|
||
# 安全加固:不碰系统目录、不给新特权(它本身就以 root 跑,靠 socket 0600 收口)
|
||
ProtectHome=read-only
|
||
PrivateTmp=false
|
||
NoNewPrivileges=false
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
SHD
|
||
echo "Web 终端守护单元已就位"
|
||
|
||
echo "=== 9. 生成 isp-dns 占位文件(dnsmasq resolv-file 引用)==="
|
||
cat > /etc/drouter/generated/isp-dns.conf <<'ISP'
|
||
# 由 drouter 自动维护:运营商 PPPoE 下发的 DNS
|
||
# 拨号成功后由 pppd 的 usepeerdns 写入 /etc/ppp/resolv.conf,
|
||
# 可执行 /opt/drouter/bin/sync-isp-dns.sh 将其同步到此处。
|
||
ISP
|
||
|
||
cat > $OPT/bin/sync-isp-dns.sh <<'SYNC'
|
||
#!/bin/bash
|
||
# 将 pppd 获取到的运营商 DNS 同步给 dnsmasq
|
||
SRC=/etc/ppp/resolv.conf
|
||
DST=/etc/drouter/generated/isp-dns.conf
|
||
{
|
||
echo "# 由 drouter 自动生成:运营商下发 DNS(来源 $SRC)"
|
||
if [ -f "$SRC" ]; then
|
||
grep -E '^nameserver' "$SRC" || echo "# 尚未获取到运营商 DNS"
|
||
else
|
||
echo "# $SRC 不存在(尚未拨号成功)"
|
||
fi
|
||
} > "$DST"
|
||
chmod 644 "$DST"
|
||
SYNC
|
||
chmod +x $OPT/bin/sync-isp-dns.sh
|
||
|
||
# 存量机器迁移:/etc/drouter/rescue.conf 旧版本写成了 0600 属主 root,
|
||
# 以 drouter 身份运行的快照读不到它,一个文件就让整个 /etc/drouter 目录
|
||
# 进不了快照(界面却显示「已创建快照」)。这里把它放宽成 0640 属组 drouter。
|
||
if [ -f /etc/drouter/rescue.conf ]; then
|
||
chmod 0640 /etc/drouter/rescue.conf 2>/dev/null || true
|
||
chgrp drouter /etc/drouter/rescue.conf 2>/dev/null || true
|
||
fi
|
||
|
||
echo "=== 10. 校验 ==="
|
||
# 逐个编译校验:缺文件直接中止部署(避免上线后才发现少装了一个守护进程)
|
||
for f in render.py drouter-helper.py drouter-web.py drouter-logd.py \
|
||
drouter-snapshotd.py drouter-rescue.py drouter-shelld.py \
|
||
drouter-helpd.py theme.py; do
|
||
p="$OPT/backend/$f"
|
||
if [ ! -f "$p" ]; then echo "✘ 缺少文件: $p"; exit 1; fi
|
||
python3 -c 'import ast,sys; ast.parse(open(sys.argv[1],encoding="utf-8").read())' "$p" \
|
||
&& echo "语法OK: $p" || { echo "✘ 语法错误: $p"; exit 1; }
|
||
done
|
||
echo "--- 目录 ---"
|
||
ls -la $OPT/
|
||
echo "--- 服务文件 ---"
|
||
systemctl daemon-reload
|
||
systemctl is-enabled drouter-web 2>&1 || true
|
||
echo "--- 自动快照定时器(默认开启)---"
|
||
systemctl enable --now drouter-snapshot.timer 2>&1 | tail -2 || true
|
||
systemctl is-active drouter-snapshot.timer 2>&1 || true
|
||
echo "--- 紧急救援通道(默认关闭)---"
|
||
systemctl is-active drouter-rescue 2>&1 || true
|
||
echo "--- 磁盘与日志清理定时器(默认开启)---"
|
||
# 单元与入口脚本由 helper 的 cleanup save 动作生成(默认参数即推荐值),
|
||
# 这里只负责在部署时把它建立起来 —— 用户之后在界面改策略会重新生成。
|
||
/usr/bin/python3 $OPT/backend/drouter-helper.py cleanup '{"op":"save","enabled":true}' \
|
||
>/dev/null 2>&1 && echo "清理策略已初始化" || echo "⚠ 清理策略初始化失败(界面保存一次即可恢复)"
|
||
systemctl is-active drouter-cleanup.timer 2>&1 || true
|
||
echo "--- 常驻执行守护 ---"
|
||
# 必须 restart 而不是 enable --now:守护把 helper 代码常驻在内存里,
|
||
# 只 enable --now 的话它还在跑上一版代码,改的东西根本不生效。
|
||
systemctl enable drouter-helpd 2>&1 | tail -1 || true
|
||
systemctl restart drouter-helpd 2>&1 | tail -2 || true
|
||
sleep 1
|
||
systemctl is-active drouter-helpd 2>&1 || echo "⚠ 常驻执行守护未运行(面板会自动回退到慢路径)"
|
||
echo "--- Web 终端守护 ---"
|
||
# 重启 shelld 会断掉正在用的终端会话,所以默认只 ensure 起来。
|
||
# 但两种情况必须重启:① socket 文件没了(多半是被 helpd 重启连带删掉的);
|
||
# ② 代码确实变了 —— 不重启的话跑的还是旧代码。
|
||
mkdir -p /var/lib/drouter
|
||
SHELLD_SEEN=$(cat "$SHELLD_MARK" 2>/dev/null || echo "-")
|
||
if [ ! -S /run/drouter/shell.sock ] || [ "$SHELLD_NEW" != "$SHELLD_SEEN" ]; then
|
||
echo "终端守护代码有更新($SHELLD_SEEN → $SHELLD_NEW),重启以生效"
|
||
systemctl restart drouter-shelld 2>&1 | tail -2 || true
|
||
# 只有真的起来了才更新 mark,否则下一轮还会重试重启
|
||
if systemctl is-active --quiet drouter-shelld; then
|
||
echo "$SHELLD_NEW" > "$SHELLD_MARK"
|
||
else
|
||
echo "⚠ 终端守护未能启动,mark 不更新(下一轮部署会再试)"
|
||
fi
|
||
else
|
||
systemctl enable --now drouter-shelld 2>&1 | tail -1 || true
|
||
echo "$SHELLD_NEW" > "$SHELLD_MARK"
|
||
fi
|
||
systemctl is-active drouter-shelld 2>&1 || true
|
||
# socket 必须真的存在:systemd 说 active 不代表 socket 已经在盘上
|
||
# (restart 是异步的,守护起来后还要自己 mkdir + bind,得给它几秒)。
|
||
for _i in 1 2 3 4 5 6 7 8 9 10; do
|
||
[ -S /run/drouter/shell.sock ] && break
|
||
sleep 1
|
||
done
|
||
[ -S /run/drouter/shell.sock ] && echo "终端 socket 就位" \
|
||
|| echo "⚠ 终端 socket 缺失,Web 终端将不可用"
|
||
echo "--- 网络与 VNC 复核 ---"
|
||
ip -4 -o addr show ens18 2>/dev/null || echo "(ens18 不存在,跳过)"
|
||
ss -lnt | grep 5900 || echo "5900 异常"
|
||
|
||
# 重启管理后台,让新代码真正生效。
|
||
# 必须放在语法校验之后:一旦新代码有语法错误,宁可继续跑旧进程也不能把自己关在门外。
|
||
echo "--- 重启管理后台 ---"
|
||
systemctl restart $UNIT_WEB 2>&1 | tail -3 || echo "(重启失败,请手动检查)"
|
||
sleep 2
|
||
systemctl is-active $UNIT_WEB || echo "⚠ drouter-web 未在运行"
|
||
|
||
# 统一日志采集定时器:默认配置是 enabled=true,但 service/timer 单元只有
|
||
# 在界面里点过一次「保存日志设置」才会被 helper 写出来。全新部署后防火墙日志
|
||
# 页面会一直是空的,表现为「日志功能没做」。这里补一次同步,让默认开关真正生效。
|
||
echo "--- 同步日志采集定时器 ---"
|
||
python3 - <<'PY' || echo "(日志定时器同步失败,可在界面「日志设置」里保存一次重建)"
|
||
# 文件名带连字符,不能用普通 import,走 importlib 按路径加载
|
||
import importlib.util
|
||
spec = importlib.util.spec_from_file_location(
|
||
'drouter_helper', '/opt/drouter/backend/drouter-helper.py')
|
||
h = importlib.util.module_from_spec(spec)
|
||
spec.loader.exec_module(h)
|
||
r = h.act_logd_sync({'interval_sec': 60})
|
||
print(r.get('msg_cn') or r)
|
||
PY
|
||
systemctl is-active drouter-logd.timer 2>&1 || true
|
||
|
||
echo "DEPLOY_DONE"
|