Repository navigation
History 073 Backup and restore
历史文档:保留迁移前已公开内容,适用其正文声明的版本。当前候选说明见 Backup-and-restore。
适用于 SandIAM 0.7.3 源码候选;安装兼容性与真实操作结果须在自己的隔离环境确认。当前候选尚未形成正式发行,发行状态见版本与兼容性。
同一时间点保存 PostgreSQL 数据库、SandIAM 插件文件、管理端产物、门户产物、部署配置、 密钥版本标识和候选包摘要。秘密应由密钥系统单独备份,不写入普通归档。
数据库备份至少覆盖所有 sand_iam_* 表及其序列、约束和迁移账本。若采用整库备份,
恢复演练必须同时确认宿主及其他插件不受损。
下列脚本使用 POSIX shell 和 PostgreSQL 客户端工具,适用于 Linux/macOS。macOS 没有 sha256sum 时,使用 shasum -a 256。Windows 应使用已批准的 POSIX shell 或由运维方将脚本转换为 PowerShell,并独立验证退出状态;本页没有经过 Windows 运行验收。工作目录必须是权限受控的备份目录。
以下命令不会创建数据库。先由运维方配置 libpq service,SOURCE_PG_SERVICE 只保存源数据库的服务名称;主机、端口、库名和用户由受控 pg_service.conf 指定,密码使用权限为 0600 的 passfile 或平台提供的安全注入方式,不写入命令参数、脚本、终端历史或文件名。service 名称不是环境判定依据,恢复前仍须核对数据库身份。整库 custom-format 归档能保留 SandIAM 与宿主的一致时间点:
set -eu
: "${SOURCE_PG_SERVICE:?source PostgreSQL service name is required}"
umask 077
BACKUP_FILE="sandadmin-$(date -u +%Y%m%dT%H%M%SZ).dump"
PGSERVICE="$SOURCE_PG_SERVICE" pg_dump --verbose --format=custom --no-owner --no-acl --file="$BACKUP_FILE"
pg_restore --list "$BACKUP_FILE" >"$BACKUP_FILE.list"
sha256sum "$BACKUP_FILE" "$BACKUP_FILE.list"保存命令退出状态、PostgreSQL/SandAdmin/SandPackage/SandIAM 版本、两个 SHA-256 和备份时间。
pg_dump、pg_restore --list 或摘要计算任一失败,就不能把文件登记为可恢复备份。列表中应能找到
sand_iam_* 表、相关序列/约束和 SandIAM 迁移账本;列表校验只证明归档可读取,不能代替恢复演练。
-
在隔离环境锁定与备份匹配的 SandAdmin、SandPackage 和 SandIAM 版本。
-
由环境负责人预先提供空的隔离 PostgreSQL 数据库并配置
RESTORE_PG_SERVICE指定的 libpq service。本流程不创建数据库, 不允许把RESTORE_PG_SERVICE指向生产库,也不得使用--create、--clean或自动建库选项。 真正查询数据库身份、更改数据或执行恢复,都必须另获当次授权;下面的身份查询本身只读,也不能成为 创建数据库的授权。 -
不比较 DSN、IP 或端口:这些都可能因 IPv4/IPv6、代理或连接池而不同,且仍指向同一 PostgreSQL 集群/数据库。环境负责人必须预先批准完整的
system_identifier:database_oid:database_name_hex恢复目标身份, 并指定明确的非生产环境枚举。以下 guard 只读查询(pg_control_system()).system_identifier、当前数据库在pg_database中的 OID,以及非系统 schema 的用户关系数。查询无权限、返回空/多行、身份同一、目标不精确 匹配、环境不是允许枚举,或恢复目标已有用户关系时,都在调用pg_restore前退出。source、target 和批准身份 都必须是单行 ASCII^[0-9]+:[0-9]+:[0-9a-f]+$,因此 warning、空值、空白、额外字段或其他命令输出也会 fail-closed;它不会创建数据库:set -eu : "${SOURCE_PG_SERVICE:?source PostgreSQL service name is required}" : "${RESTORE_PG_SERVICE:?restore PostgreSQL service name is required}" : "${BACKUP_FILE:?verified backup file is required}" : "${RESTORE_TARGET_IDENTITY:?pre-approved restore identity is required}" : "${RESTORE_TARGET_ENVIRONMENT:?explicit non-production environment is required}" identity_sql="SELECT (pg_control_system()).system_identifier::text || ':' || d.oid::text || ':' || encode(convert_to(current_database(), 'UTF8'), 'hex') FROM pg_catalog.pg_database AS d WHERE d.datname = current_database()" empty_target_sql="SELECT count(*)::text FROM pg_catalog.pg_class AS c JOIN pg_catalog.pg_namespace AS n ON n.oid = c.relnamespace WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema' AND c.relkind IN ('r', 'p', 'm', 'S', 'v', 'f')" identity_is_valid() { identity="$1" case "$identity" in *[!0-9a-f:]*|:*|*:) return 1 ;; esac previous_ifs="$IFS"; IFS=: set -- $identity IFS="$previous_ifs" [ "$#" -eq 3 ] || return 1 system_identifier="$1"; database_oid="$2"; database_name_hex="$3" case "$system_identifier" in 0|0[0-9]*|'') return 1 ;; esac case "$database_oid" in 0|0[0-9]*|'') return 1 ;; esac decimal_not_greater_than "$system_identifier" 18446744073709551615 || return 1 decimal_not_greater_than "$database_oid" 4294967295 || return 1 [ "${#database_name_hex}" -ge 2 ] && [ "${#database_name_hex}" -le 126 ] || return 1 [ $(( ${#database_name_hex} % 2 )) -eq 0 ] || return 1 # Valid nonempty hex has a nonempty decoded byte sequence; no shell # arithmetic or lossy command substitution is used to parse identities. return 0 } decimal_not_greater_than() { value="$1"; maximum="$2" case "$value" in *[!0-9]*|'') return 1 ;; esac [ "${#value}" -lt "${#maximum}" ] && return 0 [ "${#value}" -gt "${#maximum}" ] && return 1 [ "$value" \> "$maximum" ] && return 1 return 0 } source_identity="$(PGSERVICE="$SOURCE_PG_SERVICE" psql -X -v ON_ERROR_STOP=1 -Atqc "$identity_sql")" || exit 1 restore_identity="$(PGSERVICE="$RESTORE_PG_SERVICE" psql -X -v ON_ERROR_STOP=1 -Atqc "$identity_sql")" || exit 1 restore_user_relations="$(PGSERVICE="$RESTORE_PG_SERVICE" psql -X -v ON_ERROR_STOP=1 -Atqc "$empty_target_sql")" || exit 1 identity_is_valid "$source_identity" || exit 1 identity_is_valid "$restore_identity" || exit 1 identity_is_valid "$RESTORE_TARGET_IDENTITY" || exit 1 source_identity_key="${source_identity%:*}" restore_identity_key="${restore_identity%:*}" [ "$source_identity_key" != "$restore_identity_key" ] || exit 1 [ "$restore_identity" = "$RESTORE_TARGET_IDENTITY" ] || exit 1 case "$RESTORE_TARGET_ENVIRONMENT" in development|test|staging|isolated) ;; *) exit 1 ;; esac [ "$restore_user_relations" = "0" ] || exit 1 pg_restore --verbose --exit-on-error --single-transaction --no-owner --no-acl \ --dbname="service=$RESTORE_PG_SERVICE" "$BACKUP_FILE"
-
恢复与备份匹配的插件文件、管理端/门户产物和配置,重新关联正确的密钥版本。不要从 demo 或恢复环境反向覆盖权威源码。
-
检查迁移账本、组织/应用/环境层级、用户与机器身份、策略、凭证状态和审计连续性。
-
分别验证允许、拒绝、撤销后拒绝、Webhook 幂等、同步游标和协议登录。
-
确认恢复前已经撤销的会话和凭证不会复活,恢复后的新审计可以继续写入并关联 request id。
恢复演练不得覆盖生产数据库。演练结束后按隔离环境的授权流程清理,不删除需要保留的审计证据。 只有恢复命令成功、关键对象与撤销状态一致、允许/拒绝/审计链通过,并确认宿主及其他插件未受损, 才能把该备份标记为“已验证可恢复”。
发布维护者需要为候选包和恢复演练保留可独立复核的证据:候选 ZIP 与摘要、备份与恢复目标身份、 PostgreSQL 归档可读性、恢复后的关键对象/撤销状态、允许/拒绝/审计探针,以及清理记录。仓库内的离线 证据校验工具只校验证据结构;它不执行备份或恢复,也不能证明真实恢复成功。证据格式、发布门槛和维护者 验收机制不属于部署者的日常恢复操作,按项目维护流程另行处理。
数据库和密钥版本必须来自兼容时间点。若密钥不可恢复,应按事件响应流程轮换相关签名、加密、Webhook 和机器凭证,并强制失效旧会话。不要通过关闭验证或恢复旧明文绕过密钥不一致。
版本更新
开始使用
日常操作
业务接入
- 应用接入
- PHP SDK
- TypeScript SDK
- Dart / Flutter SDK
- Webman 示例
- 独立 consumer
- 机器调用
- 机器 provider
- OpenAPI 导入
- 导入 JSON 示例
运维与维护