如何从 lambda 调用 IAM 授权的 AWS ApiGateway 端点?
Posted
技术标签:
【中文标题】如何从 lambda 调用 IAM 授权的 AWS ApiGateway 端点?【英文标题】:How to invoke IAM authorized AWS ApiGateway endpoint from lambda? 【发布时间】:2021-04-13 23:01:52 【问题描述】:我正在尝试从我已使用 IAM 授权方保护的 lambda 函数调用 AWS ApiGateway HTTP 端点,但是我无法从我的 lambda 函数中获得任何东西来工作。
我已经使用 Postman 测试了端点,当我选择“AWS 签名”作为授权类型并输入我的本地凭证时,可以确认它可以工作,因此端点的设置方式不是问题。这一定是我如何从 Lambda 发送请求的问题。额外的挑战是将标头添加到 GraphQL API 请求中。
这就是我的 lambda 函数的样子:
import ApolloServer from 'apollo-server-lambda';
import APIGatewayProxyEvent, Callback, Context from 'aws-lambda';
import ApolloGateway, RemoteGraphQLDataSource from '@apollo/gateway';
import aws4 from 'aws4';
const userServiceUrl = process.env.USER_SERVICE_URL;
const hostname, pathname = new URL(userServiceUrl);
class AuthenticatedDataSource extends RemoteGraphQLDataSource
willSendRequest(request)
console.log('request is: ', request);
const opts: Record<string, any> =
service: 'execute-api',
region: process.env.AWS_REGION,
host: hostname,
path: pathname,
body: JSON.stringify(query: request.query),
method: 'POST'
aws4.sign(opts);
console.log('opts are: ', opts);
request.http.headers.set('X-Amz-Date', opts.headers['X-Amz-Date']);
request.http.headers.set('Authorization', opts.headers['Authorization']);
request.http.headers.set('X-Amz-Security-Token', opts.headers['X-Amz-Security-Token']);
无论我尝试什么,我总是会收到 403 禁止错误,并且请求永远不会到达授权方背后的实际端点。我尝试删除正文,尝试将本地凭据硬编码到 aws4 调用中,但都不起作用。我的预感是我的签名调用在某种程度上是错误的,但是当我将它与我在互联网上找到的几个示例进行比较时,我看不出任何明显的错误。
任何可以为我指明正确方向的资源将不胜感激。我发现的大多数示例都是特定于前端的,所以我知道这可能会误导我。
【问题讨论】:
【参考方案1】:willSendRequest
函数不是签署请求的最佳位置,因为 apollo-server 可以在调用 willSendRequest
后修改请求对象。
相反,您应该实现自定义提取并将其传递给 RemoteGraphQLDataSource
构造函数,以确保您在发送最终请求之前对其进行签名。
您的自定义 GraphQLDataSource
与自定义提取将是 something 像这样:
import Request, RequestInit, Response, fetch, Headers from "apollo-server-env";
import aws4 from 'aws4';
import RemoteGraphQLDataSource from '@apollo/gateway';
class AuthenticatedDataSource extends RemoteGraphQLDataSource
public constructor(
url: string,
)
super(
url: url,
fetcher: doFetch,
);
async doFetch(
input?: string | Request | undefined,
init?: RequestInit | undefined
): Promise<Response>
const url = new URL(input as string);
const opts: Record<string, any> =
service: 'execute-api',
region: process.env.AWS_REGION,
host: url.hostname,
path: url.pathname,
body: init?.body,
method: init?.method
aws4.sign(opts);
init.headers.set('X-Amz-Date', opts.headers['X-Amz-Date']);
init.headers.set('Authorization', opts.headers['Authorization']);
init.headers.set('X-Amz-Security-Token', opts.headers['X-Amz-Security-Token']);
const response = await fetch(input, init);
return response;
【讨论】:
非常感谢,终于搞定了!将其更改为此后,我能够通过覆盖所有标题而不是我设置的 3 来使其工作。 (请参阅我的答案以了解我所做的更改)【参考方案2】:为了繁荣,这就是我最终所做的并且完美无瑕的工作(非常感谢 Glen Thomas!)
import Request, RequestInit, Response, fetch from "apollo-server-env";
import ApolloServer from 'apollo-server-lambda';
import APIGatewayProxyEvent, Callback, Context from 'aws-lambda';
import ApolloGateway, RemoteGraphQLDataSource from '@apollo/gateway';
import aws4 from 'aws4';
const userServiceUrl = process.env.USER_SERVICE_URL;
async function doFetch(
input?: Request | string,
init?: RequestInit
): Promise<Response>
const urlString = typeof input === 'string' ? input : input.url;
const url = new URL(urlString);
const opts: Record<string, any> =
service: 'execute-api',
region: process.env.AWS_REGION,
host: url.hostname,
path: url.pathname,
body: init?.body,
method: init?.method
aws4.sign(opts);
init.headers = opts.headers;
const response = await fetch(input, init);
return response;
class AuthenticatedDataSource extends RemoteGraphQLDataSource
constructor(url: string)
super(
url,
fetcher: doFetch
)
const server = new ApolloServer(
gateway: new ApolloGateway(
serviceList: [
name: 'users', url: userServiceUrl
],
buildService(url)
return new AuthenticatedDataSource(url)
),
subscriptions: false,
introspection: true,
playground: true,
);
export const handler = (event: APIGatewayProxyEvent, context: Context, callback: Callback) =>
console.log('event is: ', JSON.stringify(event, null, 2))
return server.createHandler(
cors:
origin: '*'
)(event, context, callback);
【讨论】:
对于发现此问题的任何人的另一个提示是,始终使用通配符 IAM 策略进行测试,然后在运行后使其更加严格。我想我可能会因为使用我认为是有效的资源限定符而把事情搞砸了。以上是关于如何从 lambda 调用 IAM 授权的 AWS ApiGateway 端点?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 IAM 在 AWS Lambda 中调用 AppSync?
AWS Lambda:即使在STS:AssumeRole成功之后,lambda函数仍然使用旧的IAM角色
AWS Lambda 无法调用 Cognito Identity - IAM 角色
使用授权方后 AWS Amplify API Gateway cors 错误:aws_iam
即使 IAM 角色具有完整的 Redshift 权限,AWS Lambda 在调用 Redshift 的“CreateCluster”操作时也会出现“拒绝访问”错误