4、租户身份管理:租户注册、认证与元数据注入

在多租户容器运行时环境中,日志隔离的第一道防线并非日志存储或传输,而是租户身份。没有可靠的租户身份,后续所有日志路由、访问控制、审计追溯都将失去锚点。本章聚焦于租户从注册到运行时身份注入的完整链路,确保每条日志记录都能被准确归因到发起租户。

4.1 租户生命周期与身份模型

租户身份管理覆盖三个核心阶段:注册(Onboarding)、认证(Authentication)、元数据注入(Metadata Injection)。下表梳理了每个阶段的关键职责与交付物:

阶段 核心职责 关键产出 安全约束
注册 租户身份预配、密钥生成、策略初始化 Tenant ID、API 、初始 RBAC 绑定 禁止重复注册、防重放攻击
认证 请求方身份验证、 签发与校验 JWT / SPIFFE SVID、短期凭证 时效性、吊销机制
元数据注入 将租户身份写入 Pod 运行时上下文 Downward API 卷、环境变量、Sidecar 代理 不可篡改性、最小权限
设计原则:租户身份必须与 Kubernetes 原生 ServiceAccount 解耦。多租户场景下,一个 ServiceAccount 可能被多个租户复用,因此需要引入 TenantID 作为一级标识,并通过 Admission Webhook 强制绑定。

4.2 租户注册流程

租户注册是身份管理的起点。一个标准化的注册流程应包含以下步骤:

  1. 租户申请:租户管理员通过 API 或 CLI 提交注册请求,包含租户名称、命名空间前缀、配额需求。
  2. 身份预配:控制面生成全局唯一的 TenantID(UUID v4),并创建对应的 Tenant CRD 对象。
  3. 密钥分发:系统生成初始 API 或 SPIFFE 引导凭证,通过安全通道(如 TLS + 加密响应)返回给租户。
  4. 策略初始化:创建默认的日志隔离策略、网络策略、资源配额,并绑定到该租户。
  5. 审计记录:将注册事件写入审计日志,包含操作者、时间戳、分配的 TenantID。
# 租户注册请求示例(简化)
POST /v1/tenants/register
Content-Type: application/json

{
  "tenant_name": "fintech-prod",
  "namespace_prefix": "fin-",
  "quota": {
    "pods": 200,
    "log_retention_days": 90
  }
}

# 响应
{
  "tenant_id": "tnt-2a1b3c4d-5678-90ef-gh12-3456789ijklm",
  "api_": "tnt_sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "initial_policies": ["log-isolation-basic", "network-default-deny"]
}

4.3 认证机制:从 到工作负载身份

容器运行时环境对认证提出了更高要求:不仅需要验证用户或管理面请求,还需要验证工作负载(Pod)的身份。我们采用双层认证模型:

  • 管理面认证:租户管理员通过 API 或 OIDC 登录控制面,用于管理日志策略、查看审计日志。
  • 数据面认证:Pod 内的工作负载通过 SPIFFE 或绑定的 ServiceAccount 向日志代理证明身份。

下表对比了两种认证方式的适用场景与安全属性:

认证类型 凭证形式 生命周期 适用场景 吊销方式
管理面认证 JWT / OAuth2 短期(1h~24h) 租户管理员操作控制面 黑名单 + 刷新
数据面认证 SPIFFE SVID / K8s 与 Pod 生命周期绑定 Pod 内日志采集、Sidecar 通信 Pod 删除自动失效
安全警告:不要将管理面 直接注入到 Pod 环境变量中。Pod 内的 应使用 spiffe://cluster.local/ns/<namespace>/sa/<serviceaccount> 格式的 SPIFFE ID,并通过 CSI 驱动或 Sidecar 代理自动轮换。

4.4 元数据注入:将租户身份嵌入运行时

元数据注入是确保日志记录携带租户身份的关键环节。我们采用三层注入策略,从不同维度将 TenantID 写入 Pod 运行时上下文:

  1. Downward API 卷注入:通过 Kubernetes Downward API 将 metadata.labels['tenant-id'] 以文件形式挂载到 Pod 内,日志采集器直接读取。
  2. 环境变量注入:Admission Webhook 在 Pod 创建时自动注入 TENANT_ID 环境变量,适用于应用层日志框架。
  3. Sidecar 代理注入:对于需要加密或转换日志格式的场景,注入日志 Sidecar 代理,代理负责在日志记录中附加租户身份头。
# Pod 创建时 Webhook 注入后的 YAML 片段
apiVersion: v1
kind: Pod
metadata:
  labels:
    tenant-id: "tnt-2a1b3c4d-5678-90ef-gh12-3456789ijklm"
spec:
  containers:
  - name: app
    env:
    - name: TENANT_ID
      value: "tnt-2a1b3c4d-5678-90ef-gh12-3456789ijklm"
    volumeMounts:
    - name: tenant-identity
      mountPath: /etc/tenant
      readOnly: true
  - name: log-sidecar
    image: log-sidecar:latest
    env:
    - name: TENANT_ID
      valueFrom:
        fieldRef:
          fieldPath: metadata.labels['tenant-id']
  volumes:
  - name: tenant-identity
    downwardAPI:
      items:
      - path: "tenant-id"
        fieldRef:
          fieldPath: metadata.labels['tenant-id']

4.5 身份注入架构总览

下图展示了从租户注册到运行时元数据注入的完整架构,以及各组件之间的身份流转关系:

租户身份管理架构:注册 → 认证 → 元数据注入 1. 租户注册 API Tenant CRD 创建 密钥分发 (/SPIFFE) 2. 管理面认证 (JWT) 数据面认证 (SPIFFE) 校验 & 吊销 3. Downward API 注入 环境变量注入 Sidecar 代理注入 运行时 Pod TENANT_ID 环境变量 | /etc/tenant/tenant-id 文件 | Sidecar 代理 日志输出(携带 TenantID 标签) 说明:注册阶段生成 TenantID 和密钥;认证阶段验证身份并签发短期凭证;注入阶段将身份写入 Pod 运行时上下文,最终日志输出携带租户标签。

4.6 元数据注入的不可篡改性保障

元数据注入面临的最大风险是租户身份伪造。攻击者可能通过修改环境变量或挂载文件来冒充其他租户。我们通过以下机制保障注入数据的可信度:

  • Admission Webhook 强制校验:在 Pod 创建时,Webhook 检查 tenant-id 标签是否与控制面记录一致,不一致则拒绝创建。
  • 只读挂载 + 不可变标签:Downward API 卷以只读方式挂载;Pod 的 tenant-id 标签通过 metadata.immutable 字段锁定,禁止运行时修改。
  • Sidecar 代理签名:日志 Sidecar 在输出日志前,使用租户私钥对日志记录附加数字签名,接收端验证签名确保身份未被篡改。
最佳实践:在大型集群中,建议将租户身份管理功能封装为独立的 Tenant Identity Operator,负责监听 Tenant CRD 变化、自动更新 Webhook 配置、以及同步 SPIFFE 注册条目。这样可以将身份逻辑与日志管道解耦,提升可维护性。

4.7 本章小结