alitrack

Quack 协议的生产环境缺了最后一块拼图。有人用 OAuth 补上了

DuckDB 今年 5 月发布了 Quack 协议——一个原生的 HTTP 客户端-服务器协议,让 DuckDB 从嵌入式引擎变成了可以被远程连接的数据库服务。单端口搞定查询和并行批量数据传输,6000 万行数据 4.94 秒搞定,比 Arrow Flight 快 3.5 倍。

但 Quack 有个尴尬的地方:它的认证机制是个 stub。

Quack 协议内置了两个回调——quack_check_token 和 quack_nop_authorization。前者做的是共享密钥比对,后者永远返回"允许"。单人实验室够用,但放到团队、企业、或者任何需要多用户访问的场景里,等于没有认证。

5 月 14 日,DataZooDE 在 GitHub 上发布了 quack-oauth,名字很直白——就是给 Quack 协议加 OAuth 的。一个 MIT 协议的 DuckDB 扩展,在 Quack 的现有回调接口上外挂了一层标准 OAuth 2.1 / OIDC 认证和基于声明的授权,一行 Quack 源码都没改。

● ● ●

它做了什么

Quack 预留了两个标量函数作为认证回调的入口。quack-oauth 提供了自己的实现,替换掉默认的 stub:

SET quack_authentication_function = 'quack_oauth_check_token';
SET quack_authorization_function  = 'quack_oauth_check_authorization';

两行 SQL 就把一个"认共享密钥"的 Quack 服务变成了"认 OAuth Bearer Token"的生产级服务。

认证层支持四种验证模式:

  • JWKS:本地下载签名公钥,离线验证 JWT 签名(Keycloak、Entra 默认)
  • RFC 7662 introspection:把 opaque token 发回 IdP 的 introspection endpoint 校验
  • Google tokeninfo:Google 特有的 token 验证端点
  • GitHub token check:GitHub 不实现 OIDC 也不实现 RFC 7662,quack-oauth 专门为它写了适配器,POST 到 api.github.com/applications/{client_id}/token

授权层更有意思。验证通过后,JWT 里的 claims(sub、iss、scopes、exp)被提取成 Principal。每个进来的 SQL 请求会被解析出 action 类型——Attach(连接)、Scan(查询)、CopyTo(导出)、CopyFrom(导入)等——然后和 Principal 一起交给策略引擎做准入判断。

策略存在哪里?SQL 表里。

CREATE TABLE main.policies (
    priority  INTEGER NOT NULL,
    subject   VARCHAR,
    any_scope VARCHAR[],
    actions   VARCHAR[],
    allow     BOOLEAN NOT NULL
);

INSERT INTO main.policies VALUES
    (10, NULL, ['quack:read'],  ['Attach', 'Scan'],                       true),
    (20, NULL, ['quack:write'], ['Attach', 'Scan', 'CopyTo', 'CopyFrom'], true);

没有 YAML 配置文件,没有自定义 DSL,没有重启。INSERT 一行,下一条查询就能看到新规则生效。hot-reload 是内置的。默认策略是 deny(fail-closed),没匹配到规则就拒绝。

每次鉴权决策都记入内存 audit ring,可选持久化到 SQL audit 表。bearer token 不会明文记录——只存 SHA-256 的 8 位十六进制前缀用于去重。

● ● ●

支持的 IdP(开箱即用)

quack-oauth 内置了 provider preset,告诉它你用哪个 IdP,它自动填好 issuer URL、JWKS URI、introspection endpoint:

Provider验证模式备注
KeycloakJWKS`tenant_or_realm` = 完整 realm URL
Microsoft Entra IDJWKS支持 `tid` claim allowlist
Googletokeninfo`sub` + `scope`(space-separated)
GitHubgithub_check非 OIDC,专用适配器
generic任意手动配置所有 URL

客户端这一侧也不只是发 token。quack-oauth 提供了三个 OAuth flow 的客户端函数:

  • quack_oauth_login(secret_name) — client_credentials grant,服务间通信
  • quack_oauth_refresh(secret_name) — refresh_token grant,长连接自动续期
  • quack_oauth_device_login(secret_name) — device_code grant,CLI/嵌入式设备

而且客户端可以做到零手动 token 管理:

ATTACH 'quack:server.example.com:9494' AS srv (
    TYPE quack,
    token quack_oauth_acquire('cli')
);

quack_oauth_acquire 自动从 client secret 里读 IdP 配置,用 client_credentials 去 IdP 换 access token,缓存到过期前复用。每次 ATTACH 不需要贴 JWT,不需要手动 refresh。

● ● ●

架构上的几个决策

quack-oauth 的架构文档里有一些值得注意的设计决策:

  1. 01不修改 quack 源码。通过替换回调函数名来接入,关闭只需要把 SET 改回去或清掉 quack_oauth_enabled。
  2. 02Token 在 Quack 的 RPC 消息里传输,不走 HTTP header。因为 Quack 本身不是 RESTful——它是自定义二进制协议跑在 HTTP 上。
  3. 03Caddy 只做 TLS 终止,token 验证在 DuckDB extension 内部完成,不依赖反向代理做 auth。
  4. 04Wasm 构建不跑 OAuth,浏览器端由 host JS 通过 PKCE flow 拿 token 后注入。省了约 30% 代码体积。
  5. 05GitHub 是非标准接入。GitHub 不发 JWT,不实现 RFC 7662,quack-oauth 专门写了一个适配层,把 applications/{client_id}/token 的返回映射成标准 Principal 结构。

● ● ●

谁做的

代码仓库在 github.com/DataZooDE/quack-oauth,MIT 协议,9 个 star,2 个贡献者(jrosskopf 和 claude)。5 月 14 日创建,5 月 28 日发了第一个 release。CI 通过,end-to-end 测试覆盖了真实 Keycloak 环境下的完整 client→server 流程,Entra ID 和 Google 有录制重放覆盖。

安装只需要两行 DuckDB SQL(extension 未签名,需要 -unsigned 参数启动):

INSTALL 'quack_oauth' FROM 'http://get.erpl.io';
LOAD 'quack_oauth';

一句安装,一句加载,一句换回调,Quack 就有生产级认证了。

● ● ●

这意味着什么

Quack 协议让 DuckDB 从嵌入式引擎变成了可以被远程连接的数据库服务端。但一个没有正经认证的服务端协议,只能活在 localhost 和 homelab 里。

quack-oauth 补上了这个缺口。它不是 DuckDB 官方出的,但它是 Quack 生态走向生产可用的关键一块——跟 Quack 协议本身的后端存储(DuckLake)、前端工具(Duckle)、和云平台(MotherDuck Flights)一起,正在把 DuckDB 从一个单机库推向一个完整的数据平台。

DuckDB v2.0 预计今年秋天发布,Quack 届时将从 extension 升级为核心组件。到那时,认证层就是标配而不是补丁了。

github.com/DataZooDE/quack-oauth