概述
楼主最近做了一个项目,需要Lambda去连接一个DynamoDB,难度在于Lambda和DynamoDB分别在两个AWS账户中,同时Lambda处于也要连接RDS的需要,跟RDS也放在同一个VPC中,这无异于再增加了一层难度,也就是说Lambda的traffic需要通过VPC Endpoint先出VPC才能连接DynamoDB。与此同时还有权限问题和身份问题,Lambda需要assume role的权限来操控另一个账户的DynamoDB, 楼主将一步一步来解析这些是如何实现。
建立权限
首先需要给Lambda Function建立一个单独的身份(role), 除了给到你Lambda本身需要的那这些基本权限诸如RDS/CloudWatch的读写权限以外,还需要增加两个规则,一个是AWS提供的VPC读写权限,一个是自定义的AssumeRole Police在Lambda所在的账户中(这里假定是A账户),因为Lambda本身是没有权限去读写在另一个账户的任何资源的,只能通过assume role到DynamoDB所在的AWS账户中(这里假定是B账户)的DynamoDB所绑定的角色来进行读写操作.
以下是账户A中Lambda的身份(role)中拥有的VPC权限和Assume Role权限, 注意Resource指定的Arn号码对应的是B账户中的账户B中DynamoDB所依附的身份(role):
以下是账户B中DynamoDB所依附的身份(role)中所依附的权限规则, 包含了要显式信任的账户A的Lambda身份资源号码(ARN):
以下是账户B中DynamoDB所依附的身份(role)中所依附的权限规则, 包含了对应DynamoDB表的读写权限:
到这一步在IAM中创建权限和身份的工作就完成了,下一步就是进行VPC网络配置。
VPC网络配置
这个章节讲述如何将Lambda的网络请求和数据访问到DynamoDB。需要注意的是我设置Lambda是放在VPC之中,默认只能访问到所在VPC里面的资源,比如同处于一个VPC的RDS. 默认配置下AssumeRole和Lambda对DynamoDB的读写请求都无法通过VPC到达公网。这里就要用到两个新的网管组件,一个是NAT(network address translation)和VPC Endpoint, NAT可以讲VPC内网请求连接到公网,VPC Endpoint则是专门给DynamoDB提供网络请求的数据通道,这样DynamoDB的读写请求和数据交互就不用通过公网了,保证了数据的安全。
1.创建VPC
在AWS VPC服务中创建VPC, 并且给Lambda指定这个刚刚创建的VPC.
2.创建子网Subnet
VPC是由子网作为基本单元所组成的,我们需要最少要创建三个子网subnet,一个子网subnet-a给IGW(internet gateway)用于连接公网所用,另外两个(subnet-b和subnet-c)给Lambda用。创建subnet的时候,CIDR可以用下图的组合:
将创建好的subnet-b和subnet-c绑定到Lambda
3.创建igw公网网关
创建internet gateway,并且绑定到我们一开始创建的VPC上即可。
4.创建NAT Gateway
子网subnet选择subnet-a,就是那个公共子网(public subnet), 下面弹性IP ID选择创建新的EIP按钮即可。
5.创建路由表route table
创建两个新的路由表(route table),为了理解方便,分别叫route-table-a和route-table-b,
route-table-a作为默认主要路由表,默认所有外网请求通过internet gateway网关来请求VPC外的资源。
route-table-b则是这个项目最为精华的部分了,如下图配置,0.0.0.0/0地址规则表明默认流量是去到NAT的,NAT会又会自动把流量导到Internet gateway网关,这个规则主要是为了assume role的流量服务的,倒数第二行的规则在创建目的地的时候下拉选项里面会自动弹出来,选择我么刚刚创建的VPC endpoint对应的目的地,这样Lambda看到要去到dynamoDB的流量请求就会走VPC endpoint。
同时将这个路由表route-table-b跟我们之前创建的两个私有子网subnet进行关联.
6.创建安全组Security Group
并且把security group绑定到Lambda.
至此,所有网络设置都完成了,下面将讲解如何在代码中实现连接。
代码
const assumeRoleOnDynamoDB = function (results, sts = new AWS.STS({apiVersion: '2012-10-17'})) {
const params = {
RoleArn: process.env.AssumedDDBRole,
RoleSessionName: process.env.RoleSessionName
};
return new Promise((resolve, reject) => {
sts.assumeRole(params, function (err, stsEncryptedData) {
if (err) {
console.error("[ERROR] Unable to assume role on Freelunch DynamoDB. ");
return reject(err);
}
return resolve([results, stsEncryptedData]);
});
});
};
const upsertDynamoDBTable = function (results, stsEncryptedData) {
const dynamodb = new AWS.DynamoDB({
apiVersion: '2012-08-10',
accessKeyId: stsEncryptedData.Credentials.AccessKeyId,
secretAccessKey: stsEncryptedData.Credentials.SecretAccessKey,
sessionToken: stsEncryptedData.Credentials.SessionToken,
region: process.env.REGION
});
const paramsForDDB = {
TableName: 'TableName',
Item: { 'data': { "S": "data" }}
};
return new Promise((resolve, reject) => {
dynamodb.putItem(paramsForDDB, function (err, data) {
if (err) {
console.error("[ERROR]: Failed to upsert info into DynamoDB. ");
return reject(err);
}
resolve([results, dynamodb])
});
});
};
网友评论