Neon Proxy Authentication
Neon Proxy 认证:原生 PostgreSQL、HTTP 密码与 JWT
Neon Proxy 在原生 PostgreSQL Direct 连接中分别扮演服务端和客户端:先验证前端用户,再以该用户角色登录后端。两段 SCRAM 认证独立进行,但可以复用密钥,无需获取用户明文密码。
本文依据 Neon 开源提交 fa504217,整理于 2026-10-29。先说明原生 PG 双段 SCRAM,再补充 HTTP 密码和 JWT 路径;不涵盖 console redirect。
角色与凭据
| 连接 | Proxy 的角色 | 认证方式 |
|---|---|---|
| 客户端 → Proxy | PostgreSQL 服务端 | SCRAM-SHA-256,或支持 channel binding 的 SCRAM-SHA-256-PLUS |
| Proxy → PostgreSQL | PostgreSQL 客户端 | 使用前端认证得到的密钥,完成新的 SCRAM 认证 |
| Proxy → 控制面 | 内部 API 客户端 | 独立的服务 Bearer Token,与数据库用户密码无关 |
原生 PG:核心机制
控制面向 Proxy 提供访问策略和角色的 SCRAM verifier,结构为:
SCRAM-SHA-256$iterations:salt$StoredKey:ServerKey
- 前端认证:Proxy 识别 endpoint、role 和 database,获取策略与 verifier,检查访问来源并与客户端完成 SCRAM 交互。
- 得到密钥:Proxy 从客户端的 ClientProof 恢复并验证 ClientKey,再结合 verifier 中的 ServerKey,形成后端认证凭据。
- 后端认证:Proxy 获取或唤醒 Compute,以用户角色发送 PG 启动消息;使用 ClientKey 和 ServerKey,针对后端的新挑战完成 SCRAM。
这不是转发前端认证消息,也不是重放 ClientProof。Neon 修改的 PostgreSQL 客户端库支持直接使用密钥进行认证。
认证时序
sequenceDiagram
participant C as PG客户端
participant P as Proxy
participant CP as 控制面
participant DB as PostgreSQL
C->>P: TLS握手与StartupMessage
P->>P: 识别endpoint、role和database
P->>CP: 获取认证材料和访问策略
CP-->>P: SCRAM verifier及策略
P->>P: 访问检查与限流
P-->>C: AuthenticationSASL
C->>P: client-first
P-->>C: server-first,salt及iterations
C->>P: client-final,ClientProof
P->>P: 验证证明,得到ClientKey和ServerKey
P-->>C: server-final及AuthenticationOk
P->>CP: wake_compute
CP-->>P: 后端地址及TLS信息
P->>DB: 建连,按配置协商TLS
P->>DB: StartupMessage
DB-->>P: 新的SCRAM挑战
P->>DB: 使用密钥完成新的SCRAM交互
DB-->>P: 服务端证明、认证成功及启动响应
P-->>C: 启动参数、代理取消标识、ReadyForQuery
控制面请求可以命中 Proxy 缓存;目标 Compute 休眠时,由控制面协调启动。客户端收到 AuthenticationOk 仅表示前端认证通过,收到 ReadyForQuery 才表示后端会话也已就绪。
HTTP:密码认证
SQL over HTTP 使用 POST /sql,通过 Neon-Connection-String 请求头提供 endpoint、role、database 和密码,例如:
POST /sql
Neon-Connection-String: postgresql://app_user:YOUR_PASSWORD@ep-example.example.com/appdb
Content-Type: application/json
{"query":"SELECT $1::int AS value","params":[42]}
请求应通过 HTTPS 发送。这里不进行客户端与 Proxy 之间的 SCRAM 消息往返:Proxy 会接收到密码,使用控制面提供的 SCRAM verifier 在本地验证并派生密钥。随后复用连接池中的后端连接,或使用密钥新建后端 SCRAM 连接。HTTP 请求会进行认证,复用后端连接不等于跳过前端认证。
HTTP:JWT 认证与后端身份传递
JWT 模式使用 Authorization: Bearer <JWT>;连接串仍指定 endpoint、role、database,但不携带数据库密码:
POST /sql
Authorization: Bearer YOUR_JWT
Neon-Connection-String: postgresql://authenticated@ep-example.example.com/appdb
Content-Type: application/json
{"query":"SELECT current_user","params":[]}
该版本由 accept_jwts 配置选择 HTTP 凭据模式,不应假设同一入口会自动兼容密码和 JWT。
典型的 auth broker → local_proxy 路径如下:
sequenceDiagram
participant C as HTTP客户端
participant A as 独立Proxy / auth broker
participant J as 控制面与JWKS服务
participant L as Compute内local_proxy
participant DB as 本机PostgreSQL
C->>A: HTTPS POST /sql,Bearer JWT及连接信息
A->>J: 按需获取角色规则与JWKS公钥
J-->>A: 规则及公钥,可缓存
A->>A: 验证JWT
A->>A: 发现或唤醒目标Compute
A->>L: 转发原Bearer JWT、连接信息及SQL
L->>L: 按本地规则和JWKS再次验证JWT
L->>DB: 复用或新建本机trust连接
L->>L: 使用连接专属私钥重签已验证的claims
L->>DB: auth.jwt_session_init,设置本次身份
L->>DB: 执行SQL
DB-->>C: 经local_proxy及独立Proxy返回JSON
- 前端校验:按 endpoint 和请求 role 获取允许的 JWKS 规则,使用
kid选择密钥、验证签名;配置了 audience 时检查aud,存在exp、nbf时检查时间,容忍 30 秒时钟偏差。此实现会记录iss,但不做独立的 issuer 值匹配校验。 - 角色选择:数据库角色来自连接信息,JWKS 规则限制该角色可使用哪些密钥;不应理解为直接把 JWT 的
sub当作数据库角色。 - 后端身份:JWT 不会转换为 SCRAM 密码。本机 PostgreSQL 使用受保护的 localhost trust 路径;
local_proxy通过pg_session_jwt将已验证的 claims 绑定到当前请求,供数据库函数和 RLS 策略使用。 - 连接初始化:
local_proxy按需调用compute_ctl安装扩展、授予 schema 使用权限;每个新后端连接配置专属公钥并执行auth.init()。每次请求再重签 claims、更新jti并调用auth.jwt_session_init(),避免复用连接时沿用上一请求身份。
图示依赖 auth broker、local_proxy 和数据库扩展的配套配置,不代表所有 HTTP 请求都经过 local_proxy,也不等同于 Databricks Lakebase OAuth 的完整实现。
三种路径对照
| 路径 | 前端凭据 | Proxy 是否接收用户明文密码 | 后端身份机制 |
|---|---|---|---|
| 原生 PG Direct | SCRAM 证明 | 否,限本文的 SCRAM 路径 | 派生密钥完成新 SCRAM |
| HTTP 密码 | HTTPS 内的数据库密码 | 是 | 校验派生密钥,复用或新建 PG 连接 |
| HTTP JWT 配套路径 | Bearer JWT | 不需要数据库密码 | JWT 验证、本机 trust 连接及 claims 会话绑定 |
自建服务的关键约束
- verifier 一致:控制面与后端角色必须使用匹配的 SCRAM verifier。仅分别设置相同明文密码不够,重新设置密码可能生成不同的 salt。
- 密码更新协调:同步数据库角色、控制面认证材料与 Proxy 缓存失效,避免新旧状态不一致。
- 后端认证匹配:此密钥复用路径针对 SCRAM,不能将密钥当作通用明文密码用于其他认证方式。
- 保护认证边界:Proxy 持有敏感 verifier 和认证密钥;前后端 TLS 分别协商,前端 channel binding 不会原样延伸到后端。
- 权限由数据库执行:后端以用户角色登录,表、schema 等 SQL 权限仍由 PostgreSQL 检查。前端认证成功不能替代后端认证和授权。
- JWT 配套边界:保护本机 trust 入口;配置允许的角色、密钥和 audience,并明确 token 时效与 issuer 策略。自建数据库仍需配置 SQL 权限和 RLS,JWT 签名有效本身不代表拥有所有数据权限。
源码参考
blog comments powered by Disqus