背景 链接到标题

NetBird 自托管控制平面使用内置 Dex 作为 OIDC Provider。管理员账号在首次部署时通过 config.yaml 的 owner 字段创建,密码以 bcrypt 哈希存储。

长时间没有登录 Dashboard,密码遗忘了。Dashboard 没有提供密码重置功能,需要直接操作数据库恢复。

架构 链接到标题

NetBird 的认证数据分两处存放:

位置 文件 作用
配置 config.yaml server 启动时读取 owner.password,同步到数据库
数据库 Docker volume idp.db Dex 运行时读写的 password

Dex 的 password 表结构:

email         - 登录邮箱
hash          - bcrypt(password)
username      - 用户名
user_id       - UUID

直接改数据库?不行 链接到标题

第一个尝试是直接用 Python 更新 idp.db 的 password 表:

import sqlite3, bcrypt
conn = sqlite3.connect("idp.db")
h = bcrypt.hashpw(b"new_password", bcrypt.gensalt())
conn.execute("UPDATE password SET hash = ? WHERE email = ?",
             (h.decode(), "admin@example.com"))
conn.commit()

验证 bcrypt 哈希正确,但 Dashboard 登录一直提示 Invalid credentials。更糟的是,重启容器后密码被还原。

config.yaml 优先级 链接到标题

NetBird server 启动时会读取 config.yamlowner.password,与 idp.db 比对后同步写入。如果两者哈希不一致,config.yaml 的会覆盖数据库。

这意味着必须同时更新两处,且哈希值必须一致。

正确流程 链接到标题

1. 生成 bcrypt 哈希 链接到标题

python3 -c "import bcrypt; print(bcrypt.hashpw(b'your_password', bcrypt.gensalt()).decode())"

输出类似:

$2b$12$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

2. 更新 config.yaml 链接到标题

sudo sed -i 's|password: $2b$12$.*|password: $2b$12$你的新哈希|' /path/to/config.yaml

3. 更新 idp.db 链接到标题

可以直接在容器内执行,也可以从宿主机操作 volume:

# 容器内直接操作
sudo docker exec netbird-server python3 -c "
import sqlite3, bcrypt
conn = sqlite3.connect('/var/lib/netbird/idp.db')
h = bcrypt.hashpw(b'your_password', bcrypt.gensalt()).decode()
conn.execute('UPDATE password SET hash = ? WHERE email = ?',
             (h, 'admin@example.com'))
conn.commit()
"

# 或从宿主机操作 Docker volume
sudo python3 -c "
import sqlite3
conn = sqlite3.connect('/var/lib/docker/volumes/netbird_netbird-management/_data/idp.db')
conn.execute('UPDATE password SET hash = ? WHERE email = ?',
             ('$2b$12$你的新哈希', 'admin@example.com'))
conn.commit()
"

两个操作的 bcrypt 哈希必须相同。

4. 重启容器 链接到标题

sudo docker restart netbird-server netbird-dashboard

5. 验证 链接到标题

打开 Dashboard 登录,或在终端使用设备码流程确认:

# 请求设备码
curl -s -X POST "https://your-domain/oauth2/device/code" \
  -d "client_id=netbird-cli&scope=openid profile email"

# 用户访问 verification_uri 输入 user_code 完成授权

原理总结 链接到标题

config.yaml (owner.password) ──启动覆盖──▶ idp.db (password.hash)
                                                │
                                          Dex 认证时读取
                                                │
                                          用户登录验证

每次容器重启,server 都会将 config.yaml 的哈希同步到 idp.db。所以只改数据库没有用——配置文件的优先级更高。

如果需要批量修改或通过 CI/CD 管理密码,直接修改 config.yaml 然后重启容器即可,无需进数据库操作。

注意事项 链接到标题

  • NetBird 自 0.32.0 起将 setup key 以哈希形式存在 store.db,但 idp.db 的 password 表仍然是 bcrypt 明文哈希
  • Dex 的 local connector 不支持密码哈希算法切换,bcrypt 是唯一选项
  • 如果开启了外部 OIDC 提供商(如 Google/GitHub),管理员密码不经过 idp.db,此方法不适用
  • 建议首次部署后立即生成 Personal Access Token 备存,避免再次陷入密码遗忘的困境