在具有 AWS_IAM 身份验证模式的 ECS 容器 (Fargate) 中使用 AWSAppSyncClient - Returns 403 UnrecognizedClientException
Using AWSAppSyncClient inside an ECS Container (Fargate) with AWS_IAM auth mode - Returns 403 UnrecognizedClientException
我们在 ECS Fargate 容器中有以下代码,但它不断返回错误。
当 运行在具有 IAM 身份验证和正确角色设置的 lambda 中使用相同的代码时,我能够成功 运行 这个。
错误
Network error: Response not successful: Received status code 403
UnrecognizedClientException
The security token included in the request is invalid.
代码
import 'isomorphic-fetch';
import AWSAppSyncClient, { AUTH_TYPE } from 'aws-appsync';
import AWS from 'aws-sdk';
// Setup variables for client
const graphqlEndpoint = process.env.GRAPHQL_ENDPOINT;
const awsRegion = process.env.AWS_DEFAULT_REGION;
const client = new AWSAppSyncClient({
url: graphqlEndpoint,
region: awsRegion,
auth: {
type: AUTH_TYPE.AWS_IAM,
credentials: AWS.config.credentials,
},
disableOffline: true,
})
Cloudformation
TaskDefinition:
Type: "AWS::ECS::TaskDefinition"
Properties:
ContainerDefinitions:
- Ommitted
Cpu: !FindInMap [CpuMap, !Ref Cpu, Cpu]
ExecutionRoleArn: !GetAtt "TaskExecutionRole.Arn"
Family: !Ref "AWS::StackName"
Memory: !FindInMap [MemoryMap, !Ref Memory, Memory]
NetworkMode: awsvpc
RequiresCompatibilities: [FARGATE]
TaskRoleArn: !GetAtt "TaskRole.Arn"
TaskRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: "ecs-tasks.amazonaws.com"
Action: "sts:AssumeRole"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/AWSAppSyncInvokeFullAccess" # Invoke Access for AppSync
我最终发现这是 AppSyncClient 无法正确加载 ECS 中的凭据的结果。
根据 IAM roles in ECS 上的 AWS 文档,凭证的加载方式与其他 AWS 服务不同。 Amazon ECS 代理没有在环境变量中填充凭证,而是使用凭证路径填充 AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
变量。通过首先手动加载 ECS
凭证并将其传递给 AWSAppSyncClient
,我能够成功地 AWSAppSyncClient
在 ECS
容器中使用 IAM
身份验证。以下示例有效。
// AWSAppSyncClient needs to be provided ECS IAM credentials explicitly
const credentials = new AWS.ECSCredentials({
httpOptions: { timeout: 50000 },
maxRetries: 10,
});
AWS.config.credentials = credentials;
// Setup AppSync Config
const AppSyncConfig = {
url: graphqlEndpoint,
region: awsRegion,
auth: {
type: AUTH_TYPE.AWS_IAM,
credentials: AWS.config.credentials,
},
disableOffline: true,
};
我们在 ECS Fargate 容器中有以下代码,但它不断返回错误。 当 运行在具有 IAM 身份验证和正确角色设置的 lambda 中使用相同的代码时,我能够成功 运行 这个。
错误
Network error: Response not successful: Received status code 403
UnrecognizedClientException
The security token included in the request is invalid.
代码
import 'isomorphic-fetch';
import AWSAppSyncClient, { AUTH_TYPE } from 'aws-appsync';
import AWS from 'aws-sdk';
// Setup variables for client
const graphqlEndpoint = process.env.GRAPHQL_ENDPOINT;
const awsRegion = process.env.AWS_DEFAULT_REGION;
const client = new AWSAppSyncClient({
url: graphqlEndpoint,
region: awsRegion,
auth: {
type: AUTH_TYPE.AWS_IAM,
credentials: AWS.config.credentials,
},
disableOffline: true,
})
Cloudformation
TaskDefinition:
Type: "AWS::ECS::TaskDefinition"
Properties:
ContainerDefinitions:
- Ommitted
Cpu: !FindInMap [CpuMap, !Ref Cpu, Cpu]
ExecutionRoleArn: !GetAtt "TaskExecutionRole.Arn"
Family: !Ref "AWS::StackName"
Memory: !FindInMap [MemoryMap, !Ref Memory, Memory]
NetworkMode: awsvpc
RequiresCompatibilities: [FARGATE]
TaskRoleArn: !GetAtt "TaskRole.Arn"
TaskRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: "ecs-tasks.amazonaws.com"
Action: "sts:AssumeRole"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/AWSAppSyncInvokeFullAccess" # Invoke Access for AppSync
我最终发现这是 AppSyncClient 无法正确加载 ECS 中的凭据的结果。
根据 IAM roles in ECS 上的 AWS 文档,凭证的加载方式与其他 AWS 服务不同。 Amazon ECS 代理没有在环境变量中填充凭证,而是使用凭证路径填充 AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
变量。通过首先手动加载 ECS
凭证并将其传递给 AWSAppSyncClient
,我能够成功地 AWSAppSyncClient
在 ECS
容器中使用 IAM
身份验证。以下示例有效。
// AWSAppSyncClient needs to be provided ECS IAM credentials explicitly
const credentials = new AWS.ECSCredentials({
httpOptions: { timeout: 50000 },
maxRetries: 10,
});
AWS.config.credentials = credentials;
// Setup AppSync Config
const AppSyncConfig = {
url: graphqlEndpoint,
region: awsRegion,
auth: {
type: AUTH_TYPE.AWS_IAM,
credentials: AWS.config.credentials,
},
disableOffline: true,
};