news 2026/10/12 3:28:21

Cortex 多租户认证与授权实战:基于 X-Scope-OrgID 的租户隔离方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Cortex 多租户认证与授权实战:基于 X-Scope-OrgID 的租户隔离方案
  • 可观测性
  • 时序数据库
  • 后端
  • 指标监控

【免费下载链接】cortex

A horizontally scalable, highly available, multi-tenant, long term Prometheus.

项目地址:https://gitcode.com/gh_mirrors/cortex6/cortex
点击查看免费下载

本篇技术指南围绕 Cortex 的多租户认证与授权机制展开,核心讲解X-Scope-OrgID请求头的传递方式、-auth.enabled=false单租户模式、可信环境下的remote_write免认证接入,以及如何借助反向代理与 cortex-tenant 代理为 Prometheus 请求注入租户标识。读完本文,你将掌握在真实部署中为 Cortex 正确配置租户隔离、处理写入与查询租户一致性,以及规避常见安全误配的完整方案。

多租户模型:一切组件都信任 X-Scope-OrgID

Cortex 是一个水平可扩展、高可用、多租户的长期 Prometheus 存储系统。在多租户模型下,每一个 Cortex 组件都会从每个请求的X-Scope-OrgID请求头中读取租户 ID(tenant ID,也称为 "user" 或 "org")。一个租户是写入到 Cortex、并从 Cortex 查询的一组时序数据的属主,租户 ID 本质上就是这套时序数据在集群中的命名空间。

这一机制的落地贯穿了从 HTTP 到 gRPC 的整条链路。在 HTTP 层,middleware.AuthenticateUser负责从请求头提取租户 ID 并注入请求上下文(context);在 gRPC 层,则通过middleware.ServerUserHeaderInterceptor与StreamServerUserHeaderInterceptor两个拦截器完成同样的工作(参见 pkg/util/fakeauth/fake_auth.go)。提取出的租户 ID 最终会被各组件用于数据读写、配额限制(overrides)、环形哈希分片(ring sharding)等所有与租户相关的逻辑。

以写入链路为例,Prometheus 通过remote_write将数据推送到 Cortex 的/api/prom/push端点,Distributor 会依据请求上下文中的租户 ID 将时序数据路由并写入对应租户的存储空间;查询链路则相反,Querier 从上下文中取出租户 ID,只读取该租户名下的数据。整个流程对租户 ID 的依赖是全链路、无例外的。

信任边界与安全模型:为什么必须增加额外防护层

需要特别强调的是:Cortex 的所有组件都完全信任X-Scope-OrgID的值,它不会对这个值做真实性校验,也不会反向验证调用方的身份。如果你的 Cortex 实例暴露在不可信网络中,任何能够构造请求的人都可以通过伪造X-Scope-OrgID头,冒充任意租户读写数据。因此,若需要防止意外或恶意的调用,就必须在 Cortex 之外增加一层额外的保护。

典型做法是把 Cortex 部署在反向代理(reverse proxy)之后,并确保所有调用方——无论是通过remote_write接口推送数据的机器,还是通过 GUI 发起查询的人——都必须提供能够标识身份并确认其已获授权的凭据(credentials)。反向代理负责完成认证(Authentication,你是谁)与授权(Authorisation,你能访问哪个租户),校验通过后,再代为注入X-Scope-OrgID头转发给后端的 Cortex 组件。

在配置 Prometheus 的remote_writeAPI 时,可以使用 HTTP Basic Auth 的user与password字段,或 Bearer token 来携带租户 ID 和/或凭据。这是官方推荐的身份传递方式,因为它把"租户标识"与"调用方身份认证"统一到了标准 HTTP 认证机制中,反向代理可以透明地解析并校验这些凭据,再转换为X-Scope-OrgID头。

可信环境下的免认证接入:在 remote_write 中直接设置请求头

如果你运行在可信环境(trusted environment)中——例如集群内部网络、所有写入方都已被信任——可以让 Prometheus 自己发送X-Scope-OrgID头,通过在remote_write配置的headers字段中直接声明即可:

remote_write: - url: http://<cortex>/prometheus/api/v1/push headers: X-Scope-OrgID: <org>

其中<org>替换为你的租户 ID,<cortex>替换为 Cortex 集群的入口地址。这种方式的优点是不需要额外部署代理组件,配置简单直接;代价是安全性完全依赖于网络环境的可信度——只要网络内有任意主机能连到 Cortex,它就可以伪造该头。

从源码角度看,这一配置之所以能生效,是因为 HTTP 层提取租户 ID 的中间件只是机械地读取X-Scope-OrgID头并写入上下文(见 pkg/api/api.go 中HTTPAuthMiddleware对处理器的包装逻辑),它不区分这个头是 Prometheus 自己发的、代理注入的,还是攻击者伪造的。因此"可信环境 + 自带头"和"不可信环境 + 代理校验"两种部署方式,必须在设计安全模型时就明确区分。

关闭多租户:-auth.enabled=false 与 fake 租户

如果不需要多租户功能(例如单租户的私有部署),可以向每一个 Cortex 组件传入-auth.enabled=false参数。此时所有请求的租户 ID 都会被统一设置为字符串fake。

该开关在 pkg/cortex/cortex.go 中定义,默认值为true:

f.BoolVar(&c.AuthEnabled, "auth.enabled", true, "Set to false to disable auth.")

当AuthEnabled为false时,pkg/util/fakeauth/fake_auth.go 中的SetupAuthMiddleware会做三件事:

  1. 为 HTTP 层挂载fakeHTTPAuthMiddleware:该中间件直接把"fake"注入请求上下文,再透传请求(user.InjectOrgID(r.Context(), "fake"));
  2. 为 gRPC 一元调用挂载fakeGRPCAuthUniaryMiddleware:同样注入"fake"租户 ID;
  3. 为 gRPC 流式调用挂载fakeGRPCAuthStreamMiddleware:包裹ServerStream使其上下文携带"fake"。

也就是说,关闭认证后 Cortex 内部的租户解析逻辑依然在运行,只是所有请求都被强行归一到fake这一个租户名下,数据全部写入fake命名空间。这保证了组件间通信代码无需感知认证是否启用,内部依然保持多租户的处理逻辑。

需要留意的是,即使auth.enabled保持开启,也并非所有 gRPC 方法都需要校验租户 ID。在 pkg/cortex/cortex.go 中,有一份显式的豁免名单(noGRPCAuthOn),包括健康检查grpc.health.v1.Health/Check、前端到调度器的长连接schedulerpb.SchedulerForFrontend/FrontendLoop、Querier 与调度器的schedulerpb.SchedulerForQuerier/QuerierLoop等方法。原因是这些调用要么天然不带租户(如健康检查),要么单次长连接会为多个租户服务,无法在握手阶段绑定单一租户 ID。

租户 ID 命名规范:写入前必须先校验的约束

在配置租户 ID 之前,务必了解 Cortex 对租户 ID 命名的三条硬性约束(详见 Tenant ID 命名规范)。虽然租户 ID 对 Cortex 而言是"不透明"的字符串,但命名仍有限制:

1. 支持的字符集

以下字符是安全可用的:

  • 字母与数字:0-9、a-z、A-Z
  • 特殊字符:感叹号!、连字符-、下划线_、单个句点.(但.和..本身无效)、星号*、单引号'、左括号(、右括号)

除此之外的字符都不安全,尤其不支持斜杠/和空白字符(空格)。这一限制的底层实现位于 pkg/util/users/tenant.go 的isSupported函数,它逐 rune 校验租户 ID 中的每个字符;一旦遇到不支持的字符,会返回形如tenant ID 'xxx' contains unsupported character 'y'的错误。

2. 无效租户 ID

以下租户 ID 在 Cortex 中被视为无效并会被拒绝:

  • 当前目录.
  • 父目录..
  • 标记目录__markers__(Cortex 内部用于块存储元数据标记的全局目录名)
  • 用户索引文件user-index.json.gz

这些判断在CheckTenantIDIsSupported中实现(pkg/util/users/tenant.go),目的是防止租户 ID 与对象存储中预留的目录/文件名冲突,杜绝路径穿越类问题。

3. 长度限制

租户 ID 长度不应超过150 字节/字符,超出会返回tenant ID is too long: max 150 characters错误(pkg/util/users/tenant.go)。

上述全部规则都有对应的单元测试覆盖,见 pkg/util/users/tenant_test.go,测试用例包含了 150 字符边界、151 字符超限、./../__markers__/user-index.json.gz无效 ID,以及全量特殊字符组合的合法校验。这些测试可以直接作为你选择租户 ID 时的合法性判据。

写入与查询的租户一致性:最常见的"查不到数据"根因

官方文档明确了一个非常容易踩坑的约束:写入数据时使用的租户 ID,必须与查询时使用的租户 ID 完全一致。

  • 如果两者不匹配,写入的时序数据会落在租户 A 的命名空间,而查询却以租户 B 的身份发起,结果自然是看不到任何数据;
  • 即使匹配,目前的实现中你也无法跨租户查看其他租户的时序数据——多租户隔离在读写路径上都是严格按租户 ID 分隔的。

这一点可以在源码中得到印证:从上下文提取租户 ID 后,Querier、Distributor、Ingester 等组件均以该 ID 作为读写数据的键(例如 pkg/util/users/resolver.go 中SingleResolver.TenantID的解析流程),存储索引、overrides 限制、环形分片等全部按租户隔离。因此,在接入新的写入源时,务必先确认remote_write的认证配置、反向代理注入规则与查询端的租户 ID 三者一致,再排查其他原因。

如果你计划使用多个租户并希望允许跨租户查询,Cortex 提供了"租户联邦"(tenant federation)能力:当-tenant-federation.enabled=true时,请求中可用|分隔多个租户 ID(例如X-Scope-OrgID: tenant-a|tenant-b),底层通过 pkg/util/users/resolver.go 中的MultiResolver解析并归一化(排序 + 去重)租户列表。需要说明的是,启用联邦后对租户 ID 字符集的限制会更严格,|将变为保留分隔符而不能再出现在单个租户 ID 中。这是本文主文档之外、从源码结构推断的进阶能力,具体可参考 Ruler 租户联邦 及相关实现 regex_resolver.go。

Cortex-Tenant 代理:从 Prometheus 标签自动提取租户

当 Prometheus 实例较多、且你不想为每个实例单独配置认证头时,可以采用cortex-tenant代理方案:它是一个能够从 Prometheus 标签(labels)中提取租户 ID 的代理组件,可以放置在 Prometheus 与 Cortex 之间。

其工作流程是:

  1. Prometheus 仍按原有方式将时序数据推送到代理;
  2. 代理在收到的时序数据中查找一个预定义标签;
  3. 将该标签的值作为X-Scope-OrgID头,在将时序数据转发给 Cortex 时注入。

这种方案非常适合在可信环境中运行 Cortex 并按某种标准(例如团队、应用等)将指标划分到不同命名空间的场景:你只需在 Prometheus 的指标上打一个统一的租户标签,代理即可自动完成"标签 → 租户"的映射,无需逐台修改 Prometheus 的remote_write认证配置。

需要特别提醒的是,cortex-tenant 是第三方社区项目,并非由 Cortex 团队维护。在将它引入生产环境前,你需要自行评估其维护活跃度、安全性与功能完整性;同时,由于它只是做标签到请求头的转换,并不承担身份认证职责,它更适合可信环境下的"命名空间划分",而非不可信环境下的"访问控制"。

完整接入检查清单

最后,汇总一份将新数据源接入 Cortex 多租户集群的检查清单,覆盖本文全部要点:

  1. 确定租户 ID:遵循 租户 ID 命名规范,字符集受限、长度 ≤ 150、避开保留名;
  2. 选择接入方式:可信环境可让 Prometheus 在remote_write.headers中直接声明X-Scope-OrgID;不可信环境必须在反向代理处完成认证,再注入该头;或使用 cortex-tenant 从标签自动提取;
  3. 统一读写租户:确认写入认证配置与查询端使用的租户 ID 完全一致,否则查询将看不到数据;
  4. 确认认证开关:单租户部署可对所有组件传-auth.enabled=false(统一使用fake租户);多租户部署保持默认true,并理解 gRPC 豁免名单的边界;
  5. 验证隔离性:确认不存在跨租户可见性(除非显式启用租户联邦),并可在 tenant_test.go 的用例基础上补充自己的租户 ID 合法性自测。

按照以上步骤,你就能在 Cortex 上搭建出符合预期隔离语义的多租户读写链路,同时避免"查不到数据""租户串号"这两类最典型的生产事故。

  • 可观测性
  • 时序数据库
  • 后端
  • 指标监控

【免费下载链接】cortex

A horizontally scalable, highly available, multi-tenant, long term Prometheus.

项目地址:https://gitcode.com/gh_mirrors/cortex6/cortex
点击查看免费下载

相关推荐

上一篇:UniExtract2:500+格式一键提取,你的万能文件解压神器
下一篇:GSD-2 编码 Agent 推倒重来决策指南:四大信号、重新评估协议与低成本重写的架构支撑

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/12 3:27:31

Chainer 实现 DCGAN 完整指南:从 GAN 原理到 CIFAR-10 图像生成

深度学习机器学习 【免费下载链接】chainer A flexible framework of neural networks for deep learning 项目地址&#xff1a; https://gitcode.com/gh_mirrors/ch/chainer 点击查看 免费下载 本教程基于 Chainer 官方仓库中的 DCGAN 示例&#xff08;examples/dcgan 目录&a…

作者头像 李华
网站建设 2026/10/12 3:22:13

声呐阵列信号处理——声呐阵列波束形成(第一章第三节)

一、声呐阵列模型3.接收数据模型&#xff08;1&#xff09;数据组成阵元的实际接收数据是信号、噪声等干扰的叠加&#xff0c;所以接收数据模型建立的前提需是信号模型、噪声模型的构建。对于第m个阵元&#xff0c;其接收数据可以表示为数据中包含期望信号&#xff0c;D个干扰信…

作者头像 李华
网站建设 2026/10/12 3:21:06

展讯平台Camera驱动移植:从MIPI时序到ISP通路实战指南

1. 项目概述&#xff1a;为什么“展讯平台手机camera驱动移植”是嵌入式系统工程师绕不开的硬核课题展讯平台手机camera驱动移植——这八个字背后&#xff0c;不是简单的代码搬运&#xff0c;而是一场横跨硬件抽象层、图像信号处理链路、Linux内核子系统与SoC私有IP核的多线程协…

作者头像 李华
网站建设 2026/10/12 3:20:52

从无状态到有状态:AGENTS.md 与 Memory 工程实战指南

1. 从无状态到有状态&#xff1a;AI 编程范式转换的底层逻辑1.1 为什么传统 AI 编程模式正在失效过去两年&#xff0c;大多数人用 AI 写代码的方式还停留在“对话式问答”&#xff1a;打开一个聊天窗口&#xff0c;把需求描述一遍&#xff0c;AI 吐出一段代码&#xff0c;复制粘…

作者头像 李华