Первоначально опубликовано в моем блоге.
Недавно я узнал, что разработал систему, которая имела 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»