Data Formulator 凭证保险箱(Credential Vault)深度解析:加密存储、安全模型与部署实践
【免费下载链接】data-formulator🪄 Data Formulator is an interactive AI-powered data analysis system makes it easy to connect, explore and visualize data.项目地址: https://gitcode.com/GitHub_Trending/da/data-formulator
适用版本:Data Formulator 0.7+ | 面向读者:部署运维人员、管理员、需要理解凭证保存行为的用户
Data Formulator 是面向交互式 AI 数据分析的开放系统,支持连接并探索数据库、数据湖等外部数据源。为了在服务端安全保存数据库密码、访问 token、API key 等敏感凭证,项目引入了 Credential Vault(凭证保险箱)机制。本文以 docs/docs-cn/6-credential-vault.md 为主体,结合py-src/data_formulator/auth/vault/源码与测试用例,系统讲解 Vault 的零配置启动、Fernet 加密安全模型、DataConnector 生命周期集成、TokenStore 优先级链、手动 REST API 以及 Docker 部署与密钥管理,帮助读者完整掌握凭证的安全保存与恢复策略。
1. 功能简介:Vault 到底保存什么、不保存什么
Credential Vault 用于在服务端加密保存外部数据源凭证,典型内容包括:
- 数据库密码(MySQL、PostgreSQL、SQL Server、Kusto、MongoDB、CosmosDB 等);
- 访问 token(Superset 等 SSO 数据源的 service token);
- API key(BigQuery、S3、Azure Blob、Athena 等云服务的凭证)。
DataConnector 连接成功后,可以把敏感凭证保存到 vault;后续用户打开同一个连接时无需重新输入,直接由后端从 vault 解密取出并重建 loader。
需要特别强调边界:Vault只保存凭证,不保存连接卡片本身。连接卡片(connector 定义)保存在 YAML 配置文件中,按管理员全局与用户个人分两级:
DATA_FORMULATOR_HOME/connectors.yaml # 管理员全局连接 DATA_FORMULATOR_HOME/users/<identity>/connectors.yaml # 用户个人连接注:从 data_connector.py 的
create_connector实现看,用户连接除了上述users/<identity>/connectors.yaml,还会以 JSON 形式持久化到DATA_FORMULATOR_HOME/users/<identity>/connectors/<source_id>.json,二者共同构成"连接定义"层;而密码等敏感字段被_connector_config_params(data_connector.py)剥离,只把非敏感参数落盘,敏感凭证一律进入 vault。
这种"定义与凭证分离"的设计是本文理解一切后续行为的基础。
2. 零配置启动:本地开箱即用
本地使用无需任何配置。首次使用 vault 时系统自动完成两件事:
- 生成 Fernet 加密密钥,写入
DATA_FORMULATOR_HOME/.vault_key; - 创建加密数据库
DATA_FORMULATOR_HOME/credentials.db。
首次运行后,数据目录结构大致如下:
~/.data_formulator/ ├── .vault_key ├── credentials.db ├── connectors.yaml # 可选,管理员预配置连接 └── users/ └── <identity>/ ├── connectors.yaml # 用户创建的连接 └── ...这些文件在重启和升级后持续保留,迁移服务器时必须一起备份(详见 docs/docs-cn/7-server-migration-guide.md)。
源码级印证:密钥解析与库表结构
零配置自动化的实现位于 auth/vault/init.py 的_resolve_key:先检查CREDENTIAL_VAULT_KEY环境变量,未设置时读取.vault_key文件;若文件也不存在,则调用Fernet.generate_key()自动生成并写回文件(key_file.write_text(new_key + "\n", encoding="utf-8"))。get_credential_vault()是全局单例工厂,还有两个特殊分支:
- 当通过
disable_data_connectors禁用数据连接器时(如 ephemeral demo 部署),跳过 vault 创建; - 当密钥解析失败时返回
None,插件退化为仅会话存储。
credentials.db的库表由 auth/vault/local_vault.py 的_init_db创建,核心为一张credentials表:
CREATE TABLE IF NOT EXISTS credentials ( user_id TEXT NOT NULL, source_key TEXT NOT NULL, encrypted_data BLOB NOT NULL, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (user_id, source_key) )encrypted_data是经 Fernet 加密后的密文 BLOB,主键(user_id, source_key)直接对应下文"访问隔离"按身份 + 数据源 key 的设计。
3. 安全模型:Fernet 加密 + 逻辑隔离
Vault 的安全设计可归纳为下表:
| 方面 | 设计 |
|---|---|
| 加密算法 | Fernet(AES-128-CBC + HMAC-SHA256) |
| 密钥存储 | DATA_FORMULATOR_HOME/.vault_key,或CREDENTIAL_VAULT_KEY环境变量 |
| 数据库位置 | DATA_FORMULATOR_HOME/credentials.db |
| 访问隔离 | 按(user_identity, connector_id)逻辑隔离 |
| 前端隔离 | 明文凭证不返回前端;前端只看到连接状态、参数表单和非敏感配置 |
| 传输安全 | 生产环境应使用 HTTPS |
几个要点需要展开:
- Vault 是逻辑隔离,不是每用户一个数据库文件。所有用户的凭证都放在同一个
credentials.db中,通过user_id区分。因此服务端进程持有加密密钥,备份与权限管理应以整个DATA_FORMULATOR_HOME为单位处理。 - 加密写入:
store将凭证字典json.dumps后经self._fernet.encrypt(...)加密,再写入 SQLite;retrieve则decrypt后json.loads还原(见 local_vault.py)。若解密失败(如密钥不匹配),retrieve返回None并记录 warning,而不是崩溃。 - 身份命名空间:
user_id来自 auth/identity.py 的get_identity_id(),取值形如user:alice@corp.com(OIDC 认证用户)、local:<os_username>(单用户 localhost)、browser:<uuid>(匿名浏览器 UUID)。前缀命名空间确保认证用户的数据无法通过伪造 header 访问,匿名用户也无法读取他人凭证。 - 前端隔离:所有凭证 API 都不向浏览器返回明文。
/api/credentials/list只返回"哪些 source key 存了凭证"。
传输层安全不在 vault 本身范畴:生产环境必须通过反向代理或直接配置启用 HTTPS,避免凭证在网络上明文传输。
4. DataConnector 工作流程:创建 / 重连 / 断开与删除
4.1 创建连接
用户填写连接参数后,后端依次执行:
用户填写连接参数 ↓ POST /api/connectors ↓ 后端创建用户 connector 定义 ↓ 如果参数足够,立即连接并测试 ↓ 非敏感参数写入 users/<identity>/connectors.yaml 敏感凭证加密写入 credentials.db从源码看,create_connector(data_connector.py)会先剥离敏感参数生成default_params并持久化连接定义;随后若提供了connect_params,调用connector._connect(connect_params)建 loader 并test_connection();测试通过后调用connector._persist_credentials(connect_params),由_vault_store将完整user_params(含密码)加密写入 vault(见 data_connector.py)。注意:只有连接测试成功才会持久化凭证,避免把错误密码写进库。
4.2 重新连接
用户点击已存在的数据源卡片 ↓ 后端按 identity + connector_id 查找连接定义 ↓ 如需要凭证,则从 vault 取出并创建 loader ↓ 连接成功后进入 catalog 浏览和导入界面实际重连逻辑在_try_auto_reconnect(data_connector.py):从 vault 取出stored_params与默认参数合并,创建 loader 并test_connection()。关键实现细节包括:
- 重试与退避:最多尝试
_RECONNECT_MAX_ATTEMPTS = 3次,退避间隔为0.5s、1.0s(_RECONNECT_BACKOFF_BASE = 0.5),用于扛过网络抖动、token 交换等瞬时故障; - 过期凭证清理:只有当所有尝试都失败时,才
_vault_delete(identity)清除失效凭证; - 两级兜底:无 vault 凭证时,对 token/SSO 模式走
_try_sso_auto_connect;对 Kusto 这类使用DefaultAzureCredential/托管身份的环境凭据连接器,走_try_ambient_reconnect直接用持久化的连接参数重连(见 data_connector.py)。
4.3 断开和删除
| 操作 | 连接卡片 | 当前 loader | Vault 凭证 |
|---|---|---|---|
| Disconnect | 保留 | 清除 | 清除当前服务 token/凭证 |
| Delete | 删除 | 清除 | 删除 |
- Disconnect适合临时切换账号或清理当前授权:连接定义保留,方便之后一键重连;
- Delete表示不再需要该用户连接:连接定义、内存 loader、vault 凭证一并清除;
- 管理员预配置的连接不能由普通用户删除:从 data_connector.py 的
delete_connector看,删除用户连接时同时清除 vault 凭证并移除配置文件;管理员(admin)连接删除会返回 403。
在 TokenStore 侧,clear_service_token(token_store.py)会同时清除 Session 缓存和 vault 凭证,并对sso_exchange模式的数据源在当前浏览器会话中阻止自动重连,直到用户显式重新登录。
5. TokenStore 与 SSO:六级凭证解析优先级链
对于 Superset 等支持 SSO 或弹窗委托登录的数据源,系统并非直接使用 vault,而是通过统一的 auth/token_store.py 解析凭证。其get_access(system_id)实现了一个六级优先级链:
- Session 中已有目标系统 token(未过期则直接返回
access_token); - refresh token 可续期(向
token_url发起 refresh_token 换取新 token); - 用 Data Formulator 的 SSO token 换取目标系统 token(
sso_exchange,POST 到exchange_url); - 使用弹窗委托登录保存的 token(
delegated模式); - 使用 vault 中保存的静态凭证(
_try_vault→vault.retrieve(identity, system_id)); - 无可用凭证,提示用户重新授权或重新输入。
其中第 5 级的_vault_retrieve(token_store.py)会通过get_identity_id()取得当前身份,再调用 vault 单例按(identity, system_id)读取。这解释了文档中"TokenStore 会优先使用当前 Session 中的 service token 或通过 SSO exchange 获取目标系统 token,静态凭证是更靠后的兜底"的行为:动态 token 优先于静态密码,vault 主要用于没有 SSO 能力的场景或刷新链路全部失效时的最后手段。
从源码结构看,_all_auth_configs会遍历所有已注册的DATA_LOADERS,收集声明了auth_config()(mode 为sso_exchange/oauth2/delegated等)的 loader,为其建立 token 解析通道。OIDC/TokenStore 的完整开发细节见 docs/dev-guides/4-authentication-oidc-tokenstore.md。
6. 手动 API:通用凭证接口
尽管普通 DataConnector 流程无需直接调用,系统仍保留一套通用的凭证 REST API(实现于 routes/credentials.py):
| 方法 | 路径 | 用途 |
|---|---|---|
| GET | /api/credentials/list | 列出有存储凭证的 source key,不暴露密码 |
| POST | /api/credentials/store | 存储或更新凭证 |
| POST | /api/credentials/delete | 删除凭证 |
接口行为要点:
- 全部按身份隔离:每个端点都通过
get_identity_id()取得当前用户,只能读写自己的凭证; /list不暴露明文:只返回{"sources": [...]},即存有凭证的 source_key 列表(见 credentials.py);/store校验必填字段:缺少source_key或credentials时返回INVALID_REQUEST;vault 未配置时返回SERVICE_UNAVAILABLE(见 credentials.py)。
DataConnector 的连接、断开、删除操作会通过/api/connectors/*路由完成对应的 vault 生命周期处理,因此普通用户通常不需要直接调用上述三个端点。
7. Docker 部署:挂载数据目录 + 外部密钥注入
7.1 挂载整个数据目录
Docker 部署时只需挂载完整数据目录,密钥、凭证库、连接配置和用户数据都会包含其中:
volumes: - df-data:/root/.data_formulator注意挂载点必须是DATA_FORMULATOR_HOME的实际路径(镜像内默认~/.data_formulator),且必须保留.vault_key与credentials.db两个文件——只挂载部分目录会导致密钥与密文分离,vault 将无法解密。
7.2 使用外部 Secret Manager 注入密钥
如果使用外部 Secret Manager(如云厂商的密钥托管服务)管理密钥,可以通过环境变量注入,避免密钥落盘:
CREDENTIAL_VAULT_KEY=<your-fernet-key>设置后,.vault_key文件将被忽略,系统直接使用环境变量中的密钥。这为容器化部署提供了确定性密钥(_resolve_key中环境变量优先级高于文件,见 auth/vault/init.py)。
8. 生成 Vault 密钥与密钥丢失后果
需要外部生成 Fernet 密钥(例如在 CI/CD 中预生成再注入 Secret Manager)时,使用:
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"密钥丢失的后果
如果密钥丢失,credentials.db中的已保存凭证无法恢复——Fernet 的 HMAC-SHA256 完整性校验会直接拒绝解密(对应retrieve中except Exception返回None的分支)。届时用户需要:
- 重新输入外部数据源密码;
- 或重新走 SSO/弹窗委托授权流程。
因此运维上的建议是:DATA_FORMULATOR_HOME(含.vault_key、credentials.db、connectors.yaml)应整体纳入备份策略;密钥本身应像其他主密钥一样受控保管,并考虑定期轮换(轮换时需同步迁移已加密数据或接受重新授权成本)。
9. 相关文件与源码地图
Vault 功能的核心源码位于:
py-src/data_formulator/auth/vault/ ├── __init__.py # 工厂:密钥解析(env > .vault_key > 自动生成)、单例创建 ├── base.py # CredentialVault 抽象接口(store/retrieve/delete/list_sources) └── local_vault.py # LocalCredentialVault:SQLite + Fernet 实现周边配合模块:
- auth/token_store.py —— 六级凭证解析链,统一供 Agent、DataConnector、routes 调用;
- auth/identity.py —— 身份解析与命名空间(
user:/local:/browser:); - data_connector.py —— 连接生命周期与 vault 集成(
_vault_store/_vault_retrieve/_vault_delete、自动重连); - routes/credentials.py —— 手动凭证 API;
- tests/backend/auth/test_credential_vault.py 与 tests/backend/auth/test_credential_vault_factory.py —— 覆盖加解密、工厂密钥解析优先级、vault 未配置等分支的单元测试。
相关文档:
- docs/docs-cn/1-data-source-connections.md
- docs/docs-cn/7-server-migration-guide.md
- docs/dev-guides/4-authentication-oidc-tokenstore.md
- docs/dev-guides/5-data-connector-api.md
小结
Data Formulator 的 Credential Vault 用一条清晰的职责边界(连接定义落盘 YAML、敏感凭证加密入库)解决了多数据源场景下"免重复输入密码"与"明文凭证不落盘"的矛盾:本地单用户零配置即可启用 Fernet 加密;多用户部署按(identity, source_key)逻辑隔离;Docker/生产环境可通过CREDENTIAL_VAULT_KEY把密钥管理上移到 Secret Manager。理解密钥丢失即凭证不可恢复这一约束,并在备份、迁移(参考 docs/docs-cn/7-server-migration-guide.md)与轮换策略中把.vault_key与credentials.db作为一个整体对待,是安全运维该功能的关键。
【免费下载链接】data-formulator🪄 Data Formulator is an interactive AI-powered data analysis system makes it easy to connect, explore and visualize data.项目地址: https://gitcode.com/GitHub_Trending/da/data-formulator
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考