Первоначально опубликовано в моем блоге.
Недавно я узнал, что разработал систему, которая имела 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 частной подсети.
Это решение имеет три больших преимущества:
- Трафик остается внутри вашего VPC, никогда не пересекает публичный интернет. Таким образом, более быстрый, дешевле, и безопаснее.
- Подобно экземпляру NAT, конечные точки обслуживания могут иметь к ним группы безопасности.
- Инфраструктура для эксплуатации и управления конечной точкой обслуживания невероятно минимальна. Экономия времени, денег и эксплуатационных усилий.
Это решение, которое я хотел! Конечные точки обслуживания проверяют все коробки требований, которые у меня были.
* 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. Это безопасно, быстро, дешево, и лучшее из всех легко управлять.
Вы использовали конечные точки обслуживания раньше? У тебя есть вопросы? Давайте поговорим в комментариях ниже.
Ресурсы
- Вот и Пример проекта террафора на Github.
Оригинал: «https://dev.to/david_j_eddy/how-to-aws-service-endpoints-via-terraform-for-fun-and-profit-ba1»