Grafana Tempo 中的 AWS SigV4 签名深入解析 prometheus/sigv4 RoundTripper 模块与 remote_write 集成【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo本文以 Grafana Tempo 仓库内 vendor 的 prometheus/sigv4 README 及其源码为线索讲解该模块如何实现 AWS Signature Version 4SigV4请求签名以及它在 Tempo metrics-generator 远程写入remote_write到 AWS 托管 Prometheus 服务时的实际应用。读完本文你将掌握 sigv4 模块的核心 API、SigV4Config全部配置字段与校验规则、签名请求的底层流程并能在 Tempo 配置文件中正确启用 SigV4 认证。一、sigv4 模块是什么github.com/prometheus/sigv4是一个独立的 Go 模块它在 README 中自我定位非常清晰sigv4 provides a http.RoundTripper that will sign requests using Amazons Signature Verification V4 signing procedure, using credentials from the default AWS credential chain.即它提供一个实现了http.RoundTripper接口的组件用于按照 AWS SigV4 签名流程为 HTTP 请求签名签名所需凭证来自 AWS 默认凭证链。它不关心请求要发给谁只负责在请求发出前为它加上合法的 SigV4 认证信息。该模块被设计成独立于github.com/prometheus/common发布README 明确说明了原因This is a separate module from github.com/prometheus/common to prevent it from having and propagating a dependency on the AWS SDK.即独立成模块是为了避免 Prometheus 公共库被迫引入并向外传播 AWS SDK 依赖只有真正需要 SigV4 签名的项目或模块才引入它。同时 README 也提醒This module is considered internal to Prometheus, without any stability guarantees for external usage.即该模块在 Prometheus 生态中被视为内部模块对外部使用不提供稳定性保证——升级时 API 可能变动生产使用需锁定版本。在 Tempo 仓库中该模块以 v0.4.1 版本被 vendor 进 vendor/github.com/prometheus/sigv4 目录并在 go.mod 中声明为间接依赖// indirect由 Tempo 依赖的 Prometheus 配置库传递引入。二、核心 APINewSigV4RoundTripper模块对外暴露的入口是一个构造函数定义在 vendor/github.com/prometheus/sigv4/sigv4.go// NewSigV4RoundTripper returns a new http.RoundTripper that will sign requests // using Amazons Signature Verification V4 signing procedure. The request will // then be handed off to the next RoundTripper provided by next. If next is nil, // http.DefaultTransport will be used. // // Credentials for signing are retrieved using the the default AWS credential // chain. If credentials cannot be found, an error will be returned. func NewSigV4RoundTripper(cfg *SigV4Config, next http.RoundTripper) (http.RoundTripper, error)两个参数的含义cfg *SigV4Config签名配置见下文第三节。所有字段均可留空空值会回退到 AWS 默认凭证链解析。next http.RoundTripper签名完成后的下一跳传输器传nil时使用http.DefaultTransport。构造函数内部做了四件关键的事均可在 sigv4.go 源码中逐一印证装配 AWS SDK 加载选项根据配置动态追加config.WithCredentialsProvider静态 AccessKey/SecretKey、config.WithUseFIPSEndpointFIPS 端点开关、config.WithRegion、config.WithSharedConfigProfile。加载并预检凭证调用config.LoadDefaultConfig后立即执行awscfg.Credentials.Retrieve(ctx)若默认凭证链上找不到任何凭证构造函数直接返回错误could not get SigV4 credentials若最终 region 为空同样返回错误region not configured in sigv4 or in default credentials chain。这是失败要快的设计——签名器不会在请求发出时才报凭证缺失。支持 STS AssumeRole当RoleARN非空时用stscreds.NewAssumeRoleProvider包装凭证提供者并可附带ExternalID实现跨账号/角色切换签名身份。确定签名服务名默认serviceName aps即 Amazon Prometheus Service可通过配置覆盖。返回的sigV4RoundTripper结构体包含 region、下一跳 transport、sync.Pool字节缓冲池、*aws.CredentialsCache凭证缓存和*signer.Signer签名器其中凭证缓存aws.NewCredentialsCache配置了30 秒过期窗口和 0.5 抖动系数见 credentialCacheOptions避免大量并发请求在凭证即将过期时同时刷新。三、SigV4Config配置字段与校验规则SigV4Config定义在 vendor/github.com/prometheus/sigv4/sigv4_config.go全部字段带 YAML tag可直接嵌入 Prometheus 风格的 YAML 配置YAML 字段Go 类型说明regionstringAWS 区域为空时从默认凭证链解析access_keystring静态 Access Key与secret_key必须成对出现secret_keyconfig.Secret静态 Secret Key类型为config.Secret安全字符串打印时会被遮蔽profilestringAWS 共享配置文件~/.aws/credentials等中的 profile 名role_arnstring要扮演的 IAM 角色 ARN启用 STS AssumeRoleexternal_idstringAssumeRole 的 External ID只能与role_arn配合使用use_fips_sts_endpointbool是否启用 STS 的 FIPS 端点service_namestringSigV4 签名服务名默认apsAmazon Prometheus Service所有字段均带omitempty即**留空即走 AWS 默认凭证链**——这是该模块的核心设计哲学默认情况下凭证、区域都会按 AWS 标准顺序从环境变量、共享凭证文件、ECS/EC2 元数据等默认凭证链中获取。SigV4Config实现了UnmarshalYAML在反序列化后立即调用Validate()sigv4_config.go强制两条配置规则func (c *SigV4Config) Validate() error { if (c.AccessKey ) ! (c.SecretKey ) { return fmt.Errorf(must provide a AWS SigV4 Access key and Secret Key if credentials are specified in the SigV4 config) } if c.ExternalID ! c.RoleARN { return fmt.Errorf(external_id can only be used with role_arn) } return nil }即AccessKey 与 SecretKey 必须同时提供或同时留空ExternalID 只有在配置了 RoleARN 时才有意义否则配置校验直接失败。这意味着即使配置写错错误也会在配置加载阶段暴露而不是在运行时签名失败才暴露。四、签名流程RoundTrip 内部实现签名在sigV4RoundTripper.RoundTrip中完成见 sigv4.go。完整流程如下复用缓冲池从sync.Pool取一个bytes.Buffer初始容量 1KB见 newBufdefer保证归还减少高并发下 GC 压力。计算请求体哈希若req.Body非空把 body 整体拷贝进缓冲池关闭原 body用sha256.Sum256计算请求体哈希并 hex 编码空 body 则使用预置的空串 SHA256 常量e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855。SigV4 规范要求签名必须覆盖请求体哈希这就是其中的X-Amz-Content-Sha256值的来源。规范化 URL 路径按 AWS 文档要求执行req.URL.Path path.Clean(req.URL.Path)消除路径中的.、..冗余保证 canonical request 与 AWS 侧计算一致。克隆请求并剔除 denylist 头req.Clone(req.Context())后删除签名黑名单头。当前黑名单为sigv4HeaderDenylistvar sigv4HeaderDenylist []string{ uber-trace-id, }原因是uber-trace-id这类链路追踪头每跳都会变化若参与签名会导致签名校验失败签名完成后再把原始请求中的该头恢复回去见 sigv4.go——即签名时不带发送时带上。获取凭证并签名从凭证缓存rt.creds.Retrieve(ctx)获取当前有效凭证调用 AWS SDK 的signer.SignHTTP(ctx, creds, signReq, strHash, serviceName, region, time.Now().UTC())为克隆出的请求写入Authorization、X-Amz-Date、X-Amz-Content-Sha256、X-Amz-Security-Token临时凭证时等头。交给下一跳发送最终return rt.next.RoundTrip(signReq)把带签名的请求交给原始 transport 发出。需要注意一个实现细节签名发生在请求体被缓冲读取之后因此RoundTrip期间 body 是可重读的bytes.Reader同时签名目标与发送目标都是同一个克隆后的请求对象原始req的 header 用于恢复黑名单头。这套实现与 Go 标准http.Client的中间件链可以无缝组合。五、在 Grafana Tempo 中的实际应用metrics-generator remote_writesigv4 模块在 Tempo 中并非直接调用而是通过Prometheus remote_write 配置体系间接生效这条链路可以在源码中完整追踪Tempo 的 metrics-generator 存储配置Config.RemoteWrite使用[]prometheus_config.RemoteWriteConfig类型见 modules/generator/storage/config.go// Prometheus remote write config // https://prometheus.io/docs/prometheus/latest/configuration/configuration/#remote_write RemoteWrite []prometheus_config.RemoteWriteConfig yaml:remote_write,omitempty该类型来自 vendor 的 Prometheus 配置库其结构体 RemoteWriteConfig 中内嵌了认证配置SigV4Config *sigv4.SigV4Config yaml:sigv4,omitempty AzureADConfig *azuread.AzureADConfig yaml:azuread,omitempty GoogleIAMConfig *googleiam.Config yaml:google_iam,omitempty因此Tempo 的remote_write条目下可以直接写sigv4:块。配置校验逻辑config.go强制签名认证方式互斥basic_auth、authorization、oauth2、sigv4、azuread、google_iam六种方式最多配置一种否则报错。以 Tempo metrics-generator 向 AWS 托管 PrometheusAPS远程写入为例最小可用配置形如metrics_generator: storage: remote_write: - url: https://aps-workspaces.us-east-1.amazonaws.com/workspaces/workspace-id/api/v1/remote_write sigv4: region: us-east-1 service_name: aps queue_config: max_samples_per_send: 1000region也可以省略交由默认凭证链解析service_name默认就是aps通常也无需显式写出。若 Tempo 运行在 EC2/ECS 上可完全省略access_key/secret_key通过实例 IAM 角色自动获取凭证若在本地开发可通过AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY环境变量或~/.aws/credentials提供。需要跨账号写入时配置role_arn可选加external_id模块会走 STS AssumeRole。需要 FIPS 合规环境时设置use_fips_sts_endpoint: true。注意与remote_write相关联Tempo 还默认开启remote_write_add_org_id_header默认值为true见 modules/generator/storage/config.go多租户场景下会为每个租户注入X-Scope-OrgID头这一点在对接 AWS 托管 Prometheus 的租户隔离/权限映射时也值得留意。六、使用注意事项与边界结合源码实现有几个实践要点值得记录凭证错误尽早暴露NewSigV4RoundTripper在构造时就会尝试拉取凭证并校验 region所以凭证链未配置会在启动阶段立刻报错而不是等到第一次写请求才失败sigv4.go。请求体会被整体读入内存为了计算 SigV4 要求的 body 哈希RoundTrip会把整个 body 缓冲到内存中。对超大 payload 的接口需要评估内存开销remote_write 场景下 body 本身就是批量样本符合该模块的设计预期。URL 路径会被path.Clean规范化/a/../b这类路径在签名前会被清洗如果你的端点依赖未被清洗的原始路径需要自行确认兼容性。链路追踪头被排除在签名外uber-trace-id不会参与签名计算避免链路上下文头导致签名不一致发送时该头仍会原样保留。模块稳定性README 明确 sigv4 是 Prometheus 内部模块、外部使用无稳定性保证升级 Tempo 或 Prometheus 库版本时若涉及该模块建议关注其行为变更。配置互斥一个 remote_write 目标只能使用一种认证方式sigv4与basic_auth/oauth2等互斥错误配置会在配置校验阶段被拒绝config.go。七、小结github.com/prometheus/sigv4虽然是一个体量很小的模块两个 Go 源文件 一个 README但它精确地解决了给 HTTP 请求加上 AWS SigV4 签名这一横切问题以标准http.RoundTripper形式提供中间件能力、默认接入 AWS 默认凭证链、内置 STS AssumeRole 与 FIPS 支持、在构造期做凭证预检、并通过SigV4Config的 YAML 校验兜底配置错误。在 Grafana Tempo 中它经由 Prometheus remote_write 配置体系成为 metrics-generator 向 AWS 托管 Prometheus 安全推送指标的关键一环——理解它的配置字段与签名流程即可在生产环境中正确、安全地启用 AWS 认证的远程写入链路。【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考