Aws lambda VPC(与MongoDB Atlas对等)连接NAT网关中的Terraform Lambda
我已经构建了一个Terraform配置,它部署了一个MongoDB atlas云集群,并使用我的AWS帐户设置了一个VPC对等点。terraform配置将凭据存储在AWS Secrets Manager中。不幸的是,我的Lambda无法访问Secrets Manager API端点或Atlas上托管的MongoDB集群。我读到我需要在我的专有网络上设置一个NAT网关来访问公共互联网。我不是一个网络专家,我尝试过添加一系列不同的配置,但都没有效果。请帮助:Aws lambda VPC(与MongoDB Atlas对等)连接NAT网关中的Terraform Lambda,aws-lambda,amazon-vpc,terraform-provider-aws,mongodb-atlas,aws-secrets-manager,Aws Lambda,Amazon Vpc,Terraform Provider Aws,Mongodb Atlas,Aws Secrets Manager,我已经构建了一个Terraform配置,它部署了一个MongoDB atlas云集群,并使用我的AWS帐户设置了一个VPC对等点。terraform配置将凭据存储在AWS Secrets Manager中。不幸的是,我的Lambda无法访问Secrets Manager API端点或Atlas上托管的MongoDB集群。我读到我需要在我的专有网络上设置一个NAT网关来访问公共互联网。我不是一个网络专家,我尝试过添加一系列不同的配置,但都没有效果。请帮助: 我是否需要为我的VPC设置NAT网关以访
variable "admin_profile" {
type = string
default = "superadmin"
}
variable "region" {
type = string
default = "us-west-2"
}
provider "aws" {
profile = var.admin_profile
region = "us-west-2"
alias = "admin"
}
// create mongo db organization + cluster on atlas
provider "mongodbatlas" {
public_key = var.atlas_public_key
private_key = var.atlas_private_key
}
//superadmin creds
variable aws_account_id {
type = string
}
variable atlas_private_key {
type = string
}
variable atlas_public_key {
type = string
}
variable atlas_region {
type = string
default = "US_WEST_2"
}
variable atlas_org_id {
type = string
default = "" #EXCLUDE THIS
}
// generated creds for db
variable atlas_db_user {
default = "mongo_user"
}
resource "random_password" "password" {
length = 16
special = false
#override_special = "_%-"
}
locals {
atlas_db_password = random_password.password.result
}
variable atlas_db_vpc_cidr {
default = "192.168.224.0/21"
}
// resources
resource "mongodbatlas_project" "cluster-partner-project" {
name = "live"
org_id = var.atlas_org_id
}
resource "mongodbatlas_cluster" "cluster-partner" {
project_id = mongodbatlas_project.cluster-partner-project.id
name = "cluster-partner"
num_shards = 1
replication_factor = 3
provider_backup_enabled = true
cluster_type = "REPLICASET"
auto_scaling_disk_gb_enabled = true
mongo_db_major_version = "4.2"
//Provider Settings "block"
provider_name = "AWS"
disk_size_gb = 40
provider_disk_iops = 120
provider_volume_type = "STANDARD"
provider_encrypt_ebs_volume = true
provider_instance_size_name = "M10"
provider_region_name = var.atlas_region
}
resource "mongodbatlas_database_user" "cluster-partner-user" {
username = var.atlas_db_user
password = local.atlas_db_password
auth_database_name = "admin"
project_id = mongodbatlas_project.cluster-partner-project.id
roles {
role_name = "readAnyDatabase"
database_name = "admin"
}
roles {
role_name = "readWrite"
database_name = "app_db"
}
}
resource "mongodbatlas_network_container" "cluster-partner-network" {
atlas_cidr_block = var.atlas_db_vpc_cidr
project_id = mongodbatlas_project.cluster-partner-project.id
provider_name = "AWS"
region_name = var.atlas_region
}
resource "mongodbatlas_network_peering" "cluster-partner-network-peering" {
accepter_region_name = var.region
project_id = mongodbatlas_project.cluster-partner-project.id
container_id = mongodbatlas_network_container.cluster-partner-network.container_id
provider_name = "AWS"
route_table_cidr_block = aws_vpc.primary.cidr_block
vpc_id = aws_vpc.primary.id
aws_account_id = var.aws_account_id
}
resource "mongodbatlas_project_ip_whitelist" "default-db-access" {
project_id = mongodbatlas_project.cluster-partner-project.id
aws_security_group = aws_security_group.primary_default.id
comment = "Access for App to MongoDB"
depends_on = [mongodbatlas_network_peering.cluster-partner-network-peering]
}
// create a vpc in AWS
resource "aws_vpc" "primary" {
provider = aws.admin
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
}
// Internet Gateway
resource "aws_internet_gateway" "primary" {
provider = aws.admin
vpc_id = aws_vpc.primary.id
}
// route table
resource "aws_route" "primary-internet_access" {
provider = aws.admin
route_table_id = aws_vpc.primary.main_route_table_id
destination_cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.primary.id
}
resource "aws_route" "peeraccess" {
provider = aws.admin
route_table_id = aws_vpc.primary.main_route_table_id
destination_cidr_block = var.atlas_db_vpc_cidr
vpc_peering_connection_id = mongodbatlas_network_peering.cluster-partner-network-peering.connection_id
depends_on = [aws_vpc_peering_connection_accepter.peer]
}
//subnets
//public
resource "aws_subnet" "primary-az1" {
provider = aws.admin
tags = {
Name = "public primary subnet"
}
vpc_id = aws_vpc.primary.id
cidr_block = "10.0.1.0/24"
map_public_ip_on_launch = true
availability_zone = "${var.region}a"
}
//private
resource "aws_subnet" "primary-az2" {
provider = aws.admin
tags = {
Name = "private subnet 0"
}
vpc_id = aws_vpc.primary.id
cidr_block = "10.0.2.0/24"
map_public_ip_on_launch = false
availability_zone = "${var.region}b"
}
// security groups for mongo vpc connect
resource "aws_security_group" "primary_default" {
provider = aws.admin
name_prefix = "defaultvpc-"
description = "Default security group for all instances in VPC ${aws_vpc.primary.id}"
vpc_id = aws_vpc.primary.id
ingress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = [
aws_vpc.primary.cidr_block,
var.atlas_db_vpc_cidr
]
# cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
// vpc peering auto accept
resource "aws_vpc_peering_connection_accepter" "peer" {
provider = aws.admin
vpc_peering_connection_id = mongodbatlas_network_peering.cluster-partner-network-peering.connection_id
auto_accept = true
}
// save mongo account details to secret manager
resource "aws_secretsmanager_secret" "partner_iam_mongo_access" {
provider = aws.admin
name = "mongo-access"
}
locals {
mongo_credentials = {
connection_strings = mongodbatlas_cluster.cluster-partner.connection_strings
password = local.atlas_db_password
}
}
resource "aws_secretsmanager_secret_version" "partner_iam_mongo_access" {
provider = aws.admin
secret_id = aws_secretsmanager_secret.partner_iam_mongo_access.id
secret_string = jsonencode(local.mongo_credentials)
}
// create lambdas for each of the key steps in the app
// have to add the vpc
resource "aws_iam_role_policy" "lambda_policy" {
provider = aws.admin
name = "lambda_policy"
role = aws_iam_role.lambda_role.id
policy = file("./lambda-policy.json")
}
data "aws_iam_policy" "aws_lambda_vpc_access_execution_role" {
provider = aws.admin
arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
}
resource "aws_iam_role" "lambda_role" {
provider = aws.admin
name = "lambda-vpc-role-managed"
assume_role_policy = file("./lambda-assume-policy.json")
}
data "archive_file" "test-connection" {
type = "zip"
source_file = "./test-connection"
output_path = "./test-connection_deploy.zip"
}
resource "aws_lambda_function" "test-connection" {
provider = aws.admin
filename = "./test-connection_deploy.zip"
function_name = "test-connection"
role = aws_iam_role.lambda_role.arn
handler = "test-connection"
runtime = "go1.x"
timeout = 15
source_code_hash = data.archive_file.test-connection.output_base64sha256
vpc_config {
subnet_ids = [aws_subnet.primary-az1.id] // public subnet
security_group_ids = [aws_security_group.primary_default.id]
}
}
这是我的TFVAR
admin_profile = "default"
atlas_private_key =
atlas_public_key =
atlas_org_id =
aws_account_id =
这是我的Lambda策略(Lambda policy.json)
这是我的Lambda策略(Lambda-aspect-policy.json)
这是我的Lambda的(GoLang)代码
package main
import (
"context"
"fmt"
"errors"
"time"
"encoding/json"
"github.com/aws/aws-lambda-go/lambda"
"github.com/sparrc/go-ping"
"github.com/aws/aws-sdk-go/service/secretsmanager"
"go.mongodb.org/mongo-driver/mongo"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
)
type MongoCreds struct {
ConnectionStrings []map[string]interface{} `json:"connection_strings"`
Password string `json:"password"`
}
var MainRegion = "us-west-2"
func HandleRequest(ctx context.Context, updatedValues interface{}) (string, error) {
fmt.Println("we are pinging")
pinger, err := ping.NewPinger("www.google.com")
if err != nil {
panic(err)
}
pinger.Count = 3
pinger.Run() // blocks until finished
stats := pinger.Statistics() // get send/receive/rtt stats
fmt.Println(stats)
fmt.Println("connecting to mongo")
err = ConnectToMongoClient()
if err != nil {
fmt.Println("failure to connect to mongo")
}
return "", err
}
func ConnectToMongoClient() error {
sess := session.Must(session.NewSession(&aws.Config{
Region: aws.String(MainRegion),
}))
svc := secretsmanager.New(sess)
input := &secretsmanager.GetSecretValueInput{
SecretId: aws.String("mongo-access"),
}
fmt.Println("getting credentials")
secret, err := svc.GetSecretValue(input)
if err != nil {
return err
}
var mongo_creds MongoCreds
secretJson := []byte(*secret.SecretString)
err = json.Unmarshal(secretJson, &mongo_creds)
fmt.Println("credentials fetched")
fmt.Println(mongo_creds)
if err != nil {
return err
}
var mongoURI string
for _, connection := range(mongo_creds.ConnectionStrings) {
if val, ok := connection["standard_srv"]; ok {
mongoURI = val.(string)
}
}
if mongoURI == "" {
return errors.New("Unable to parse a connecting string from secret")
}
clientOptions := options.Client().ApplyURI(mongoURI).SetAuth(options.Credential{Username: "mongo_user", Password: mongo_creds.Password})
ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
client, err := mongo.Connect(ctx, clientOptions)
fmt.Println("connecting")
if err != nil {
fmt.Println(err.Error())
return err
}
ctx, _ = context.WithTimeout(context.Background(), 5*time.Second)
if err = client.Ping(ctx, readpref.Primary()); err != nil {
return err
}
return err
}
func main() {
lambda.Start(HandleRequest)
}
如果有人可以建议对我的VPC配置或Lambda代码进行实施或调整,以允许访问Secrets Manager和我的Mongo群集。理想情况下,保留专有网络中的所有流量,但如果需要公共互联网接入,就这样吧
编辑我收到的错误是超时。请注意,即使我硬编码凭据(并跳过Secret Manager步骤),我在尝试连接到Atlas托管的Mongo实例时仍会超时
我是否需要为我的VPC设置NAT网关以访问机密
经理或者我可以在VPC中托管秘密吗
您需要创建NAT网关,或者为机密管理器配置
这里的最佳实践是什么
秘密经理
我需要为我的Lambda设置NAT网关才能访问我的Atlas吗
托管MongoDB群集,即使它们位于同一VPC和I上
是否已将我的Lambda所在的安全组列入白名单
不,VPC对等的全部要点是,您可以在VPC内直接连接,而无需通过互联网。请注意,它们不是“在同一VPC中”,而是在两个具有对等连接的独立VPC中
在您的Terraform中我没有看到任何问题,在我看来,Lambda函数应该能够连接到Mongo Atlas集群。如果在无法连接到原始问题时添加您看到的实际错误消息,可能会有所帮助
地形代码如下所示:
resource "aws_vpc_endpoint" "secretsmanager" {
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.us-west-2.secretsmanager"
vpc_endpoint_type = "Interface"
security_group_ids = [
aws_security_group.sg1.id,
]
private_dns_enabled = true
}
即使我重写secretmanager请求并手动硬编码凭据,我在ping Atlas时也会超时。错误是超时。对不起,我必须投你的反对票。Terraform中没有对Secrets Manager的VPC端点的文档支持。@XaxD这似乎不必要。我把你和文件联系起来了。我刚刚在我的答案中添加了地形示例。我不知道你为什么说没有文件支持这一点。Terraform支持所有AWS VPC端点类型,您只需在配置
VPC_端点
resource.com.amazonaws.us-west-2.secretsmanager不受支持时指定AWS服务名称即可。您是否可以共享获得的准确错误消息?
package main
import (
"context"
"fmt"
"errors"
"time"
"encoding/json"
"github.com/aws/aws-lambda-go/lambda"
"github.com/sparrc/go-ping"
"github.com/aws/aws-sdk-go/service/secretsmanager"
"go.mongodb.org/mongo-driver/mongo"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
)
type MongoCreds struct {
ConnectionStrings []map[string]interface{} `json:"connection_strings"`
Password string `json:"password"`
}
var MainRegion = "us-west-2"
func HandleRequest(ctx context.Context, updatedValues interface{}) (string, error) {
fmt.Println("we are pinging")
pinger, err := ping.NewPinger("www.google.com")
if err != nil {
panic(err)
}
pinger.Count = 3
pinger.Run() // blocks until finished
stats := pinger.Statistics() // get send/receive/rtt stats
fmt.Println(stats)
fmt.Println("connecting to mongo")
err = ConnectToMongoClient()
if err != nil {
fmt.Println("failure to connect to mongo")
}
return "", err
}
func ConnectToMongoClient() error {
sess := session.Must(session.NewSession(&aws.Config{
Region: aws.String(MainRegion),
}))
svc := secretsmanager.New(sess)
input := &secretsmanager.GetSecretValueInput{
SecretId: aws.String("mongo-access"),
}
fmt.Println("getting credentials")
secret, err := svc.GetSecretValue(input)
if err != nil {
return err
}
var mongo_creds MongoCreds
secretJson := []byte(*secret.SecretString)
err = json.Unmarshal(secretJson, &mongo_creds)
fmt.Println("credentials fetched")
fmt.Println(mongo_creds)
if err != nil {
return err
}
var mongoURI string
for _, connection := range(mongo_creds.ConnectionStrings) {
if val, ok := connection["standard_srv"]; ok {
mongoURI = val.(string)
}
}
if mongoURI == "" {
return errors.New("Unable to parse a connecting string from secret")
}
clientOptions := options.Client().ApplyURI(mongoURI).SetAuth(options.Credential{Username: "mongo_user", Password: mongo_creds.Password})
ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
client, err := mongo.Connect(ctx, clientOptions)
fmt.Println("connecting")
if err != nil {
fmt.Println(err.Error())
return err
}
ctx, _ = context.WithTimeout(context.Background(), 5*time.Second)
if err = client.Ping(ctx, readpref.Primary()); err != nil {
return err
}
return err
}
func main() {
lambda.Start(HandleRequest)
}
resource "aws_vpc_endpoint" "secretsmanager" {
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.us-west-2.secretsmanager"
vpc_endpoint_type = "Interface"
security_group_ids = [
aws_security_group.sg1.id,
]
private_dns_enabled = true
}