DuckDB 做 Server 还差什么?RBAC,社区已经补上了
2026 年 5 月,DuckDB Labs 发布了 Quack —— 第一个官方 client-server 协议。DuckDB 从此不再是只能嵌入应用的单机引擎。
但 MotherDuck 的博客写得很直白:
"所有客户端共享同一个 token,没有角色权限。Quack 没法区分不同用户。"
一句话说透了 Quack 最大的短板:没有访问控制。Server 模式下所有人要么全权读写、要么只读。这在任何生产环境都不可接受。
最直接的解法是上 MotherDuck。它内置了数据库级权限隔离、Read-Scaling Token、SSO 对接,SOC 2 + GDPR 认证,是 DuckDB 生态里唯一的"开箱即用"方案。代价是付费。
如果你不想为托管服务买单——或者你更想控制自己的基础设施——DuckDB 的扩展机制已经提供了完整的钩子,社区也在快速补齐这块拼图。本文拆解四种开源方案,从零代码到 C++ 扩展,覆盖不同的场景和投入。
四种方案架构总览
● ● ●
先搞清楚:MotherDuck 的"RBAC"长什么样
MotherDuck 其实没有传统 RBAC(CREATE ROLE / GRANT SELECT ON table TO role)。他们用了更简单的模型:
| Database 级隔离 | |
| Read-Scaling Token | |
| Zero-Copy SHARE | |
| Filtered View | CREATE VIEW team_a AS SELECT * WHERE team='A' |
MotherDuck 的官方立场:数据库级隔离 + Read-Only Token 对大多数场景已经够了。 传统行级权限的运维成本太高。
这意味着"对标 MotherDuck 的 RBAC"比想象中简单。不需要实现完整的 SQL 标准 RBAC(CREATE ROLE / GRANT / REVOKE),只需要做三件事:
- 01多用户认证
(不同 token = 不同身份) - 02权限分级
(谁可以读写、谁只能读) - 03数据隔离
(谁可以看到哪些表/库)
● ● ●
方案一:SQL MACRO — 零代码,5 分钟上线
Quack 内置了两个可替换的钩子函数:
-- 认证钩子:客户端连接时调用 quack_authentication_function(session_id, client_token, server_token) → BOOLEAN -- 授权钩子:客户端发 SQL 前调用 quack_authorization_function(connection_id, query_string) → BOOLEAN你可以用 SQL MACRO 覆盖它们,不需要写任何 C++ 代码:
-- 1. 建用户表 CREATE TABLE quack_users ( token_hash VARCHAR PRIMARY KEY, user_name VARCHAR, role VARCHAR -- 'admin' | 'writer' | 'reader' ); INSERT INTO quack_users VALUES ('alice-key-123', 'alice', 'admin'), ('bob-key-456', 'bob', 'reader'); -- 2. 覆盖认证钩子:查 token 表 CREATE MACRO check_token(sid, client_token, server_token) AS ( EXISTS (SELECT 1 FROM quack_users WHERE token_hash = client_token) ); SET GLOBAL quack_authentication_function = 'check_token'; -- 3. 覆盖授权钩子:按角色做权限检查 CREATE MACRO check_perm(sid, query) AS ( CASE -- admin 全放行 WHEN (SELECT role FROM quack_users WHERE token_hash = sid) = 'admin' THEN true -- reader 只放 SELECT 开头的语句 ELSE regexp_matches(upper(trim(query)), '^(SELECT|FROM|WITH|EXPLAIN|DESCRIBE|SHOW)\b') END ); SET GLOBAL quack_authorization_function = 'check_perm';优点:5 分钟上线、热加载(改表即生效)、不依赖任何外部工具。
局限:授权钩子只拿到原始 SQL 字符串,没有解析后的查询树。做权限控制只能靠正则匹配 SQL 文本——CTE、子查询、多语句场景下可能漏掉。
适用场景:开发环境、小团队内部使用、只需要 admin/reader 两级权限。
● ● ●
方案二:quack_oauth — 社区扩展,支持 OAuth + 策略表
德国的 DataZooDE 团队写了一个更完整的方案:quack_oauth。已发布为 DuckDB 社区扩展。
INSTALL 'quack_oauth' FROM 'http://get.erpl.io'; LOAD 'quack_oauth';它的思路是用 JWT + 策略表 替代简单的 token 查表:
quack_oauth 认证授权流程
策略表存在 DuckDB 里,热加载:
CREATE TABLE main.policies ( priority INTEGER, subject VARCHAR, -- 用户 ID,NULL = 所有用户 any_scope VARCHAR[], -- OAuth scope,如 ['quack:read'] actions VARCHAR[], -- Attach, Scan, CopyTo, CopyFrom, ServeAdmin allow BOOLEAN ); INSERT INTO main.policies VALUES (10, NULL, ['quack:read'], ['Attach', 'Scan'], true), (20, NULL, ['quack:write'], ['Attach', 'Scan', 'CopyTo', 'CopyFrom'], true);它做了什么:
真正的 JWT 验证(支持 Keycloak、Azure Entra、Google) 动作级权限(允许 SELECT、禁止 DDL) 审计日志(每次授权决定都记录到 main.audit表)客户端自动获取 token( quack_oauth_acquire())
它没做什么:表级/列级权限。因为和 MACRO 一样,它也只能拿到原始 SQL 字符串,没法知道查询涉及哪些表。
适用场景:需要对接企业 IdP(Keycloak/Azure AD)、需要审计日志的团队。
GitHub: github.com/DataZooDE/quack-oauth
● ● ●
方案三:独立 DuckDB RBAC 扩展 — 最完整的答案
如果你需要真正的 SQL 级 RBAC(表级、列级、甚至行级权限),需要在 DuckDB 引擎层面动手。社区已经有人写了详细的 RFC。
2025 年 12 月,dufferzafar 在 GitHub Gist 上发布了一份完整的实现方案。核心理念是用 DuckDB 的三个扩展钩子:
RBAC 扩展的三个钩子协作流程
目标语法:
CREATE ROLE analyst; GRANT analyst TO alice; GRANT SELECT ON orders TO analyst; GRANT SELECT (id, customer, amount) ON orders TO analyst; -- 列级权限 CREATE ROW POLICY filter ON orders FOR SELECT USING (region = current_user_region()) TO analyst;技术细节:
在 DuckDB 的查询优化器运行之前,扩展遍历 LogicalOperator 树,找到所有 LogicalGet(表扫描)节点,检查当前用户是否有权限访问这些表/列。如果有行策略,注入一个 LogicalFilter 来限制可见行。
已知局限:SELECT 问题。DuckDB 在 binding 阶段就把 SELECT 展开为显式列名,这发生在优化器扩展运行之前。所以列级权限无法"隐藏"列,只能拒绝整个查询。用户需要使用显式列名来绕过这个限制。
实现难度:需要 C++ 扩展开发能力,深度理解 DuckDB 的 binder/optimizer 内部机制。但社区 RFC 已经把架构讲清楚了,不存在未知的技术障碍。
为什么值得做成独立扩展而不是嵌进 Quack?
因为 RBAC 是 SQL 引擎层的语义问题,不是协议层问题。一个独立扩展可以在所有 DuckDB 使用模式下工作——嵌入 Python、CLI、Quack server、Arrow Flight——而不仅仅是在 client-server 模式下。
Gist: gist.github.com/dufferzafar/f12081d4f32e640966d984b33e7077e6
● ● ●
方案四:nginx 反向代理层 — 最务实的"够用"方案
如果你只是想让外部 BI 工具(Tableau、Metabase)安全地连上 Quack,最简单的方法是在反向代理层做访问控制:
# 只允许特定 IP 的 SELECT 查询 location /quack { allow 10.0.1.0/24; # 内网 BI 工具 deny all; # 只放行 GET 请求(Quack 的 SELECT 用 GET) limit_except GET { deny all; } proxy_pass http://localhost:9494; }优点:不改一行 DuckDB 代码、运维团队已有的 nginx 技能直接用。
局限:只能做 IP 级 + HTTP 方法级的粗粒度控制。没法区分"哪个用户在查"。
适用场景:BI 工具固定 IP、内网只读访问。
● ● ●
对比矩阵
● ● ●
我的选择:先 MACRO,后扩展
对于大多数 Quack 用户,方案一(SQL MACRO)应该先上线。5 分钟部署,覆盖 80% 的场景(admin/reader 两级 + token 认证)。
当你需要更细粒度的控制——比如财务团队只能看 finance schema,工程团队只能看 engineering schema——再考虑方案三(独立 RBAC 扩展)。
好消息是这些方案是递进关系。你可以今天用 MACRO 上线,下个月换成 quack_oauth 接企业 IdP,半年后自己写 RBAC 扩展做表级权限。Quack 的钩子设计让每一步迁移都很平滑。
DuckDB 从嵌入引擎变成 server 数据库,访问控制不是可选项,是必选项。MotherDuck 用托管的办法解决了这个问题。但 Quack 的钩子设计证明了一件事:你完全可以用开源组件自己搭一套 RBAC,不需要托管服务。
你现在在用 Quack 吗?你的权限是怎么做的?评论区聊聊。
相关项目:
Quack 协议文档: duckdb.org/docs/current/quack/overview Quack 安全机制: duckdb.org/docs/current/quack/security quack_oauth 扩展: github.com/DataZooDE/quack-oauth DuckDB RBAC RFC: gist.github.com/dufferzafar/f12081d4f32e640966d984b33e7077e6 MotherDuck vs Quack 对比: motherduck.com/blog/duckdb-client-server