SubLaneSubLane

备份与恢复

在线导出、校验,并恢复到独立的新目录。

实例投入使用后,在升级或迁移服务器前做好备份。首次连接客户端请先看第一次使用。

备份内容与保存

新备份为使用密码加密的二进制 age v1 文件(.sublane-backup.tar.gz.age),解密后的归档包含 sublane.db、匹配的 credentials.key 和记录版本、大小、SHA-256 校验和的 manifest.json。数据库快照包含已提交的 WAL 数据,覆盖用户、密钥、账号、权限、设置、请求与用量。

新导出必须设置独立的 12–128 字符密码,完整加密数据库、凭据密钥和清单。SubLane 不保存密码;请与备份分开保管,遗失后无法重置该备份的密码。

旧版未加密 .tar.gz 仍可在不提供密码时校验与恢复,页面会明确标记。持有旧归档的人可以读取其中的凭据密钥,应妥善保管,并在恢复后重新导出加密备份。校验成功不能证明备份作者的身份。

部署文件、环境变量、浏览器本地偏好与尚未完成的 OAuth 尝试不在归档中。恢复会清除浏览器会话,用户必须重新登录。

独立存放凭据密钥

未配置时仍使用 <数据目录>/credentials.key,保持已有部署兼容。可选变量 SUBLANE_CREDENTIALS_KEY_FILE 指向数据目录之外的独立文件;变量只保存路径,不保存密钥内容。显式配置的数据目录内部路径、目录符号链接和大小写别名会被拒绝。密钥仍需为权限 0600 的私有 32 字节普通文件。

已有实例应先停止服务,将原密钥复制到新的私有目录并设置服务用户所有权,再配置路径、启动并验证加密备份。确认成功后再移除数据目录里的旧副本,切勿用新生成的密钥替换原密钥;已有密文却缺失密钥时会拒绝启动。

使用独立密钥文件
SUBLANE_DATA_DIR=/srv/sublane/data \
SUBLANE_CREDENTIALS_KEY_FILE=/srv/sublane-secrets/credentials.key \
./bin/sublane

CLI 导出可通过 --key-file FILE 覆盖环境变量。导出直接把外部密钥写入加密流,不在导出暂存目录复制明文密钥。分离存储能减少数据目录泄漏的影响,无法抵御应用进程或主机已被完全控制的情况。

使用脚本迁移 Compose 密钥

应用仓库提供 scripts/separate-key.sh,用于已有的 SubLane v0.12.0+ Compose 部署。先将脚本保存到宿主机并审阅,执行前导出、校验一份使用密码加密的恢复备份。

分离现有密钥
sudo bash scripts/separate-key.sh --dir /srv/sublane --key-dir /srv/sublane-secrets

--dir 是包含原 docker.compose.yaml 和可选 .env 的部署目录。--key-dir 必须是父目录已存在、目标尚不存在的绝对路径,位于实际数据挂载及恢复目录之外,并使用持久化宿主机存储;支持含空格的普通 ASCII 路径。脚本需要本机 Linux rootful Docker(未启用 userns 映射或 SELinux)、Compose、Bash、jq 和 GNU coreutils。现有健康服务须命名为 sublane,数据目录须为可写的 bind 或命名卷挂载根,Compose 须使用常规两空格服务缩进、environment 块映射及 volumes 块列表。不支持的布局会在停服前拒绝执行。

脚本核对当前项目、镜像、数据挂载、原密钥的私有权限、32 字节大小和摘要;保留 Compose 文本及变量插值,并通过 Compose 解析结果确认只改变密钥路径和挂载。新宿主目录由 root 持有、权限 0700,只把其中权限 0600 的密钥文件以只读方式挂载给容器,文件归服务 UID/GID 持有;运行服务不会被提升权限。

切换前、切换后、删除旧密钥后,都会使用临时随机密码导出并校验加密备份。校验暂存位于数据目录之外,完成后清理;请预留快照与校验空间,以及备份密钥派生(KDF)所需内存。这些检查不保留可供恢复的备份。只有新配置、健康检查、密钥摘要和凭据校验均通过后,才删除数据目录中的旧副本,并再次重建和校验,确认不再依赖旧文件。

开始切换后遇到可捕获的错误或信号,会尝试自动回滚。也可在迁移成功后显式回滚:

回滚密钥分离
sudo bash scripts/separate-key.sh --dir /srv/sublane --rollback

私有恢复记录保存在 /srv/sublane/.key-migration。回滚会恢复原配置及匹配的旧密钥,重建原服务并验证;遇到后续配置、环境变量或密钥冲突时拒绝覆盖,也不会降级镜像或数据库。回滚后仍保留外部密钥和恢复记录,后续迁移会拒绝覆盖已有记录或目标;重试前应妥善归档已验证的恢复记录,并选择新的密钥目录。

脚本与安装器共用 .update-lock,迁移期间保持部署配置不变,并避免并发更新。强制终止或断电后,应先确认没有迁移或更新进程,再移除遗留的空 .update-lock,然后执行 --rollback。若回滚无法验证,会保留恢复密钥材料和私有 operation.log;完成恢复前请保留恢复目录及外部密钥目录。自行保留的备份与密码仍须分开保管。

命令行备份

先准备一个只含一行密码的私有文件(权限 0600,建议使用密码管理器生成),通过 --password-stdin 读取。密码不放入命令行参数或环境变量。在有足够空间的目录执行:

创建与校验备份
mkdir -p backups
./bin/sublane backup --password-stdin --output ./backups/team.sublane-backup.tar.gz.age < /secure/backup-password
./bin/sublane backup verify --password-stdin --input ./backups/team.sublane-backup.tar.gz.age < /secure/backup-password

默认读取 SUBLANE_DATA_DIR 或 ./data,也可使用 --data-dir 指定,通过 --key-file 选择独立密钥。读取旧版明文归档时可省略 --password-stdin。已有归档不会被覆盖。CLI 数据库快照上限为 4 GiB,校验和恢复还需要解压与迁移空间。维护命令不会启动 HTTP 服务或供应商执行器。

恢复流程

恢复目标必须是尚不存在的新目录,即使已有空目录也会被拒绝;父目录必须存在。

恢复到独立目录
./bin/sublane restore --password-stdin --input ./backups/team.sublane-backup.tar.gz.age --data-dir ./data-restored < /secure/backup-password

恢复成功不代表运行实例已切换。停止原服务并保留原目录;若使用独立密钥,请先将恢复目录中的 credentials.key 移至数据目录外的新私有位置,更新 SUBLANE_CREDENTIALS_KEY_FILE,不要覆盖原实例的密钥,再使用新目录启动:

启用恢复后的数据
SUBLANE_DATA_DIR=./data-restored ./bin/sublane

重新登录并检查成员、组、密钥与订阅账号,再恢复团队流量。回滚时停止服务并切回原目录,不要让两个进程同时使用同一数据库。恢复会回到快照时刻,之后的撤销和密码修改不再存在;上游可能已废弃旧刷新令牌,必要时重新授权。

网页操作

管理员进入「系统设置 → 备份与恢复」。导出时输入并确认密码;校验与恢复时填写原备份密码(旧明文归档可留空),密码只随请求体发送,修改密码或文件后必须重新校验。可在线下载加密归档、上传校验,或「准备恢复」到当前数据目录下的 restore-ready。准备完成后仍需运维人员停止服务并切换目录。

网页归档文件上限 256 MiB,更大归档使用 CLI。同一实例一次只处理一个网页维护操作。已有 restore-ready 会阻止再次准备,不会覆盖。检查该目录后再决定下一步,或用 CLI 指定其他新目录。

网页备份与恢复操作界面示例:

备份页面中的下载、归档校验与准备恢复操作

Docker Compose

独立密钥可用本地覆盖配置挂载私有目录,并将路径设为容器内位置。宿主目录应归镜像内的 sublane 用户所有(可用 docker compose exec sublane id 查询),目录权限 0700、密钥权限 0600。已有实例须先复制匹配的原密钥;密钥就绪后可将挂载改为只读。

独立密钥挂载覆盖配置
services:
  sublane:
    environment:
      SUBLANE_CREDENTIALS_KEY_FILE: /run/sublane-keys/credentials.key
    volumes:
      - type: bind
        source: /srv/sublane-secrets
        target: /run/sublane-keys
容器内导出并复制归档
docker compose -f docker.compose.yaml exec sublane sh -c 'mkdir -p /data/backups'
docker compose -f docker.compose.yaml exec -T sublane sublane backup --password-stdin --output /data/backups/team.sublane-backup.tar.gz.age < /secure/backup-password
docker compose -f docker.compose.yaml cp sublane:/data/backups/team.sublane-backup.tar.gz.age ./team.sublane-backup.tar.gz.age

恢复归档放入容器可访问的私有目录后执行:

容器内准备恢复
docker compose -f docker.compose.yaml exec -T sublane sublane restore --password-stdin --input /data/backups/team.sublane-backup.tar.gz.age --data-dir /data/restore-ready < /secure/backup-password

创建 docker.compose.restore.yaml:

恢复目录的 Compose 覆盖配置
services:
  sublane:
    environment:
      SUBLANE_DATA_DIR: /data/restore-ready
停止服务并切换数据目录
docker compose -f docker.compose.yaml stop sublane
docker compose -f docker.compose.yaml -f docker.compose.restore.yaml up -d

后续部署保持使用覆盖配置,勿删除命名数据卷。容器已停止时,可用 docker compose -f docker.compose.yaml run --rm --no-deps sublane ... 执行维护命令。

校验边界

密码加密使用 age,scrypt 工作因子为 18,单次派生约需 256 MiB 内存;导入时拒绝更高工作因子,避免不可信归档任意提高资源消耗。这不是整个进程的内存上限。错误密码、篡改、截断或追加的加密内容都会在发布恢复目录前被拒绝。正常取消会清理私有暂存;强制终止可能留下暂存目录,应在无备份操作运行时清理。

校验检查归档条目、大小、摘要、gzip 完整性、受支持的合并结构版本、SQLite 完整性与外键,并验证凭据解密。路径穿越、符号链接、重复条目、缺失文件及未知未来数据库版本会被拒绝。当前预发布版本不接受旧开发结构;校验不会修改运行中的数据库,也不联系供应商验证账号。

自动加密备份

通过下面的部署配置启用定时备份。目标目录必须为私有目录(0700);密码须放在私有普通文件(0600,不能是符号链接)中,与数据目录、备份目录分开。独立密码为 12–128 个字符,请单独保管;每次备份都会重新读取。

Automatic backup deployment settings
export SUBLANE_BACKUP_DIR=/srv/sublane-backups
export SUBLANE_BACKUP_PASSWORD_FILE=/secure/sublane/backup-password
export SUBLANE_BACKUP_INTERVAL=24h
export SUBLANE_BACKUP_KEEP=7

启动时执行一次,之后每次尝试结束再等待配置的间隔。新归档完整校验成功后,才轮换预留的 scheduled- 文件;手动归档和新校验的归档会保留。成功、失败均记入服务日志。首版不含远程上传或网页调度表单,演示模式不运行备份。

SUBLANE_BACKUP_INTERVAL: 1h–720h (default 24h). SUBLANE_BACKUP_KEEP: 1–365 (default 7).

每个实例必须使用独立的备份目录。

可选配置 SUBLANE_BACKUP_WEBHOOK_URL 为公网 HTTPS 地址,每次备份失败发送一个有界的 backup_failure JSON 事件。这是独立于工作空间告警的实例运维通知;发送失败会记入日志。

Docker 中使用容器内路径,通过本地 Compose 覆盖文件挂载私有密码文件和可写的独立备份目录,并授予服务用户访问权限。默认 /tmp tmpfs 为 64 MiB;校验较大归档时,应另挂载私有可写暂存目录,并在覆盖文件中设置 TMPDIR。