Repository navigation
Docker Production Root Identity Reset CN
这是一份面向拥有 PostgreSQL 管理权限的运维教程。它处理的是通用的身份恢复场景,不依赖系统备份:保留既有 root 用户的 UUID,只重置账号、邮箱和密码。
users.id 不变
-> 业务资源 owner 不变
-> 审计记录关联不变
-> workspace membership / role binding 不变
不要删除 root 后重新创建。那会生成新的 UUID,并使依赖旧 user_id 的数据失去原 owner。
- 数据库为 PostgreSQL,且已完成当前 1flowbase migration。
- 操作对象是唯一的
default_display_role = 'root'用户。 - 操作会使该用户的旧登录会话失效。
- 账号和邮箱必须在系统中唯一;若与其他用户的本地登录 identity 冲突,脚本会回滚。
先停止入口容器,避免在身份记录更新的中间状态接受登录请求;内置数据库的 db 与 plugin-runner 可以保持运行:
cd /srv/1flowbase/docker
docker compose stop api web外部 PostgreSQL 部署将每个命令替换为 docker compose -f docker-compose.external-db.yaml ...。
先确认目标 UID。内置 PostgreSQL 示例:
docker compose exec db psql -U "${POSTGRES_USER:-postgres}" -d "${POSTGRES_DB:-1flowbase}" \
-c "select id, account, email from users where default_display_role = 'root';"结果必须恰好一行。若为零行或多行,停止操作并先排查数据库状态。
.env 里的 BOOTSTRAP_ROOT_PASSWORD 是明文输入,不能直接写入 users.password_hash。应用使用 Argon2id 哈希;以下脚本会生成哈希,并在单个事务中同步 users 和 user_auth_identities。
在可信运维机器上安装一次依赖(Node 20+):
mkdir -p ~/1flowbase-ops && cd ~/1flowbase-ops
npm install pg argon2保存为 reset-root-identity.mjs:
import { readFile } from 'node:fs/promises';
import { randomUUID } from 'node:crypto';
import process from 'node:process';
import argon2 from 'argon2';
import pg from 'pg';
const envFile = process.argv[2];
if (!envFile || !process.env.DATABASE_URL) {
throw new Error('usage: DATABASE_URL=postgres://... node reset-root-identity.mjs /path/to/docker/.env');
}
const entries = (await readFile(envFile, 'utf8'))
.split(/\r?\n/)
.filter((line) => line && !line.startsWith('#') && line.includes('='))
.map((line) => {
const index = line.indexOf('=');
return [line.slice(0, index), line.slice(index + 1)];
});
const env = Object.fromEntries(entries);
const account = env.BOOTSTRAP_ROOT_ACCOUNT?.trim();
const email = env.BOOTSTRAP_ROOT_EMAIL?.trim();
const password = env.BOOTSTRAP_ROOT_PASSWORD;
if (!account || !email || !password) {
throw new Error('docker/.env must contain BOOTSTRAP_ROOT_ACCOUNT, BOOTSTRAP_ROOT_EMAIL, and BOOTSTRAP_ROOT_PASSWORD');
}
const passwordLocalAuthenticatorId = '00000000-0000-0000-0000-000000000001';
const client = new pg.Client({ connectionString: process.env.DATABASE_URL });
await client.connect();
try {
await client.query('begin');
const roots = await client.query(
"select id from users where default_display_role = 'root' for update",
);
if (roots.rowCount !== 1) {
throw new Error(`expected exactly one root user, found ${roots.rowCount}`);
}
const userId = roots.rows[0].id;
const conflict = await client.query(
`select user_id from user_auth_identities
where authenticator_id = $1
and lower(subject_value) = any($2::text[])
and user_id <> $3
limit 1`,
[passwordLocalAuthenticatorId, [account.toLowerCase(), email.toLowerCase()], userId],
);
if (conflict.rowCount) {
throw new Error('the requested account or email already belongs to another local-login user');
}
const passwordHash = await argon2.hash(password, { type: argon2.argon2id });
await client.query(
`update users
set account = $2, email = $3, password_hash = $4,
session_version = session_version + 1,
updated_by = $1, updated_at = now()
where id = $1`,
[userId, account, email, passwordHash],
);
await client.query(
`delete from user_auth_identities
where user_id = $1 and authenticator_id = $2
and subject_type in ('account', 'email')`,
[userId, passwordLocalAuthenticatorId],
);
for (const [subjectType, subjectValue] of [['account', account], ['email', email]]) {
await client.query(
`insert into user_auth_identities
(id, user_id, authenticator_id, subject_type, subject_value, metadata, created_by, updated_by)
values ($1, $2, $3, $4, $5, '{}'::jsonb, $2, $2)`,
[randomUUID(), userId, passwordLocalAuthenticatorId, subjectType, subjectValue],
);
}
await client.query('commit');
console.log(`root identity reset for existing user ${userId}`);
} catch (error) {
await client.query('rollback');
throw error;
} finally {
await client.end();
}在 .env 中设置目标账号、邮箱和密码。不要把密码放进命令行、shell history 或脚本源码。然后从能访问 PostgreSQL 的可信机器执行:
DATABASE_URL='postgres://flowbase:***@db.example.internal:5432/1flowbase?sslmode=require' \
node ~/1flowbase-ops/reset-root-identity.mjs /srv/1flowbase/docker/.env内置 PostgreSQL 的 DATABASE_URL 应使用运维侧能访问的主机地址、映射端口及 docker/.env 中的数据库账号、密码和库名;不要把容器内部地址 db:5432 用在宿主机 Node 进程上。
成功后重启入口服务:
cd /srv/1flowbase/docker
docker compose up -d api web使用新账号和新密码完成一次登录验证。旧密码和旧浏览器 session 应失效。
如果账号与邮箱都不变,且你已经在安全环境中生成了兼容的 Argon2id hash,可以只更新密码。不要使用 PostgreSQL 的 MD5、crypt() 或明文;这些格式不符合 1flowbase 的本地密码校验。
begin;
select id, account, email
from users
where default_display_role = 'root'
for update;
update users
set password_hash = '<预先生成的 Argon2id hash>',
session_version = session_version + 1,
updated_at = now()
where id = '<上一步确认的 root UUID>';
commit;不要用这段 SQL 改账号或邮箱。账号/邮箱登录依赖 user_auth_identities,只改 users 会造成登录 identity 与用户资料不一致;请使用上面的事务脚本。
立即停止并回滚,而不是猜测修复方式,如果发生以下任一情况:
- root 查询不是恰好一行;
- 新账号或邮箱已被其他用户占用;
- 数据库连接、事务、identity 写入或登录验证失败;
- 需要同时修改多个 root 用户,或不确定哪个 UID 是目标。