摘要
在制作 Linux DD 镜像、云平台自定义镜像或虚拟机模板时,仅仅清空几个 /var/log 文件并不足够。
一个真正适合克隆的系统模板,通常还需要处理登录历史、Shell 历史、软件包缓存、审计记录、容器日志、机器身份、SSH 主机密钥、Cloud-init 实例状态、网络租约和随机种子等内容。同时,清理过程不能删除业务服务、改变服务启用状态,也不能让仍在运行的程序继续向已清理的日志文件写入数据。
为了解决这些问题,我开发了 CXT-SystemPrep。
项目名称中的 CXT 来自我的博客名称,SystemPrep 表示“系统封装前准备”。本文发布时的公开版本为 v2.0.0。
项目地址:
它解决了什么问题
传统清理脚本经常直接执行类似下面的命令:
echo > /var/log/messages
echo > /var/log/auth.log
history -c
这种做法存在几个明显问题:
- 不同发行版的日志位置并不完全一致。
- systemd-journald、auditd、容器运行时和业务程序可能仍在写日志。
- 只清理当前用户,容易遗漏其他用户的历史记录与缓存。
- 直接删除
/etc/machine-id或 SSH 主机密钥,如果没有再生机制,可能造成启动或远程连接异常。 - Cloud-init 的实例状态未重置时,克隆后的机器可能不会重新执行平台下发的 User Data。
- 粗暴停止服务后,如果本次不关机,还需要准确恢复原来处于运行状态的服务。
- 清理脚本自身、终端历史和临时文件也可能成为新的残留。
CXT-SystemPrep 将这些工作组合成一套可预检、可分级、可扩展的封装流程。
核心设计
1. 真正执行前自动进入 /run
正式执行时,脚本会把自身和需要使用的外部列表复制到 /run 下的私有目录,并将目录权限设置为 0700。
/run 通常由 tmpfs 提供,重启或关机后内容自然消失。脚本还会对暂存副本进行 SHA-256 校验,避免复制损坏后继续运行。
2. 使用一次性 systemd 服务执行
脚本不会继续依赖发起命令的 SSH 会话,而是通过 transient systemd service 执行实际清理,并在派发成功后结束登录会话。
这样可以避免:
- SSH 断开导致清理中途终止;
- 退出 Shell 时再次写回
.bash_history; - 清理期间仍有登录会话产生新历史;
- 永久安装一个专用服务单元。
任务成功完成后,一次性服务、运行目录和脚本载荷会自动回收。使用 --verify 时会保留验证报告,便于开发和排查。
3. 自动发现并临时停止日志写入服务
CXT-SystemPrep 会检查哪些进程正在打开本次准备清理的日志或历史文件,并尝试将进程安全映射到对应的 systemd 服务。
例如业务模板中安装了 PicoClaw、Nginx 或其他应用:
- 服务本身不会被删除;
- unit 文件不会被修改;
- enabled/disabled 状态不会改变;
- 为避免持续写日志,相关服务可在清理期间被临时停止;
- 如果没有选择重启或关机,脚本会恢复清理前处于 active 状态的服务;
- 如果选择重启或关机,后续由系统按照原有 enablement 正常启动服务。
自动停止服务并不等于自动删除所有业务日志。非标准应用日志默认保留,只有用户通过 --paths 明确指定后才会清理。
4. 默认只处理系统级和通用数据
脚本的常规清理范围包括:
- systemd journal、syslog、audit、wtmp、btmp、lastlog 等系统记录;
- Shell、编辑器、客户端、最近使用文件等用户历史;
- 用户缓存和缩略图缓存;
- APT、DNF、Yum 等软件包缓存、历史和相关状态;
- Docker、Podman、containerd、Kubernetes 的文本日志;
- 崩溃转储、记账记录、faillock、临时目录和部分易失状态;
- logrotate 状态与常见日志队列。
它不会卸载软件、删除容器数据、删除业务配置或擅自清理无法确认用途的应用目录。

三种预设模式
CXT-SystemPrep 提供三种 profile,用于减少长参数组合:
| 模式 | 用途 | 主要内容 |
|---|---|---|
test | 日常测试与基础清理 | 系统日志、历史、缓存、软件包记录、容器文本日志和临时状态 |
seal | 模板封装 | test + machine-id、Cloud-init 状态、网络租约、随机种子、SSH 主机密钥、known_hosts |
privacy | 封装并提高镜像可压缩性 | seal + 文件系统空闲空间归零 + 活动交换空间清理 |
profile 不会自动选择重启或关机。最终动作必须由用户明确决定:
- 不添加电源参数:清理后继续运行系统,并恢复原本活动的服务;
--reboot:清理、同步数据并重启;--poweroff:清理、同步数据并关机。
对于正式模板封装,通常使用 seal 或 privacy,并搭配 --poweroff。在创建快照、验证脚本或调试时,可以先使用 test 或 --dry-run。
下载与预检
建议从项目 master 分支下载最新脚本:
curl -fL --proto '=https' --tlsv1.2 \
'https://raw.githubusercontent.com/MeowLove/Network-Reinstall-System-Modify/refs/heads/master/Tools/CXT-SystemPrep/CXT-SystemPrep.sh' \
-o /run/CXT-SystemPrep.sh
chmod 700 /run/CXT-SystemPrep.sh
查看版本:
/bin/sh /run/CXT-SystemPrep.sh --version
查看帮助:
/bin/sh /run/CXT-SystemPrep.sh --help
执行无修改预检:
/bin/sh /run/CXT-SystemPrep.sh \
--profile seal \
--poweroff \
--dry-run
rc=$?
printf 'dry-run exit=%s\n' "$rc"
--dry-run 只解析计划、检查运行环境、显示可能被停止的服务和拟清理范围,不迁移脚本、不创建服务、不结束会话,也不修改系统。退出码为 0 才表示预检通过。
如果需要严格锁定某一次审计过的内容,可以将下载地址中的 master 替换为 Git 提交号,并同时核对 SHA-256。对于普通用户,项目文档仍以获取最新修订为默认建议。
常用执行方式
基础清理,不重启
/bin/sh /run/CXT-SystemPrep.sh \
--profile test \
--yes
适合测试清理与服务恢复流程。由于服务恢复后会重新产生日志,这种方式并不等同于最终封装状态。
标准模板封装并关机
HISTFILE=/dev/null
export HISTFILE
history -c 2>/dev/null || :
exec /bin/sh /run/CXT-SystemPrep.sh \
--profile seal \
--poweroff \
--yes
seal 会执行克隆身份相关处理,因此脚本会预检 SSH 主机密钥和 Cloud-init 的下一次启动行为。关机后应在引导加载程序阶段或系统完全关机后制作镜像,避免再次启动源模板并产生新的状态。
隐私模式并关机
exec /bin/sh /run/CXT-SystemPrep.sh \
--profile privacy \
--poweroff \
--yes
空闲空间归零和交换空间清理可能耗时较长,也会临时增大正在运行的 RAW/VHD 文件;归零文件删除后,经过稀疏化或压缩的最终镜像通常会更小。对于刚完成最小化安装、从未写入大量数据的磁盘,这两项收益可能有限。
清理自定义应用日志
特殊业务日志不应硬编码进通用脚本。可以创建一个外部路径列表,每行填写一个绝对路径:
# /run/cxt-extra-paths.list
/home/picoclaw/.picoclaw/logs
/srv/myapp/log
然后执行:
/bin/sh /run/CXT-SystemPrep.sh \
--profile seal \
--poweroff \
--paths /run/cxt-extra-paths.list \
--dry-run
对于目录,脚本会删除目录中的内容但保留目录本身。路径会经过安全检查,系统配置树、关键状态树、挂载根以及不符合“日志、缓存、历史、审计或临时数据”特征的危险路径会被拒绝。
如果某个服务没有打开可识别的日志文件,但它会通过子进程、套接字或外部机制继续影响清理,可以使用高级 unit 列表:
# /run/cxt-extra-services.list
myapp.service
myapp-log-writer.service
/bin/sh /run/CXT-SystemPrep.sh \
--profile seal \
--poweroff \
--services /run/cxt-extra-services.list \
--paths /run/cxt-extra-paths.list \
--dry-run
--services 可以列出 system manager 中的 .service、.socket、.timer 或 .path unit。脚本会在清理期间停止并进行运行时屏蔽,但不会删除、禁用或卸载它们,也不会自动决定其业务数据是否应该清理。任意 .target、关键运行依赖、systemd user-manager unit 以及不存在的 unit 会被拒绝。
Cloud-init 需要特别注意
--cloud-state 会清理 Cloud-init 的实例缓存、日志、运行状态和 seed,使下一次启动重新识别 datasource,并重新执行 once-per-instance 模块与平台 User Data。
这意味着下一次启动时,Cloud-init 可能重新应用:
- 密码和用户配置;
- 主机名;
- SSH 设置和 SSH 主机密钥;
- 网络配置;
write_files内容;runcmd等用户命令。
因此,即使没有单独启用 CXT-SystemPrep 的 SSH 主机密钥删除参数,Cloud-init 也可能根据其配置独立轮换主机密钥。这是 v2.0.0 明确显示在帮助、预检和正式执行提示中的重要行为。
高风险参数 --cloud-configs 会进一步执行 cloud-init clean --configs all,删除 Cloud-init 已生成到系统中的配置。它不属于任何 profile,只适用于确认下一次启动一定存在兼容 datasource、并且生成配置可以安全重建的环境。
参数速览
--profile test|seal|privacy 使用预设模式
--poweroff 完成后关机
--reboot 完成后重启
--machine-id 重置 machine-id
--cloud-state 重置 Cloud-init 实例状态
--cloud-configs 高风险:同时删除 Cloud-init 生成配置
--leases 清理网络租约
--random-seed 清理系统随机种子
--host-keys 删除 SSH 主机密钥
--known-hosts 清理 SSH 客户端 known_hosts
--zero-free 将支持的文件系统空闲空间归零
--wipe-swap 清理活动磁盘交换空间
--paths FILE 清理额外应用日志或缓存路径
--services FILE 临时停止额外 system-manager unit
--dry-run 只显示计划,不修改系统
--verify 生成辅助验证报告
--yes 跳过交互确认
--help 显示帮助
--version 显示版本和构建信息
请始终以当前版本的 --help 输出和项目 README 为准。
支持范围与边界
CXT-SystemPrep 的正式执行要求:
- root 权限;
- 现代 systemd 系统;
- 可用的
systemd-run --collect和loginctl; /run位于 tmpfs 或等效易失运行目录。
Debian、Ubuntu、RHEL、Rocky Linux、AlmaLinux 等现代 systemd 发行版属于目标环境。Alpine Linux 默认使用 OpenRC,目前不在支持范围内。
此外,它是一款模板封装准备工具,不是取证级安全擦除工具。它不能清除:
- 远程日志服务器中的记录;
- 云平台控制面审计;
- 已创建的快照和备份;
- 底层存储的历史副本;
- Tailscale、ZeroTier、WARP 或业务 Agent 自身的克隆身份,除非另行设计对应的重置流程。
实机验证情况
v2.0.0 已在一台实际运行 Debian 13、systemd、SSH、PicoClaw 和多种网络服务的 Scaleway 云服务器上完成分阶段验证,包括:
test模式无电源动作清理与服务恢复;- 自动发现并临时停止 PicoClaw 日志写入进程;
- 通过
--paths清理指定业务日志; seal + reboot后 machine-id 和 SSH 主机密钥重新生成;- SSH、网络及已启用业务服务在重启后恢复;
- Cloud-init 状态重置后重新识别 Scaleway datasource 并执行 User Data;
- 自定义 Debian 网络配置保持可用;
/run中的源脚本、列表和运行载荷在重启后消失。
实机测试不能覆盖所有发行版、存储布局和第三方应用。正式用于生产模板前,仍建议先创建可回滚快照,执行 --dry-run,再按 test、单项身份重置、完整 seal 的顺序逐级验证。
结语
CXT-SystemPrep 的目标不是用一份无限增长的路径清单“删除一切”,而是在系统清理、应用完整性和克隆安全之间建立清晰边界:
- 系统级历史和通用日志自动处理;
- 正在写入相关文件的服务自动安全停顿;
- 业务服务与配置保持不变;
- 特殊应用日志由模板维护者明确选择;
- 身份重置与 Cloud-init 重置均可预检、可组合;
- 清理过程自身尽量不留下新的持久残留。