You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript向AWS托管Prometheus创建签名请求遇403错误求助

问题

我正在创建Lambda函数,用于查询并动态部署AWS托管Prometheus(AMP)告警,需要生成签名HTTP请求,但遭遇403错误:

"statusCode": 403, "headers": "{\n "date": "Mon, 24 Jun 2024 12:00:23 GMT",\n "content-type": "application/json",\n "content-length": "1867",\n "connection": "keep-alive",\n "x-amzn-requestid": "7a6bc218-9f69-42ac-b2f9-cfb0b0832acf, 7a6bc218-9f69-42ac-b2f9-cfb0b0832acf",\n "server": "amazon",\n "x-amzn-errortype": "InvalidSignatureException"\n}", "body": "{"message":"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.\n\nThe Canonical String for this request should have been"

原以为配置正确,但目前无法定位问题。请问是否是请求格式有误?有哪些排查思路?

以下是我的代码:

import {SignatureV4} from '@smithy/signature-v4';
import {defaultProvider} from '@aws-sdk/credential-provider-node';
import {HttpRequest} from '@smithy/protocol-http';
import {Sha256} from '@aws-crypto/sha256-browser';


const makeSignedRequest = async (
  path: string,
  region: string
): Promise<any> => {
  const hostname = `aps-workspaces.${region}.amazonaws.com`;
  const request = new HttpRequest({
    method: 'GET',
    protocol: 'https:',
    hostname,
    path,
    headers: {
      'Content-Type': 'application/json',
      Host: hostname, // Explicitly adding the Host header
    },
  });
  
  request.headers.Host = hostname; // Explicitly adding the Host header
  log
    .info()
    .str('initialRequest', JSON.stringify(request, null, 2))
    .msg('Initial request object');
  const signer = new SignatureV4({
    credentials: defaultProvider(),
    region,
    service: 'aps',
    sha256: Sha256,
  });
  const signedRequest = await signer.sign(request);
  log
    .info()
    .str(
      'signedRequest',
      JSON.stringify(
        {
          method: signedRequest.method,
          protocol: signedRequest.protocol,
          hostname: signedRequest.hostname,
          path: signedRequest.path,
          headers: signedRequest.headers,
        },
        null,
        2
      )
    )
    .msg('Signed request object');
  return new Promise((resolve, reject) => {
    const req = https.request(
      {
        hostname: signedRequest.hostname,
        path: signedRequest.path,
        method: signedRequest.method,
        headers: signedRequest.headers,
      },
      res => {
        let data = '';
        res.on('data', chunk => {
          data += chunk;
        });
        res.on('end', () => {
          log
            .info()
            .num('statusCode', res.statusCode || 0)
            .str('headers', JSON.stringify(res.headers, null, 2))
            .str('body', data)
            .msg('Response received');
          try {
            const parsedData = JSON.parse(data);
            resolve(parsedData);
          } catch (error) {
            log
              .error()
              .err(error)
              .str('rawData', data)
              .msg('Failed to parse response data');
            reject(error);
          }
        });
      }
    );
    req.on('error', error => {
      log.error().err(error).msg('Request error');
      reject(error);
    });
    req.end();
  });
};

(注:代码中存在重复定义makeSignedRequest的问题,已移除重复部分)

排查思路与修复建议

一、先修复代码中的明显问题

  1. 删除重复函数定义:原代码重复写了两遍makeSignedRequest,会导致运行时逻辑冲突,直接删除重复部分。
  2. 替换SHA256库:Lambda运行在Node.js环境,应使用@aws-crypto/sha256-js而非浏览器专用的@aws-crypto/sha256-browser,算法库不匹配会直接导致签名计算错误。
  3. 移除冗余Host头设置:创建HttpRequest时已设置Host头,后续重复赋值属于冗余操作,建议删除request.headers.Host = hostname;。
  4. 删除多余的Content-Type头:GET请求无请求体,不需要设置Content-Type,多余的请求头会干扰签名计算。

二、签名核心问题排查

  1. 验证凭证有效性
    • 确认Lambda执行角色拥有AMP操作权限(如aps:ListAlerts、aps:PutAlertManagerConfiguration等),且角色信任关系配置正确。
    • 测试环境下打印获取到的凭证,确认accessKeyId、secretAccessKey、sessionToken(临时凭证)有效且未过期。
  2. 核对签名参数
    • 确认service参数为aps,AMP的服务标识不能写错。
    • 确认region参数与AMP工作区所在区域完全一致,区域不匹配会直接导致签名失败。
  3. 比对Canonical String
    • 利用错误返回中服务端给出的Canonical String,和本地生成的版本逐行比对:
      • HTTP方法是否一致(此处为GET)
      • 请求路径是否完全匹配(注意URL编码是否正确)
      • Host头是否为aps-workspaces.<region>.amazonaws.com
      • 系统时间是否同步:Lambda时间与AWS服务时间差不能超过5分钟,否则签名会失效。
  4. 检查请求头完整性
    • 签名后的Authorization、X-Amz-Date等核心头不能被修改或遗漏,必须完整传递给最终请求。

三、修复后代码示例

// 替换为Node.js环境的SHA256库
import {Sha256} from '@aws-crypto/sha256-js';
import {SignatureV4} from '@smithy/signature-v4';
import {defaultProvider} from '@aws-sdk/credential-provider-node';
import {HttpRequest} from '@smithy/protocol-http';
import https from 'https';

const makeSignedRequest = async (
  path: string,
  region: string
): Promise<any> => {
  const hostname = `aps-workspaces.${region}.amazonaws.com`;
  const request = new HttpRequest({
    method: 'GET',
    protocol: 'https:',
    hostname,
    path,
    headers: {
      Host: hostname,
    },
  });

  log.info().str('initialRequest', JSON.stringify(request, null, 2)).msg('Initial request object');

  const signer = new SignatureV4({
    credentials: defaultProvider(),
    region,
    service: 'aps',
    sha256: Sha256,
  });

  const signedRequest = await signer.sign(request);

  log.info().str('signedRequest', JSON.stringify({
    method: signedRequest.method,
    protocol: signedRequest.protocol,
    hostname: signedRequest.hostname,
    path: signedRequest.path,
    headers: signedRequest.headers,
  }, null, 2)).msg('Signed request object');

  return new Promise((resolve, reject) => {
    const req = https.request(
      {
        hostname: signedRequest.hostname,
        path: signedRequest.path,
        method: signedRequest.method,
        headers: signedRequest.headers,
      },
      res => {
        let data = '';
        res.on('data', chunk => {
          data += chunk;
        });
        res.on('end', () => {
          log
            .info()
            .num('statusCode', res.statusCode || 0)
            .str('headers', JSON.stringify(res.headers, null, 2))
            .str('body', data)
            .msg('Response received');
          try {
            const parsedData = JSON.parse(data);
            resolve(parsedData);
          } catch (error) {
            log
              .error()
              .err(error)
              .str('rawData', data)
              .msg('Failed to parse response data');
            reject(error);
          }
        });
      }
    );
    req.on('error', error => {
      log.error().err(error).msg('Request error');
      reject(error);
    });
    req.end();
  });
};

内容的提问来源于stack exchange,提问作者Willis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 22:43:10