Aws lambda VPC(与MongoDB Atlas对等)连接NAT网关中的Terraform Lambda

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网关以访

我已经构建了一个Terraform配置,它部署了一个MongoDB atlas云集群,并使用我的AWS帐户设置了一个VPC对等点。terraform配置将凭据存储在AWS Secrets Manager中。不幸的是,我的Lambda无法访问Secrets Manager API端点或Atlas上托管的MongoDB集群。我读到我需要在我的专有网络上设置一个NAT网关来访问公共互联网。我不是一个网络专家,我尝试过添加一系列不同的配置,但都没有效果。请帮助:

  • 我是否需要为我的VPC设置NAT网关以访问机密 经理或者我可以在VPC中托管秘密吗?是什么 这里的最佳实践
  • 我需要为我的Lambda设置NAT网关吗 访问Atlas托管的MongoDB集群,即使它们处于 同一个VPC和我已将我的Lambda所在的安全组列入白名单
  • 如何设置NAT网关以允许Lambda连接到我的服务器 地形学中的阿特拉斯星团
  • 理想情况下,我希望尽可能地锁定与外部互联网的连接,但如果这不是一个选项,我对任何能够正常工作的实现都很满意

    这是我的地形配置

    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
    }