允许 VPC 中的 Lambda 访问同一 VPC 中的 Elasticsearch 域
Posted
技术标签:
【中文标题】允许 VPC 中的 Lambda 访问同一 VPC 中的 Elasticsearch 域【英文标题】:Allowing Lambda in a VPC to access an Elasticsearch domain in the same VPC 【发布时间】:2019-05-23 11:33:57 【问题描述】:我正在学习如何使用 Amazon 服务,特别是我目前想使用 Cloud Formation 脚本创建一个简单的设置:一个 VPC,它有一个用 JS 编写的 lambda,它可以访问同一个 Elasticsearch 服务VPC。
不知怎的,我无法让它工作。从 lambda 到 Elasticsearch 域的所有请求总是超时。但是,来自在同一 VPC 中运行 Amazon Linux 2 的 EC2 实例从相同的 JS 代码或 curl(即使没有任何额外的授权,只是卷曲 ES 域端点)发出的相同请求工作正常,我可以与 Elasticsearch 通信就好了从那个 EC2 实例(通过 SSH 连接到它)。
同时 lambda 能够访问 VPC 中的 Aurora 集群,因此 lambda 无法访问 VPC 资源不是一般问题。
请告诉我我在 Cloud Formation 的设置描述中做错了什么?下面是我的 Cloud Formation 模板的相关摘录以及能够从 EC2 实例访问 ES 服务但不能对 lambda 执行相同操作的 JS 代码示例:
AWSTemplateFormatVersion: 2010-09-09
Description: The AWS CloudFormation tutorial
Resources:
SomeDeploymentBucket:
Type: 'AWS::S3::Bucket'
AppLogGroup:
Type: 'AWS::Logs::LogGroup'
Properties:
LogGroupName: /aws/lambda/some-lambda
# ========= The Lambda Execution Role =========
IamRoleLambdaExecution:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- 'sts:AssumeRole'
Policies:
- PolicyName: !Join
- '-'
- - dev
- some-app
- lambda
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- 's3:*'
- 'rds-db:connect'
- 'rds:*'
- 'es:*'
Resource: '*'
Path: /
RoleName: !Join
- '-'
- - some-app
- dev
- eu-west-1
- lambdaRole
ManagedPolicyArns:
- !Join
- ''
- - 'arn:'
- !Ref 'AWS::Partition'
- ':iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole'
# ========= The Lambda =========
AppLambdaFunction:
Type: 'AWS::Lambda::Function'
Properties:
Code:
S3Bucket: !Ref SomeDeploymentBucket
S3Key: >-
tutorial/some-app/dev/1545610972669-2018-12-24T00:22:52.669Z/some-app.zip
FunctionName: some-lambda
Handler: app.server
MemorySize: 1024
Role: !GetAtt
- IamRoleLambdaExecution
- Arn
Runtime: nodejs8.10
Timeout: 6
VpcConfig:
SecurityGroupIds:
- !Ref xxxVPCSecurityGroup
SubnetIds:
- !Ref xxxLambdaSubnet
DependsOn:
- AppLogGroup
- IamRoleLambdaExecution
# ========= VPC =========
xxxVPC:
Type: 'AWS::EC2::VPC'
Properties:
CidrBlock: 172.31.0.0/16
InstanceTenancy: default
EnableDnsSupport: 'true'
EnableDnsHostnames: 'true'
xxxVPCSecurityGroup:
Type: 'AWS::EC2::SecurityGroup'
Properties:
GroupName: VPC SG
GroupDescription: VPC Security Group
VpcId: !Ref xxxVPC
xxxLambdaSubnet:
Type: 'AWS::EC2::Subnet'
Properties:
VpcId: !Ref xxxVPC
CidrBlock: 172.31.32.0/20
# ========= Elasticsearch =========
xxxESSecurityGroup:
Type: 'AWS::EC2::SecurityGroup'
Properties:
GroupName: ES SG
GroupDescription: ES Security group
VpcId: !Ref xxxVPC
SecurityGroupIngress:
- IpProtocol: -1
FromPort: 0
ToPort: 65535
SourceSecurityGroupId: !Ref xxxVPCSecurityGroup
xxxElasticSearch:
Type: 'AWS::Elasticsearch::Domain'
Properties:
AccessPolicies:
Version: 2012-10-17
Statement:
- Action:
- 'es:*'
- 'ec2:*'
- 's3:*'
Principal:
AWS:
- '*'
Resource: '*'
Effect: Allow
DomainName: es-xxx-domain
AdvancedOptions:
rest.action.multi.allow_explicit_index: 'true'
ElasticsearchVersion: 6.3
ElasticsearchClusterConfig:
InstanceCount: 2
InstanceType: m3.medium.elasticsearch
DedicatedMasterEnabled: 'false'
VPCOptions:
SecurityGroupIds:
- !Ref xxxESSecurityGroup
SubnetIds:
- !Ref xxxLambdaSubnet
JS代码(没有用creds签名的版本,但签名时也不起作用):
var es = require('elasticsearch');
var client = new es.Client(
host: 'vpc-es-domain-AMAZON.eu-west-1.es.amazonaws.com:80',
log: 'trace'
);
client.ping(
requestTimeout: 1000
, function(error, res, status)
if(error)
console.trace('es cluster error!');
console.trace(error);
else
console.log('All is well');
var response =
error: error,
res: res,
status: status
console.log(JSON.stringify(response));
);
在同一 VPC 中的 EC2 实例中执行此操作会从 ES 域获得响应没有任何问题:
curl vpc-es-domain-AMAZON.eu-west-1.es.amazonaws.com:80
非常感谢您的帮助,因为我已经遇到了这个问题。
【问题讨论】:
Lambda 函数和 ES 域是否在 VPC 内的同一子网上?如果它们在不同的子网中,请确保适当的路由表具有彼此的路由。 @ben5556 是的,lambda 和 ES 在同一个子网中,即示例中的xxxLambdaSubnet
。我已经改进了代码以更简洁地显示这一点。
您是在运行自管理的elasticsearch 集群还是AWS elasticsearch 服务集群?
@RenéGonzálezVenegas 这是一个与同一 VPC 关联的 AWS 弹性搜索服务集群。您可以在我的问题中提供的第一个代码示例中看到它的配置(将示例向下滚动到它的最底部,在那里您可以看到配置的 elasticsearch 部分)
【参考方案1】:
在您的设置中发现了两个问题
-
您在堆栈中仅创建一个子网,并且仅将一个子网分配给 Lambda。您需要为 Lambda 分配多个子网
You need to fix access policy for ES。我手动更新为“不需要使用 IAM 凭据签署请求”,以便在控制台中进行测试。
我更新了 Cloudformation 模板以创建基于 python 的 lambda 处理程序,以从同一 vpc 查询弹性搜索。它不完整,但如果您确定上述问题,那么它应该可以工作。
AWSTemplateFormatVersion: 2010-09-09
Description: The AWS CloudFormation tutorial
Resources:
SomeDeploymentBucket:
Type: 'AWS::S3::Bucket'
AppLogGroup:
Type: 'AWS::Logs::LogGroup'
Properties:
LogGroupName: /aws/lambda/some-lambda
# ========= The Lambda Execution Role =========
IamRoleLambdaExecution:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- 'sts:AssumeRole'
Policies:
- PolicyName: !Join
- '-'
- - dev
- some-app
- lambda
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- 's3:*'
- 'rds-db:connect'
- 'rds:*'
- 'es:*'
Resource: '*'
Path: /
RoleName: !Join
- '-'
- - some-app
- dev
- eu-west-1
- lambdaRole
ManagedPolicyArns:
- !Join
- ''
- - 'arn:'
- !Ref 'AWS::Partition'
- ':iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole'
# ========= The Lambda =========
AppLambdaFunction:
Type: AWS::Lambda::Function
DependsOn:
- AppLogGroup
- IamRoleLambdaExecution
Properties:
FunctionName: some-lambda
Handler: index.lambda_handler
Runtime: python2.7
Timeout: 60
MemorySize: 1024
Role: !GetAtt
- IamRoleLambdaExecution
- Arn
VpcConfig:
SecurityGroupIds:
- !Ref xxxVPCSecurityGroup
SubnetIds:
- !Ref xxxLambdaSubnet1
- !Ref xxxLambdaSubnet2
Code:
ZipFile: !Sub |
from __future__ import print_function
import boto3
iam = boto3.client('iam')
def lambda_handler(event, context):
print('called lambda_handler')
# ========= VPC =========
xxxVPC:
Type: 'AWS::EC2::VPC'
Properties:
CidrBlock: 172.31.0.0/16
InstanceTenancy: default
EnableDnsSupport: 'true'
EnableDnsHostnames: 'true'
xxxVPCSecurityGroup:
Type: 'AWS::EC2::SecurityGroup'
Properties:
GroupName: VPC SG
GroupDescription: VPC Security Group
VpcId: !Ref xxxVPC
xxxLambdaSubnet1:
Type: 'AWS::EC2::Subnet'
Properties:
VpcId: !Ref xxxVPC
CidrBlock: 172.31.32.0/20
xxxLambdaSubnet2:
Type: 'AWS::EC2::Subnet'
Properties:
VpcId: !Ref xxxVPC
CidrBlock: 172.31.16.0/20
# ========= Elasticsearch =========
xxxESSecurityGroup:
Type: 'AWS::EC2::SecurityGroup'
Properties:
GroupName: ES SG
GroupDescription: ES Security group
VpcId: !Ref xxxVPC
SecurityGroupIngress:
- IpProtocol: -1
FromPort: 0
ToPort: 65535
SourceSecurityGroupId: !Ref xxxVPCSecurityGroup
xxxElasticSearch:
Type: 'AWS::Elasticsearch::Domain'
Properties:
AccessPolicies:
Version: 2012-10-17
Statement:
- Action:
- 'es:*'
- 'ec2:*'
- 's3:*'
Principal:
AWS:
- '*'
Resource: '*'
Effect: Allow
DomainName: es-xxx-domain
EBSOptions:
EBSEnabled: true
Iops: 0
VolumeSize: 20
VolumeType: "gp2"
AdvancedOptions:
rest.action.multi.allow_explicit_index: 'true'
ElasticsearchClusterConfig:
InstanceCount: 1
InstanceType: m4.large.elasticsearch
DedicatedMasterEnabled: 'false'
VPCOptions:
SecurityGroupIds:
- !Ref xxxESSecurityGroup
SubnetIds:
- !Ref xxxLambdaSubnet1
更新的 Lambda 函数处理程序代码
import urllib2
def lambda_handler(event, context):
print('called lambda_handler')
data = ''
url = 'https://vpc-es-xxx-domain-fixthis.es.amazonaws.com'
req = urllib2.Request(url, data, 'Content-Type': 'application/json')
f = urllib2.urlopen(req)
for x in f:
print(x)
f.close()
【讨论】:
以上是关于允许 VPC 中的 Lambda 访问同一 VPC 中的 Elasticsearch 域的主要内容,如果未能解决你的问题,请参考以下文章
启用 VPC 的 Lambda 函数无法在同一 VPC 中启动/访问 EC2
如何从账户 A 中的 Lambda(VPC 中的 Lambda)调用账户 B 中的 AWS Lambda 函数(VPC 中的这个 Lambda)
VPC 中的 AWS Lambda 在 NAT 之后没有互联网访问权限