如何将 aws api 网关连接到 vpc 内的私有 lambda 函数
how to connect an aws api gateway to a private lambda function inside a vpc
我正在尝试将 aws api 网关连接到驻留在 VPC 中的 lambda 函数,然后检索秘密管理器以使用带有 boto3 的 python 代码访问数据库。数据库和 vpc 端点是在私有子网中创建的。
lambda 函数
def test_secret():
secret = "mysecret"
region = "MY-REGION" :)
session = boto3.session.Session()
client = session.client(
service_name="secretsmanager",
region_name=region
)
secret_value_response = client.get_secret_value(SecretId=secret)
try:
result = json.loads(secret_value_response["SecretString"])
except Exception as e:
result = "Error found: {}".format(e)
return result
def handler(event, context):
get_secrets = test_secret() # THE CODE FAIL HERE IN CLOUDWATCH
try:
some_string = event["queryStringParameters"]["some_string"]
response = {}
response["statusCode"] = 200
response["body"] = some_string + " " + get_secrets["name"]
print("secrets: ", some_string + " " + get_secrets["name"])
except Exception as e:
response = "Error: {}".format(e)
return response
地形
安全组
resource "aws_security_group" "db" {
name = "db"
vpc_id = aws_vpc.default.id
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
λ
resource "aws_lambda_function" "lambda_test" {
function_name = "lambda-test"
...
# Attach Lambda to VPC
vpc_config {
subnet_ids = [aws_subnet.private_subnet.id]
security_group_ids = [aws_security_group.db.id]
}
}
resource "aws_iam_policy" "lambda_test" {
name = "lambda-test"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:CreateLogGroup",
"logs:PutLogEvents",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeVpcs",
"ec2:DescribeNetworkInterfaces",
"ec2:CreateNetworkInterface",
"ec2:DeleteNetworkInterface",
"ec2:AttachNetworkInterface",
"ec2:AssignPrivateIpAddresses",
"ec2:UnassignPrivateIpAddresses",
"autoscaling:CompleteLifecycleAction",
"secretsmanager:GetSecretValue"
],
"Resource": [
"arn:aws:lambda:::${aws_lambda_function.lambda_test.arn}",
"arn:aws:lambda:::${aws_lambda_function.lambda_test.arn}/*"
]
},
{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": [
"arn:aws:lambda:::${data.aws_secretsmanager_secret.my_secret.arn}",
"arn:aws:lambda:::${data.aws_secretsmanager_secret.my_secret.arn}/*"
]
}
]
}
EOF
}
resource "aws_iam_role" "lambda_test_role" {
name = "lambda-test-role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Id": "",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": [
"lambda.amazonaws.com",
"secretsmanager.amazonaws.com"
]
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "lambda_test" {
policy_arn = aws_iam_policy.lambda_test.arn
role = aws_iam_role.lambda_test_role.name
}
resource "aws_iam_role_policy_attachment" "lambda_test_vpc_access" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
role = aws_iam_role.lambda_test_role.name
}
vpc 端点
resource "aws_vpc_endpoint" "vpc_endpoint" {
vpc_id = aws_vpc.default.id
service_name = "com.amazonaws.${var.AWS_REGION}.secretsmanager"
vpc_endpoint_type = "Interface"
security_group_ids = [aws_security_group.db.id]
private_dns_enabled = true
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Principal": "*",
"Resource": "*"
}
]
}
EOF
}
无需尝试访问 secretsmanager
,lambda 本身工作正常,我能够访问 url 端点,提供参数,然后在 cloudwatch 日志中查看结果,但一旦我尝试在 lambda 函数端点调用 secretsmanager
,页面 return {"message": "Internal server error"}
,当我查看日志时它说 {"errorMessage": "Could not connect to the endpoint URL: \"https://secretsmanager.REGIONHIDDEN.amazonaws.com/\"", "errorType": "EndpointConnectionError"
我上面有什么地方做错了吗?
如果您可以从 API 网关调用 Lambda 函数,那么您的问题标题“如何将 aws api 网关连接到 vpc 中的私有 lambda 函数”已经完成并且可以正常工作.
看来您的实际问题只是从 VPC 中的 Lambda 函数 运行 内部访问 Secrets Manager。
您将“db”安全组分配给 Lambda 函数也很奇怪。这个安全组的 inbound/outbound 规则是什么?
完全不清楚您创建 VPC 端点的原因。我们应该如何看待 service_name = "foo"
?什么是服务“foo”?此 VPC 终端节点与 Lambda 函数有何关联?如果这应该是 Secrets Manager 的 VPC 端点,则服务名称 should be "com.amazonaws.YOUR-REGION.secretsmanager"
.
如果您需要更多帮助,您需要编辑您的问题以提供以下内容:任何相关安全组的入站 和 出站规则,以及正在尝试的 Lambda 函数代码调用 SecretsManager。
更新: 在评论和更新问题中进行澄清后,我认为问题是您缺少 VPC 端点的任何子网分配。此外,由于您要添加具有完全访问权限的 VPC 策略,因此您可以完全忽略它,因为默认策略是完全访问权限。我建议将 VPC 端点更改为以下内容:
resource "aws_vpc_endpoint" "vpc_endpoint" {
vpc_id = aws_vpc.default.id
service_name = "com.amazonaws.${var.AWS_REGION}.secretsmanager"
vpc_endpoint_type = "Interface"
subnet_ids = [aws_subnet.private_subnet.id]
security_group_ids = [aws_security_group.db.id]
private_dns_enabled = true
}
更新 2: Lambda 函数的 IAM 策略的这一部分是错误的:
{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": [
"arn:aws:lambda:::${data.aws_secretsmanager_secret.my_secret.arn}",
"arn:aws:lambda:::${data.aws_secretsmanager_secret.my_secret.arn}/*"
]
}
这使 Lambda 可以访问秘密,其中的 ARN 是 Lambda 函数,这不是有效的秘密 ARN。应该是这样的:
{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": "${data.aws_secretsmanager_secret.my_secret.arn}"
}
你的这部分政策也搞砸了:
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:CreateLogGroup",
"logs:PutLogEvents",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeVpcs",
"ec2:DescribeNetworkInterfaces",
"ec2:CreateNetworkInterface",
"ec2:DeleteNetworkInterface",
"ec2:AttachNetworkInterface",
"ec2:AssignPrivateIpAddresses",
"ec2:UnassignPrivateIpAddresses",
"autoscaling:CompleteLifecycleAction",
"secretsmanager:GetSecretValue"
],
"Resource": [
"arn:aws:lambda:::${aws_lambda_function.lambda_test.arn}",
"arn:aws:lambda:::${aws_lambda_function.lambda_test.arn}/*"
]
您正在将此策略分配给 Lambda 函数。您在策略中列出的资源是 Lambda 函数应该有权访问的资源。您没有将 Lambda 函数本身列为资源。我不确定如何修复这部分政策,它需要分成多个部分,或者只是用 "*"
.
替换资源列表
此外,当您在 Terraform 中引用资源的 .arn
值时,您将获得完整的 ARN,因此您不应在其前面加上任何前缀。
我正在尝试将 aws api 网关连接到驻留在 VPC 中的 lambda 函数,然后检索秘密管理器以使用带有 boto3 的 python 代码访问数据库。数据库和 vpc 端点是在私有子网中创建的。
lambda 函数
def test_secret():
secret = "mysecret"
region = "MY-REGION" :)
session = boto3.session.Session()
client = session.client(
service_name="secretsmanager",
region_name=region
)
secret_value_response = client.get_secret_value(SecretId=secret)
try:
result = json.loads(secret_value_response["SecretString"])
except Exception as e:
result = "Error found: {}".format(e)
return result
def handler(event, context):
get_secrets = test_secret() # THE CODE FAIL HERE IN CLOUDWATCH
try:
some_string = event["queryStringParameters"]["some_string"]
response = {}
response["statusCode"] = 200
response["body"] = some_string + " " + get_secrets["name"]
print("secrets: ", some_string + " " + get_secrets["name"])
except Exception as e:
response = "Error: {}".format(e)
return response
地形
安全组
resource "aws_security_group" "db" {
name = "db"
vpc_id = aws_vpc.default.id
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
λ
resource "aws_lambda_function" "lambda_test" {
function_name = "lambda-test"
...
# Attach Lambda to VPC
vpc_config {
subnet_ids = [aws_subnet.private_subnet.id]
security_group_ids = [aws_security_group.db.id]
}
}
resource "aws_iam_policy" "lambda_test" {
name = "lambda-test"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:CreateLogGroup",
"logs:PutLogEvents",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeVpcs",
"ec2:DescribeNetworkInterfaces",
"ec2:CreateNetworkInterface",
"ec2:DeleteNetworkInterface",
"ec2:AttachNetworkInterface",
"ec2:AssignPrivateIpAddresses",
"ec2:UnassignPrivateIpAddresses",
"autoscaling:CompleteLifecycleAction",
"secretsmanager:GetSecretValue"
],
"Resource": [
"arn:aws:lambda:::${aws_lambda_function.lambda_test.arn}",
"arn:aws:lambda:::${aws_lambda_function.lambda_test.arn}/*"
]
},
{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": [
"arn:aws:lambda:::${data.aws_secretsmanager_secret.my_secret.arn}",
"arn:aws:lambda:::${data.aws_secretsmanager_secret.my_secret.arn}/*"
]
}
]
}
EOF
}
resource "aws_iam_role" "lambda_test_role" {
name = "lambda-test-role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Id": "",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": [
"lambda.amazonaws.com",
"secretsmanager.amazonaws.com"
]
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "lambda_test" {
policy_arn = aws_iam_policy.lambda_test.arn
role = aws_iam_role.lambda_test_role.name
}
resource "aws_iam_role_policy_attachment" "lambda_test_vpc_access" {
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
role = aws_iam_role.lambda_test_role.name
}
vpc 端点
resource "aws_vpc_endpoint" "vpc_endpoint" {
vpc_id = aws_vpc.default.id
service_name = "com.amazonaws.${var.AWS_REGION}.secretsmanager"
vpc_endpoint_type = "Interface"
security_group_ids = [aws_security_group.db.id]
private_dns_enabled = true
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Principal": "*",
"Resource": "*"
}
]
}
EOF
}
无需尝试访问 secretsmanager
,lambda 本身工作正常,我能够访问 url 端点,提供参数,然后在 cloudwatch 日志中查看结果,但一旦我尝试在 lambda 函数端点调用 secretsmanager
,页面 return {"message": "Internal server error"}
,当我查看日志时它说 {"errorMessage": "Could not connect to the endpoint URL: \"https://secretsmanager.REGIONHIDDEN.amazonaws.com/\"", "errorType": "EndpointConnectionError"
我上面有什么地方做错了吗?
如果您可以从 API 网关调用 Lambda 函数,那么您的问题标题“如何将 aws api 网关连接到 vpc 中的私有 lambda 函数”已经完成并且可以正常工作.
看来您的实际问题只是从 VPC 中的 Lambda 函数 运行 内部访问 Secrets Manager。
您将“db”安全组分配给 Lambda 函数也很奇怪。这个安全组的 inbound/outbound 规则是什么?
完全不清楚您创建 VPC 端点的原因。我们应该如何看待 service_name = "foo"
?什么是服务“foo”?此 VPC 终端节点与 Lambda 函数有何关联?如果这应该是 Secrets Manager 的 VPC 端点,则服务名称 should be "com.amazonaws.YOUR-REGION.secretsmanager"
.
如果您需要更多帮助,您需要编辑您的问题以提供以下内容:任何相关安全组的入站 和 出站规则,以及正在尝试的 Lambda 函数代码调用 SecretsManager。
更新: 在评论和更新问题中进行澄清后,我认为问题是您缺少 VPC 端点的任何子网分配。此外,由于您要添加具有完全访问权限的 VPC 策略,因此您可以完全忽略它,因为默认策略是完全访问权限。我建议将 VPC 端点更改为以下内容:
resource "aws_vpc_endpoint" "vpc_endpoint" {
vpc_id = aws_vpc.default.id
service_name = "com.amazonaws.${var.AWS_REGION}.secretsmanager"
vpc_endpoint_type = "Interface"
subnet_ids = [aws_subnet.private_subnet.id]
security_group_ids = [aws_security_group.db.id]
private_dns_enabled = true
}
更新 2: Lambda 函数的 IAM 策略的这一部分是错误的:
{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": [
"arn:aws:lambda:::${data.aws_secretsmanager_secret.my_secret.arn}",
"arn:aws:lambda:::${data.aws_secretsmanager_secret.my_secret.arn}/*"
]
}
这使 Lambda 可以访问秘密,其中的 ARN 是 Lambda 函数,这不是有效的秘密 ARN。应该是这样的:
{
"Effect": "Allow",
"Action": "secretsmanager:GetSecretValue",
"Resource": "${data.aws_secretsmanager_secret.my_secret.arn}"
}
你的这部分政策也搞砸了:
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:CreateLogGroup",
"logs:PutLogEvents",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeVpcs",
"ec2:DescribeNetworkInterfaces",
"ec2:CreateNetworkInterface",
"ec2:DeleteNetworkInterface",
"ec2:AttachNetworkInterface",
"ec2:AssignPrivateIpAddresses",
"ec2:UnassignPrivateIpAddresses",
"autoscaling:CompleteLifecycleAction",
"secretsmanager:GetSecretValue"
],
"Resource": [
"arn:aws:lambda:::${aws_lambda_function.lambda_test.arn}",
"arn:aws:lambda:::${aws_lambda_function.lambda_test.arn}/*"
]
您正在将此策略分配给 Lambda 函数。您在策略中列出的资源是 Lambda 函数应该有权访问的资源。您没有将 Lambda 函数本身列为资源。我不确定如何修复这部分政策,它需要分成多个部分,或者只是用 "*"
.
此外,当您在 Terraform 中引用资源的 .arn
值时,您将获得完整的 ARN,因此您不应在其前面加上任何前缀。