十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

深入解读 OpenObserve 内部 API 公共基础设施:openobserve-api-common 的提取器、通用类型与认证体系

深入解读 OpenObserve 内部 API 公共基础设施:openobserve-api-common 的提取器、通用类型与认证体系 深入解读 OpenObserve 内部 API 公共基础设施openobserve-api-common 的提取器、通用类型与认证体系【免费下载链接】openobserveOpen source observability platform for logs, metrics, traces, RUM, Session replay, pipelines, SLO and LLM observability. A sophisticated, simple and highly performant alternative to Datadog, Splunk, and Elasticsearch with 140x lower storage costs and single binary deployment.项目地址: https://gitcode.com/GitHub_Trending/op/openobserve导读openobserve-api-common是 OpenObserve一个面向日志、指标、链路追踪与 RUM 的开源可观测性平台Rust workspace 中供多个 API 领域 crate 共享的 HTTP 传输层基础设施。它集中提供了三部分能力请求提取器Request Extractors、通用请求/响应类型以及共享的认证与令牌校验逻辑。本文将以该 crate 的 README 为主线结合 Cargo.toml 与 src 下的源码实现讲解它的设计定位、模块结构与核心实现原理并展示它在管理 API、摄取 API 中的真实消费方式帮助读者理解 OpenObserve 多 crate API 架构中公共底座是如何被组织与复用的。一、定位与设计原则一个不依赖任何 API crate 的公共底座根据 README 的定义openobserve-api-common是被多个相互独立的 API 领域 crate 共享的 HTTP 构建块当前提供供 API handler 共享的请求提取器通用的请求与响应类型共享的认证与令牌校验逻辑。它的两条核心约束值得强调不依赖任何 API crateIt does not depend on any API crate这意味着它是一个纯粹的被依赖方所有 API 领域 crate如openobserve-api-management、openobserve-api-ingest、openobserve-api-http都可以反向依赖它而不会产生循环依赖。领域相关的 handler 与模型必须保留在各自的 API crate 中公共层只承载跨领域通用的部分防止公共模块逐渐膨胀成无所不包的上帝模块。从 Cargo.toml 可以确认两点实现细节publish false这是一个内部 workspace crate不会独立发布到 crates.io与 README 中 This is an internal workspace crate and is not published independently 完全对应。通过 Cargo features 对构建形态做区分默认不启用任何 featureenterprisefeature 会引入o2_dex、o2_enterprise、o2_openfga、transform、usage_reporting、vrl等企业级依赖并连带启用openobserve-core/enterprisecloudfeature 则进一步叠加o2_enterprise/cloud与openobserve-core/cloud。这解释了为何源码中大量认证逻辑都包裹在#[cfg(feature enterprise)]/#[cfg(feature cloud)]之下。在依赖清单中除了 axum、serde、serde_json、jsonwebtoken、utoipa 等通用依赖外还通过 workspace 共享了common、config、db、infra、openobserve-core等核心 crate为认证校验中读取用户、组织、令牌等元数据提供了底层支撑。二、模块总览lib.rs 暴露的公共 APIlib.rs 是 crate 的入口它暴露了三个模块pub mod auth; pub mod extractors; pub mod request;同时定义了一个自定义 HTTP 头常量/// Custom header name for O2 Assistant session tracking (UUID v7). pub const X_O2_ASSISTANT_SESSION_ID: axum::http::HeaderName axum::http::HeaderName::from_static(x-o2-assistant-session-id);x-o2-assistant-session-id用于 O2 AssistantLLM 助手会话跟踪承载 UUID v7 格式的会话标识。这个常量以axum::http::HeaderName形式直接暴露便于各 API crate 在构造响应或校验请求时以类型安全的方式引用该头部。三个模块的分工如下模块职责关键内容extractors请求提取器HeadersT包装提取器及对应的拒绝类型request通用请求/响应类型BulkDeleteRequest、BulkDeleteResponseauth认证与令牌校验jwtSSO 令牌处理、tokenDex 会话令牌校验、validator核心校验器三、请求提取器HeadersT 与 OptionalFromRequestPartsextractors.rs 是请求提取器能力的实现。它解决一个非常实际的 axum 开发痛点将 HTTP 请求头直接反序列化成一个强类型结构体而不是在 handler 里逐个调用headers.get()。3.1 核心提取器HeadersT/// Wrapper extractor to deserialize headers into a struct pub struct HeadersT(pub T);它实现了FromRequestPartsS在from_request_parts中调用内部的deserialize_headers::T(headers)将HeaderMap转换为 serde_json Map 后再反序列化为目标类型fn deserialize_headersT: DeserializeOwned(headers: HeaderMap) - ResultT, String { let iter headers.iter().filter_map(|(k, v)| { v.to_str() .ok() .map(|s| (k.as_str().to_string(), serde_json::json!(s))) }); let map serde_json::Map::from_iter(iter); let val serde_json::json!(map); serde_json::from_value(val).map_err(|e| { log::warn!(Header deserialization error: {e}); Invalid request.to_string() }) }要点所有头部值都被当作字符串处理v.to_str()无法转换为合法 UTF-8 的头部会被过滤掉目标结构体通过#[serde(rename ...)]将 Rust 字段名映射到 HTTP 头名例如#[serde(rename x-api-key)] api_key: String反序列化失败时返回HeadersRejection其IntoResponse实现会生成400 Bad Request响应体为{code: 400, message: ...}的标准错误 JSON。3.2 可选提取OptionHeadersT同一类型还实现了OptionalFromRequestPartsS从而允许 handler 声明OptionHeadersT当请求头缺失或无法反序列化时得到None而不是直接返回 400 拒绝。这是实现可选元数据头如可选的身份头、可选的跟踪头的惯用姿势。3.3 测试用例佐证extractors.rs 内置了 5 组单元测试覆盖了关键行为test_deserialize_headers_success验证x-api-key与user-agent能正确映射到结构体字段test_deserialize_headers_missing_field缺少必需字段时报错并返回Invalid requesttest_deserialize_headers_optional_fields字段类型为OptionString时缺失头部得到Nonetest_deserialize_headers_special_characters含连字符的自定义头x-custom-header也能正常解析test_deserialize_headers_multiple_valuesauthorization、content-type、accept等标准头可一次性映射。这些测试可以直接作为如何在 OpenObserve 中定义一个新的头部结构体的参考模板。四、通用请求/响应类型批量删除契约request.rs 目前提供了一组与批量删除相关的通用类型通过utoipa::ToSchema派生可自动生成 OpenAPI/Swagger 文档#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)] pub struct BulkDeleteRequest { pub ids: VecString, } #[derive(Default, Serialize, ToSchema)] pub struct BulkDeleteResponse { pub successful: VecString, pub unsuccessful: VecString, pub err: OptionString, }BulkDeleteRequest携带要删除的资源 ID 列表BulkDeleteResponse分别返回删除成功的 ID、失败的 ID以及可选的全局错误信息errBulkDeleteResponse实现了Default便于在部分失败场景下先构造空响应再逐步填充。对应的三个测试test_bulk_delete_response_default、test_bulk_delete_request_roundtrip、test_bulk_delete_response_serializes验证了默认值、JSON 序列化/反序列化往返以及成功/失败列表与错误信息的输出格式构成一个完整的契约测试闭环。五、认证与令牌校验体系validator / token / jwt 三位一体认证是openobserve-api-common最厚重的部分。auth/mod.rs 将认证拆成三个子模块validator核心校验器处理用户名/密码、静态令牌、摄取令牌、AWS/GCP 网关认证等token面向 Dex 签发的 JWT 会话令牌SSO 登录后的 UI 会话流的校验器jwt处理 SSO 令牌解码后的用户预置provisioning逻辑包括 LDAP DN 解析、组到角色映射、自定义 claim 解析等。5.1 基础类型RequestData、AuthError、AuthValidationResultvalidator.rs 定义了三个贯穿整个认证体系的基础类型RequestData—— 一个可安全跨 await 点传递的请求快照#[derive(Clone)] pub struct RequestData { pub uri: Uri, pub method: Method, pub headers: HeaderMap, }它的注释明确说明了设计动机所有字段都是Send Sync因此它可以被克隆后安全地传给异步校验函数避免在异步上下文中借用axum的Parts。AuthError—— 认证错误枚举包含Unauthorized、Forbidden、NotFound三种变体其IntoResponse实现值得注意对于 401 响应若 Dex 已启用会生成WWW-Authenticate: Bearer as_uridex_url头引导客户端前往 Dex 登录端点否则回退为Bearer realmopenobserve。AuthValidationResult—— 校验成功后的结果pub struct AuthValidationResult { pub user_email: String, pub user_role: OptionUserRole, pub is_internal_user: bool, }user_role为Option的原因是部分特殊端点如邀请列表、成员订阅允许用户尚未加入任何组织的中间状态。5.2 核心入口validator多凭证类型的分流validator函数是普通 API 请求的认证主入口它根据凭证形态走两条分支match if auth_info.auth.starts_with({\auth_ext\:) { // auth_ext 形态外部扩展认证passcode 摄取等 validate_credentials_ext(user_id, password, path, auth_token, method).await } else { // 普通形态用户名/密码、静态令牌等 validate_credentials(user_id, password.trim(), path, req_data.method, from_session).await }校验通过后还会做一次组织检查check_and_create_org并最终调用openobserve_core::authz::check_permissions做 OpenFGA 级别的权限判定除非auth_info.bypass_check为真。5.3validate_credentials一条函数内的多级令牌分类validate_credentials是理解整个认证策略的最佳入口它按优先级依次处理多种令牌类型Synthetics 探测令牌o2syn_前缀当路径匹配/{org}/synthetics/{jobs,agent}/*且密码以o2syn_开头时只在synthetics_probe_tokens表中查找返回一个user_role: None的合成探测身份组织级摄取令牌o2oi_前缀先在内存缓存ORG_INGESTION_TOKENS中查询{org_id}/{token}未命中再查数据库的org_ingestion_tokens表并回填缓存服务账号静态令牌若用户角色是服务账号且user.token password则校验service_account_enabled配置与allow_static_token策略allow_static_token false时禁止直接使用静态令牌必须通过assume_service_accountAPI 换取临时会话普通用户的摄取专用令牌仅当路径被ingestion_routes::is_ingestion_allowed(method, path)判定为真实摄取路径时用户的静态令牌才有效密码认证走get_hash(user_password, user.salt)与库中哈希比对含password_ext外部密码的兼容分支。值得特别指出的是代码注释中引用的安全公告GHSA-wffq-g8qf-ccmv早期实现只要路径中包含摄取关键字就接受摄取令牌导致GET /{org}/{stream}/traces/latest这类数据读取路由也可以被摄取令牌访问造成数据泄露。现在的实现改为通过权威的摄取路由表ingestion_routes::is_ingestion_allowed(method, path)按方法 精确路径形状分类只有真正的写入与 ES 只读握手桩才被接受。这是认证边界必须精确到方法与路由而不能靠关键字猜测的教科书案例。此外validate_credentials还包含一个安全细节空密码的摄取请求永远无效is_ingestion_path user_password.is_empty()从而阻断匿名摄取。5.4 专用校验器RUM 令牌、AWS Firehose 与 GCPvalidate_token(token, org_id)仅凭令牌校验的端点如 RUM使用通过users::get_user_by_token查找用户找不到则返回 403validator_aws解析X-Amz-Firehose-Access-Key头base64 解码后按user:password分割再复用validate_credentialsvalidator_gcp从 query string 中取API-Key参数同样 base64 解码后复用validate_credentials。这两个网关校验器充分体现了公共层的价值AWS/GCP 的凭证形态差异被隔离在凭证提取阶段核心的校验策略完全复用。5.5 Dex 会话令牌校验器token_validatorenterprisetoken.rs 中的token_validator面向Dex 签发的 JWT 会话令牌对应 UI 的access_token: session …cookie 流通过get_dex_jwks()获取 Dex 的 JWKS用jwt::verify_decode_token验证签名、client_id 与 audiencelogin_flow参数会针对 MCP 请求关闭 audience 校验校验通过后按路径解析出目标 org查找该 org 下的用户并执行 OpenFGA 权限检查对member_subscription、invitesGET 列表 / DELETE 拒绝、organizations/clusters等路径级预置豁免场景做了特殊处理——这些场景下用户尚未加入任何组织是合法的handler 会以 email 维度二次验证身份。该模块最引人注目的是may_skip_permission_check及其配套测试。函数注释明确指出用户不在目标组织时走None分支会完全跳过权限检查因此跳过权限检查本质上是授权绕过authorization bypass必须被严格限制在路径限定、自我校验的预置场景内。与之配套的三个测试承担安全回归护栏职责path_scoped_exemptions_are_intentional确认四种豁免场景确实被允许ordinary_request_for_nonmember_is_not_exempt普通路由上非成员用户不得豁免mcp_flag_is_not_a_parameter_of_the_permission_skip_decision这是关键的安全回归测试——MCP 请求不能因为带了x-o2-mcp: true头或走/api/mcp端点就获得豁免。测试注释详细记录了历史漏洞早期实现把allow_nonexistent_user is_mcp_requestOR 进了豁免决策导致任何合法的 Dex 身份即使未预置或属于其他组织都可以通过任意路由上的x-o2-mcp: true头访问任意组织数据。合法 MCP 调用方服务账号 Basic 认证或已在目标组织内预置的 SSO 用户本就走不到这个分支因此移除 MCP 豁免只会拒绝跨组织/未预置访问。5.6 SSO 令牌后处理process_token 与用户预置enterprisejwt.rs 中的process_token处理 Dex/SSO 令牌解码之后的一系列副作用核心目标是将外部身份SSO 用户预置到 OpenObserve 的用户体系中。流程要点首先检查系统级域名管理黑名单domain_management::evaluate_cached被拒绝的外部身份在任何创建/更新动作之前就提前拦截防止被封禁的 SSO 主体下次登录时复活从 JWT claims 中提取name缺失时回退为 email在非云形态下从 Dex 配置的group_claim中读取用户组通过parse_dn解析 LDAP DN如roleadmin,orgtestorg,cnuser提取org与role若非 LDAP 格式如 GitHub/OAuth 的简单字符串则整个字符串作为组织名若map_group_to_role开启则组名被当作自定义角色名对已存在的用户逐项对比源组织与数据库中的组织计算orgs_added/orgs_removed/orgs_role_changed分别执行加入、移除、角色更新并同步写 OpenFGA 关系元组tuple对不存在的用户创建DBUseris_external: true、空密码并在 OpenFGA 中写入用户-组织元组服务账号UserRole::ServiceAccount在登录时跳过角色更新。另一个值得注意的机制是自定义 claim 解析custom claim parsing当openfga_cfg.custom_claim_parsing_enabled开启时process_custom_claim_parsing会从_meta组织加载用户自定义的claim_parser函数支持VRL与JavaScript两种实现分别通过transform::compile_vrl_function/transform::js::compile_js_function编译执行把 SSO claims 映射为(org, role)分配列表并支持把组织不存在等错误发布到 errors 流usage_reporting::publish_error。在云形态feature cloud下check_and_add_to_org实现了另一套自注册流程校验邮箱域名是否被屏蔽、为新用户创建默认组织、发送注册追踪事件等。六、消费方验证公共底座如何被各 API crate 使用通过搜索api_common::(auth|extractors|request)可以确认这个公共 crate 的实际消费面例如管理 APIsrc/api/management/src/request/alerts/destinations.rs、templates.rs、incidents.rs、slack_oauth.rs等告警相关模块大量复用其认证与提取器摄取 APIsrc/api/ingest/src/request/logs/ingest.rs、metrics/ingest.rs、rum/ingest.rs等摄取入口复用其认证逻辑HTTP APIsrc/api/http/src/handler/http/mod.rs限流资源提取src/api/http/src/router/ratelimit/resource_extractor.rs。这验证了 README 中被多个独立 API 领域 crate 共享的定位告警、摄取、HTTP 等差异极大的领域在请求头反序列化与凭证校验这两个横切关注点上全部收敛到同一个公共实现避免了认证逻辑的多份拷贝与漂移。七、总结与工程启示openobserve-api-common是一个典型的横切关注点下沉案例其工程价值可以归纳为四点依赖方向清晰公共层不依赖任何 API crate所有 API crate 单向依赖它从结构上杜绝循环依赖关注点收敛请求头反序列化HeadersT、通用批量删除契约、以及从普通密码到组织摄取令牌、服务账号、Synthetics 探测令牌、Dex 会话 JWT、AWS/GCP 网关凭证的全谱系认证策略都集中在同一处实现安全回归可测试无论是 GHSA-wffq-g8qf-ccmv 引出的摄取路由白名单还是 MCP 豁免移除后的安全护栏测试都把防止认证绕过落实为可运行的单元测试而不是停留在代码审查层面构建形态可裁剪通过enterprise/cloudCargo features 精确控制认证能力的编译范围OSS 构建不携带企业级依赖。对于希望深入 OpenObserve 源码或仿照其架构搭建多 crate Rust 服务的开发者src/api/common是一份高质量参考先看 README 理解边界再按extractors→request→auth/validator→auth/token→auth/jwt的顺序阅读源码即可完整掌握 OpenObserve API 层的公共骨架。【免费下载链接】openobserveOpen source observability platform for logs, metrics, traces, RUM, Session replay, pipelines, SLO and LLM observability. A sophisticated, simple and highly performant alternative to Datadog, Splunk, and Elasticsearch with 140x lower storage costs and single binary deployment.项目地址: https://gitcode.com/GitHub_Trending/op/openobserve创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表