Рубрики
Uncategorized

Как: AWS Сервисный конечные точки через террафору для веселья и прибыли

Первоначально опубликовано в моем блоге. Недавно я обнаружил, что разработал систему, которая имеет функцию AWS LAMBDA … Помечено террафом, AWS, DevOps, Cloud.

Первоначально опубликовано в моем блоге.

Недавно я узнал, что разработал систему, которая имела AWS Lambda функционирует внутри частного VPC. Но мне нужно было пройти полезную нагрузку из вывода функции лямбда на службу AWS, которая должна была быть публично маршрутизированной (специально для SES) . Я нашел действительно только три варианта, чтобы решить эту ситуацию:

Варианты:

1) экземпляр NAT (хороший)

Это решение включает в себя эксплуатацию вычисленного экземпляра, чтобы действовать в качестве ресурса транслятора сетевого адреса (NAT). Когда ресурсы внутри частной подсети необходимо получить доступ к публичному DNS, трафик направляется через экземпляр NAT. Это имеет очевидный недостаток необходимости запускать вычисленный экземпляр и ограничивая его аппаратным обеспечением, связанным с ним. Это добавляет управление и затраты на голову, что я действительно не хотел иметь дело. Хотя AMI MarketPlace имеет предварительно настроенные изображения, которые я до сих пор не хотел управлять дополнительным оборудованием для одной лямбда, который вызывается спорадически.

Вот как выглядит конфигурация сети экземпляра NAT:

Кредит: Aws

2) Ворота Nat

Лучший вариант — использовать сервис Nat Gateway. Представьте себе, если вы позволите AWS эксплуатируйте супер-кластер экземпляра NAT с дополнительным преимуществом более низких затрат на работу, более простые настройки и более высокую сеть. Внизу — это неспособность использовать группы безопасности с ней, это рекомендуемое решение для текущих требований NAT, идущих вперед на AWS.

Вот что выглядит конфигурация сети NAT Gateway:

Кредит: Aws

3) Конечная точка обслуживания (Лучшая)

Новый малыш на блоке, конечных точках обслуживания обеспечивает возможность доступа к поддерживаемым службам из частной подсети с большими преимуществами по реализациям NAT. Представьте себе, если вы подключили сетевой кабель от вашей личной подсети непосредственно к публично маршруту ресурса. AWS делает это через Эластичный сетевой интерфейс (ENI) Ресурс в частную подсеть. ENI даже занимает IP-адрес в диапазоне CIDR частной подсети.

Это решение имеет три больших преимущества:

  1. Трафик остается внутри вашего VPC, никогда не пересекает публичный интернет. Таким образом, более быстрый, дешевле, и безопаснее.
  2. Подобно экземпляру NAT, конечные точки обслуживания могут иметь к ним группы безопасности.
  3. Инфраструктура для эксплуатации и управления конечной точкой обслуживания невероятно минимальна. Экономия времени, денег и эксплуатационных усилий.

Это решение, которое я хотел! Конечные точки обслуживания проверяют все коробки требований, которые у меня были.

* Side Примечание. Сервисные интерфейсы конечных точек являются реализациями службы AWS Частная ссылка особенность. Сервисные шлюзы конечной точки доступны только для S3 и Dynamodb. Конфигурация террафора минимально отличается от двух.

Вот как выглядит конфигурация сети конечной точки обслуживания:

Кредит: Aws

Давайте терафом это Плохой мальчик!

VPC.

Используя Террафом (0.12.24 во время написания) Я настроил базовый VPC, один AZ с частной подсетью и широкой открытой группой безопасности. Очень основные сети здесь; Ничего особенного, основные строительные блоки любой VPC. Обратите внимание, что VPC не имеет никаких ресурсов NAT, ни интернет-шлюза.

# Networking

## VPC

resource aws_vpc this {
  assign_generated_ipv6_cidr_block = false
  cidr_block                       = var.vpc_private_cidr
  enable_dns_hostnames             = true
  enable_dns_support               = true

  tags = merge(
    {
      Name = join(var.delimiter, [var.name, var.stage, "vpc", random_string.this.result])
      Tech = "VPC"
      Srv  = "VPC"
    },
    var.tags
  )
}

## Route Table <-> Subnet associations

resource aws_route_table_association private_0 {
  subnet_id      = aws_subnet.private_0.id
  route_table_id = aws_route_table.private_0.id
}

## Route Tables

resource aws_route_table private_0 {
  vpc_id = aws_vpc.this.id

  depends_on = [
    aws_vpc.this
  ]

  tags = merge(
    {
      Name = join(var.delimiter, [var.name, var.stage, "private-route", random_string.this.result])
      Tech = "Route"
      Srv  = "VPC"
    },
    var.tags
  )
}

## Subnets

resource aws_subnet private_0 {
  availability_zone               = var.availability_zone[0]
  vpc_id                          = aws_vpc.this.id
  cidr_block                      = var.vpc_private_cidr
  assign_ipv6_address_on_creation = false

  depends_on = [
    aws_vpc.this
  ]

  tags = merge(
    {
      Name = join(var.delimiter, [var.name, var.stage, "subnet-a", random_string.this.result])
      Tech = "Subnet"
      Srv  = "VPC"
      Note = "Private"
    },
    var.tags
  )
}

 
resource aws_security_group private_lambda_0 {
  description = "Private Lambda SG"
  name        = join(var.delimiter, [var.name, var.stage, "private-subnet-lambda-0", random_string.this.id])
  vpc_id      = aws_vpc.this.id

  ingress {
    from_port   = 0
    to_port     = 65535
    protocol    = "tcp"
    cidr_blocks = [
      var.vpc_private_cidr
    ]
  }

  egress {
    from_port   = 0
    to_port     = 65535
    protocol    = "tcp"
    cidr_blocks = [
      var.vpc_private_cidr
    ]
  }

  tags = merge(
    {
      Name = join(var.delimiter, [var.name, var.stage, "private-subnet-lambda-0", random_string.this.id])
      Tech = "Security Group"
      Srv  = "EC2"
    },
    var.tags
  )
}

QQS Queue.

Первый ресурс после базовых ресурсов VPC мне нужно было создать, была очередь SQS. Как и многие другие услуги, предлагаемые AWS, очередует, имеет портативные FQDNS. Используйте правильную безопасность и настройку IAM! Принцип наименее привилегии для защиты ваших ресурсов. Помните: безопасность сначала.

resource aws_sqs_queue dead_letter_queue {
  name = join(var.delimiter, [var.name, var.stage, "sqs-dead-letter", var.random_string.id])

  tags = merge(
    {
      Name = join(var.delimiter, [var.name, var.stage, "sqs-dead-letter", var.random_string.id])
      Tech = "SQS"
      Srv  = "SQS"
    },
    var.tags
  )
}

resource aws_sqs_queue this {
  name                      = join(var.delimiter, [var.name, var.stage, "sqs", var.random_string.id])

  redrive_policy = jsonencode({
    deadLetterTargetArn = aws_sqs_queue.dead_letter_queue.arn
    maxReceiveCount     = 4
  })

  tags = merge(
    {
      Name = join(var.delimiter, [var.name, var.stage, "sqs", var.random_string.id])
      Tech = "SQS"
      Srv  = "SQS"
    },
    var.tags
  )
}

Частный лямбда

Затем я создал функцию лямбда; Назначение его частной подсети и группы безопасности, которые содержатся внутри VPC. Лямбда код Python на основе и как таковой я использовал BOTO3 Чтобы обработать создание запроса HTTPS, которое будет поместить сообщение в очередь. Это не будет работать изначально, так как мы не создали конечную точку обслуживания.

## data

data archive_file this {
  type        = "zip"
  source_dir  = "${path.module}/src"
  output_path = "${path.module}/file.zip"
}

## resources

resource aws_lambda_function this {
  filename         = data.archive_file.this.output_path
  function_name    = join("-", [var.stage, var.name, "private-lambda", var.random_string.id])
  handler          = "index.lambda_handler"
  role             = aws_iam_role.this.arn
  runtime          = "python3.7"
  source_code_hash = data.archive_file.this.output_base64sha256

  # NOTE Need to pass the REGION and QUEUE_ARN to enable Boto3 to find the correct queue
  environment {
    variables = {
      AWS_ACCT_ID = var.aws_acct_id
      QUEUE_ARN   = var.aws_sqs_queue.arn
      REGION      = var.region
    }
  }

  # NOTE This places the Lambda inside a VPC into the subnet of choice
  vpc_config {
    security_group_ids = var.security_group_ids
    subnet_ids         = var.subnet_ids
  }

  tags = merge(
    {
      Name = join(var.delimiter, [var.name, var.stage, "private-lambda", var.random_string.id])
      Tech = "Python_3_7"
      Srv  = "Lambda"
    },
    var.tags
  )
}

## IAM role, policies, and attachments

resource aws_iam_policy this {
  name   = join(var.delimiter, [var.name, var.stage, "private-lambda-policy", var.random_string.id])
  path   = "/"
  policy = file("${path.module}/iam/policy.json")
}

resource aws_iam_role this {
  assume_role_policy = file("${path.module}/iam/role.json")
  name               = join(var.delimiter, [var.name, var.stage, "private-lambda-role", var.random_string.id])
}

resource aws_iam_role_policy_attachment this {
  role       = aws_iam_role.this.name
  policy_arn = aws_iam_policy.this.arn
}

Общественный лямбда

Вторая лямбда, которую я сделал, будет потреблять очередь SQS. Обратите внимание, что конфигурация не включает в себя конфигурацию VPC или подсети? Это означает, что лямбда будет публичной внутри моей учетной записи.

## data

data archive_file this {
  type        = "zip"
  source_dir  = "${path.module}/src"
  output_path = "${path.module}/file.zip"
}

## resources

resource aws_lambda_function this {
  filename         = data.archive_file.this.output_path
  function_name    = join("-", [var.stage, var.name, "public-lambda", var.random_string.id])
  handler          = "index.lambda_handler"
  role             = aws_iam_role.this.arn
  runtime          = "python3.7"
  source_code_hash = data.archive_file.this.output_base64sha256

  tags = merge(
    {
      Name = join(var.delimiter, [var.name, var.stage, "public-lambda", var.random_string.id])
      Tech = "Python_3_7"
      Srv  = "Lambda"
    },
    var.tags
  )
}

## IAM role, policies, and attachments

resource aws_iam_policy this {
  name   = join(var.delimiter, [var.name, var.stage, "public-lambda-policy", var.random_string.id])
  path   = "/"
  policy = file("${path.module}/iam/policy.json")
}

resource aws_iam_role this {
  assume_role_policy = file("${path.module}/iam/role.json")
  name               = join(var.delimiter, [var.name, var.stage, "public-lambda-role", var.random_string.id])
}

resource aws_iam_role_policy_attachment this {
  role       = aws_iam_role.this.name
  policy_arn = aws_iam_policy.this.arn
}

## Subscription to SQS queue

resource "aws_lambda_event_source_mapping" "example" {
  event_source_arn = var.aws_sqs_queue.arn
  function_name    = aws_lambda_function.this.arn
}

Конечная точка обслуживания

Вот волшебный соус! Эти ресурсы Terraform соединяют очередь SQS через ENI в частную подсеть моей VPC. Теперь VPC сможет маршрутить преданный HTTP-запрос на HTTPS Private Lambda в службу SQS. Несмотря на то, что частное лямбда не имеет очевидного определенного пути к государственным услугам.

resource aws_vpc_endpoint sqs {
  private_dns_enabled = true
  service_name        = join(".", ["com.amazonaws", var.region, "sqs"])
  vpc_endpoint_type   = "Interface"
  vpc_id              = aws_vpc.this.id

  security_group_ids = [
    aws_security_group.private_lambda_0.id
  ]

  # Interface types get this. It connects the Endpoint to a subnet
  subnet_ids = [
    aws_subnet.private_0.id
  ] 

  tags = merge(
    {
      Name = join(var.delimiter, [var.name, var.stage, "service-endpoint-for-sqs", random_string.this.id])
      Tech = "Service Endpoint"
      Srv  = "VPC"
    },
    var.tags
  )
}

resource aws_vpc_endpoint_subnet_association sqs_assoc {
  subnet_id       = aws_subnet.private_0.id
  vpc_endpoint_id = aws_vpc_endpoint.sqs.id
}

Демо/доказательство

Выполнение частного лямбда с помощью тестовой полезной нагрузки. Наблюдение за журналами, которые я могу успешно видеть, что частный Lambda выполняет. Проверка общественного лямбда, я также вижу полезную нагрузку от частной лямбда. Оно работает!

Cloudwatch журналы частный Вывоз вызывания лямбда. Обратите внимание на qs.us-west-2.amazonaws.com:443 fqdn.

CloudWatch Logs выводится после публичный Lambda процесс сообщения очереди SQS.

Вывод

Хотя может показаться немного странно на первых конечных точках обслуживания — отличный способ прикрепить поддерживаемые услуги AWS в частную подсеть (ы) VPC. Это безопасно, быстро, дешево, и лучшее из всех легко управлять.

Вы использовали конечные точки обслуживания раньше? У тебя есть вопросы? Давайте поговорим в комментариях ниже.

Ресурсы

Оригинал: «https://dev.to/david_j_eddy/how-to-aws-service-endpoints-via-terraform-for-fun-and-profit-ba1»