mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-01 14:53:56 +08:00
136 lines
4.6 KiB
Bash
Executable File
136 lines
4.6 KiB
Bash
Executable File
#!/bin/sh
|
|
# dnsguard-rollback —— 回滚点管理与自动回滚
|
|
#
|
|
# dnsguard-rollback 列出可用回滚点
|
|
# dnsguard-rollback --arm [分钟] 把"当前 UCI + 当前规则文件"做成回滚点,并挂倒计时自动回滚
|
|
# dnsguard-rollback --disarm 取消挂起的自动回滚(= 确认保留;不影响已有回滚点)
|
|
# dnsguard-rollback --status 输出一行 JSON:挂起状态 + 可用回滚点
|
|
# dnsguard-rollback <时间戳|latest> 还原到指定回滚点(UCI + 规则文件一起还原)并 reload
|
|
# dnsguard-rollback --auto <ts> <秒> 内部使用:倒计时结束后自动还原
|
|
#
|
|
# 为什么要有「自动回滚」:DNS 是全家网的命门,改错了人可能连路由器都访问不到(域名解析不了)。
|
|
# 挂一个 N 分钟倒计时,人不在旁边确认就自动还原 —— 与"改 LAN IP 前挂回滚定时器"同一套纪律。
|
|
#
|
|
# 生成:WorkBuddy 2026-09-26
|
|
|
|
set -u
|
|
|
|
BAKDIR=/mnt/data/backup-dnsguard
|
|
OUT=/etc/nftables.d/20-dns-guard.nft
|
|
REC=/tmp/.dnsguard-rollback
|
|
LOG_TAG=dnsguard
|
|
|
|
snapshot() {
|
|
# snapshot <时间戳> 把 UCI + 当前规则文件存进回滚点目录
|
|
_t="$1"
|
|
mkdir -p "$BAKDIR/$_t" 2>/dev/null
|
|
[ -f "$OUT" ] && cp "$OUT" "$BAKDIR/$_t/20-dns-guard.nft" 2>/dev/null
|
|
uci export dnsguard > "$BAKDIR/$_t/dnsguard.uci" 2>/dev/null
|
|
[ -f "$BAKDIR/$_t/dnsguard.uci" ] && chmod 600 "$BAKDIR/$_t/dnsguard.uci" 2>/dev/null
|
|
}
|
|
|
|
do_restore() {
|
|
_t="$1"
|
|
if [ ! -d "$BAKDIR/$_t" ]; then
|
|
echo "回滚点不存在:$BAKDIR/$_t" >&2
|
|
return 1
|
|
fi
|
|
# 还原 UCI(让界面显示与实际一致)
|
|
if [ -f "$BAKDIR/$_t/dnsguard.uci" ]; then
|
|
uci import dnsguard < "$BAKDIR/$_t/dnsguard.uci" 2>/dev/null
|
|
uci commit dnsguard 2>/dev/null
|
|
fi
|
|
# 还原规则文件
|
|
if [ -f "$BAKDIR/$_t/20-dns-guard.nft" ]; then
|
|
cp "$BAKDIR/$_t/20-dns-guard.nft" "$OUT" 2>/dev/null
|
|
else
|
|
rm -f "$OUT" 2>/dev/null
|
|
fi
|
|
fw4 reload >/dev/null 2>&1
|
|
logger -t "$LOG_TAG" "已回滚到回滚点 $_t(UCI + 规则文件),防火墙已重载"
|
|
echo "已回滚到 $_t"
|
|
return 0
|
|
}
|
|
|
|
# ---------------- 参数分发 ----------------
|
|
|
|
case "${1:-}" in
|
|
"")
|
|
echo "可用回滚点(新 → 旧):"
|
|
ls -1t "$BAKDIR" 2>/dev/null | head -20
|
|
;;
|
|
|
|
--status)
|
|
PEND=""
|
|
if [ -f "$REC" ]; then
|
|
read -r RTS RSEC RDL RSRC < "$REC" 2>/dev/null
|
|
NOW=$(date +%s)
|
|
LEFT=$(( ${RDL:-0} - NOW ))
|
|
[ "$LEFT" -lt 0 ] && LEFT=0
|
|
PEND="\"pending\":true,\"pending_ts\":\"${RTS:-}\",\"pending_left\":${LEFT},\"pending_total\":${RSEC:-0}"
|
|
else
|
|
PEND='"pending":false,"pending_ts":"","pending_left":0,"pending_total":0'
|
|
fi
|
|
LIST=""
|
|
for d in $(ls -1t "$BAKDIR" 2>/dev/null | head -10); do
|
|
[ -n "$LIST" ] && LIST="$LIST,"
|
|
LIST="$LIST\"$d\""
|
|
done
|
|
printf '{"status":"ok",%s,"backups":[%s]}\n' "$PEND" "$LIST"
|
|
;;
|
|
|
|
--arm)
|
|
SEC=$(printf '%s' "${2:-10}" | tr -dc '0-9'); [ -n "$SEC" ] || SEC=10
|
|
# 秒数上限保护:1 分钟 ~ 120 分钟
|
|
[ "$SEC" -lt 1 ] && SEC=1
|
|
[ "$SEC" -gt 120 ] && SEC=120
|
|
TS=$(date +%Y%m%d-%H%M%S)
|
|
snapshot "$TS"
|
|
DL=$(( $(date +%s) + SEC * 60 ))
|
|
printf '%s %s %s %s\n' "$TS" "$((SEC * 60))" "$DL" "pending" > "$REC"
|
|
setsid /bin/sh -c "/usr/bin/dnsguard-rollback --auto '$TS' '$((SEC * 60))'" >/dev/null 2>&1 &
|
|
logger -t "$LOG_TAG" "已挂自动回滚:$SEC 分钟后(若无确认)还原到回滚点 $TS"
|
|
printf '{"status":"ok","ts":"%s","seconds":%s}\n' "$TS" "$((SEC * 60))"
|
|
;;
|
|
|
|
--disarm)
|
|
if [ -f "$REC" ]; then
|
|
read -r RTS RSEC RDL RSRC < "$REC" 2>/dev/null
|
|
rm -f "$REC"
|
|
# 尽力杀掉倒计时进程;即使没杀干净,--auto 也会因为记录文件不存在而放弃
|
|
for pid in $(ps w 2>/dev/null | grep '[d]nsguard-rollback --auto' | awk '{print $1}'); do
|
|
kill "$pid" 2>/dev/null
|
|
done
|
|
logger -t "$LOG_TAG" "已取消自动回滚(确认保留当前设置),回滚点 $RTS 仍保留"
|
|
printf '{"status":"ok","message":"已取消自动回滚,当前设置被保留","ts":"%s"}\n' "$RTS"
|
|
else
|
|
printf '{"status":"ok","message":"没有挂起的自动回滚"}\n'
|
|
fi
|
|
;;
|
|
|
|
--auto)
|
|
TS="${2:-}"
|
|
SEC="${3:-600}"
|
|
sleep "$SEC" 2>/dev/null
|
|
# 记录文件是唯一真相:被 --disarm 删掉、或被后来的 --arm 覆盖,就什么都不做
|
|
[ -f "$REC" ] || exit 0
|
|
read -r RTS RSEC RDL RSRC < "$REC" 2>/dev/null
|
|
[ "${RTS:-}" = "$TS" ] || exit 0
|
|
rm -f "$REC"
|
|
logger -t "$LOG_TAG" "自动回滚触发:$((SEC / 60)) 分钟内未确认,正在还原到 $TS"
|
|
do_restore "$TS" >/dev/null 2>&1
|
|
exit 0
|
|
;;
|
|
|
|
*)
|
|
TS="$1"
|
|
if [ "$TS" = "latest" ]; then
|
|
TS=$(ls -1t "$BAKDIR" 2>/dev/null | head -1)
|
|
fi
|
|
[ -n "$TS" ] || { echo "找不到回滚点" >&2; exit 1; }
|
|
do_restore "$TS"
|
|
;;
|
|
esac
|
|
|
|
exit 0
|