AWS Site-to-Site VPN с NAT

В моей компании мы используем инфраструктуру Amazon Web Services (AWS). Мы помогаем организациям повысить эффективность парковок, и для этого нам необходимо взаимодействовать с их вычислительными системами. Однако эти организации, включая больницы и университеты, часто используют закрытые частные сети. Сторонние поставщики, такие как мы, могут получить доступ к этим сетям только через VPN на основе IPSec.

Можно ли создать туннель IPsec из виртуального частного облака AWS (VPC) в сеть за пределами AWS? Вариант использования, который хорошо поддерживает AWS, - это подключение вашей собственной локальной сети к VPC. Таким образом, при именовании компонентов AWS использует термин «клиентская сеть» для обозначения вашей локальной сети. Вы клиент AWS.

Кроме того, поскольку вы являетесь администратором своей локальной сети, AWS не предоставляет обширные журналы, которые позволили бы вам устранить неполадки при установке туннеля IPSec. Вместо этого AWS предполагает, что вы сможете просматривать журналы на стороне локальной сети.

Но что, если вы хотите создать туннель IPSec от вашего VPC к сторонней сети, находящейся вне вашего административного контроля? В частности, вы не можете контролировать политику CIDR сторонней сети. Третья сторона может потребовать, чтобы вы разместили свою сеть на определенном CIDR или использовали общедоступные IP-адреса.

Для поддержки создания туннелей IPSec AWS в течение многих лет предлагала специализированное решение, называемое виртуальной частной сетью (VPN). В последние годы он дополнил его общим решением под названием Transit Gateway (TGW). Решение VPN требует, чтобы сеть клиента не конфликтовала с вашим CIDR. Если вы не хотите изменять IP-адреса внутри VPC в соответствии с требованиями, вам необходимо использовать преобразование сетевых адресов (NAT). Этого можно добиться за счет творческого использования транзитного шлюза (TGW).

Ниже представлена ​​схема сети, которая иллюстрирует ключевую идею. Предположим, что у AWS VPC есть подсеть 20.0.0.0/16, сеть клиента находится в подсети 172.31.0.0/16 и что клиент хочет, чтобы мы использовали общедоступную IP-подсеть. (Мы используем терминологию AWS для обозначения третьей стороны.) Но вместо использования общедоступной IP-подсети мы будем использовать NAT для сопоставления всех экземпляров EC2 с одним эластичным общедоступным IP-адресом 1.2.3.4.

Внутри нашего VPC мы создаем подсеть 20.0.6.0/24, единственная цель которой - содержать экземпляр EC2 шлюза NAT (на схеме «NAT GW»), который будет выполнять операцию NAT. Обратите внимание, что AWS имеет встроенный компонент, называемый «шлюз NAT», но здесь мы запускаем наш собственный экземпляр EC2, который выполняет эту функцию, используя Linux и iptables фильтр пакетов.

Остальные экземпляры EC2 в нашем VPC находятся в отдельных подсетях. Таблицы маршрутизации в этих подсетях пересылают пакеты, предназначенные на сторону клиента, на эластичный сетевой интерфейс (ENI) экземпляра шлюза NAT.

Подсеть, в которой находится шлюз NAT, имеет таблицу маршрутизации, которая пересылает пакеты, предназначенные для клиента, на транзитный шлюз. Однако к этому времени адресом источника этих пакетов является общедоступный IP 1.2.3.4, потому что теперь эти пакеты были преобразованы через NAT. В отличие от более простого компонента AWS виртуальной частной сети, компонент AWS Transit Gateway не накладывает никаких ограничений на адрес источника пакета.

У транзитного шлюза есть таблица маршрутизации, которая сообщает ему, куда отправлять пакеты дальше. Пакеты, адресованные клиенту, направляются в компонент AWS «Site-to-Site VPN». Это компонент, который имеет все параметры туннеля IPsec. В частности, в нем перечислены IP-адреса конечных точек на стороне AWS и на стороне клиента. Обратите внимание, что AWS позволяет указывать IP-адрес конечной точки только на стороне клиента и автоматически выбирает общедоступный IP-адрес на своей стороне. (Мы не контролируем этот IP-адрес.)

Если компонент Site-to-Site VPN может установить соединение IPsec, то после получения пакетов от транзитного шлюза он будет пересылать их через туннель. Заказчик будет видеть 1.2.3.4 как исходный IP-адрес пакетов, а его таблица маршрутизации будет указывать на отправку пакетов, предназначенных для 1.2.3.4 IP-адреса, обратно в туннель.

Возврат пакетов

До сих пор мы обсуждали, как пакеты, исходящие из тестового экземпляра EC2 в нашем VPC, попадают на сторону клиента. Теперь посмотрим на процесс в обратном направлении. Как только VPN-соединение типа "сеть-сеть" получает пакет, предназначенный для 1.2.3.4 IP-адреса, оно пересылает его на транзитный шлюз (TGW). Таблица маршрутизации TGW сообщает, что такие пакеты нужно пересылать в конкретную подсеть внутри VPC, где работает экземпляр NAT GW.

Как только пакет прибывает в эту подсеть, конкретная запись в таблице маршрутизации подсети указывает маршрутизатору подсети пересылать пакет на эластичный сетевой интерфейс (ENI) экземпляра шлюза NAT. Экземпляр NAT принимает пакет и отменяет NAT его место назначения обратно в 20.0.0.0/16 сетевое адресное пространство. Затем он отправляет измененный пакет, и AWS пересылает его в тестовый экземпляр EC2.

Настройка экземпляра Linux NAT

Чтобы экземпляр шлюза NAT мог принимать пакеты, IP-адрес которых не указан в заголовке пункта назначения, AWS необходимо отключить «проверку источника / пункта назначения». Это можно сделать в представлении «Экземпляры» AWS Console EC2 следующим образом: щелкните правой кнопкой мыши экземпляр, чтобы открыть всплывающее меню, затем выберите «Сеть» → «Изменить проверку места назначения» и отключите проверку.

Кроме того, мы должны указать Linux, чтобы он собирал пакеты с помощью этой команды:

$ echo 1 > /proc/sys/net/ipv4/ip_forward

Предполагая, что локальный IP-адрес экземпляра NAT - 20.0.6.195, эти iptables команды устанавливают операцию NAT,

$ iptables -t nat -F
$ iptables -t nat -A POSTROUTING -d 172.31.0.0/16   -j SNAT --to-source 1.2.3.4
$ iptables -t nat -A PREROUTING  -d 1.2.3.4 -j DNAT --to-destination 20.0.6.195

Для обслуживания дополнительных клиентских сетей мы можем добавить больше линий SNAT. Например, для клиентской сети с CIDR 192.168.0.0/16 мы должны добавить это правило,

$ iptables -t nat -A POSTROUTING -d 192.168.0.0/16   -j SNAT --to-source 1.2.3.4

Если бы был другой заказчик, который хотел бы, чтобы наши экземпляры поступали из другого адресного пространства, мы бы также добавили дополнительные строки DNAT. Например, если клиент в сети 10.0.0.0/16 хотел, чтобы наш трафик выглядел так, как если бы он исходил от 10.1.0.0/16, то мы могли бы: (a) SNAT все исходящие пакеты на IP-адрес в этом CIDR, например 10.1.0.1, и (b) DNAT их вернуться к 20.0.6.195 по возвращении.

$ iptables -t nat -A POSTROUTING -d 10.0.0.0/16   -j SNAT --to-source 10.1.0.1
$ iptables -t nat -A PREROUTING  -d 10.1.0.1 -j DNAT --to-destination 20.0.6.195

(Помимо обновления правил NAT, нам также потребуется обновить инфраструктуру AWS, как указано на схеме: добавить записи в таблицы маршрутизации подсетей и создать дополнительные VPN-соединения типа «сеть-сеть» и связать их с транзитным шлюзом. )

Тестирование

Мы можем протестировать нашу установку, смоделировав сеть Заказчика, используя Учебное пособие по AWS для создания StrongSwan Linux VPN. Мы создаем еще один VPC, представляющий сторону Заказчика, и устанавливаем для его подсети 172.31.0.0/16 CIDR. Затем мы следуем руководству, чтобы создать в нем экземпляр StrongSwan Linux. Мы создаем еще один тестовый экземпляр EC2 в том же VPC и настраиваем таблицу маршрутизации VPC для пересылки пакетов с адресом 20.0.0.0/16 на эластичный сетевой интерфейс (ENI) экземпляра StrongSwan. Кроме того, мы назначаем публичный IP-адрес экземпляру StongSwan.

Возвращаясь к нашему основному VPC, мы создаем соединение Site-to-Site VPN и устанавливаем общедоступный IP-адрес экземпляра StrongSwan в качестве пункта назначения. Мы устанавливаем остальную часть конфигурации, как рекомендовано в руководстве, и ждем, пока первый туннель не покажет, что он находится в состоянии UP.

В руководстве рекомендуется использовать протокол пограничного шлюза (BGP) при создании соединения Site-to-Site VPN. После настройки туннеля экземпляр StrongSwan будет автоматически настроен с подсетью 20.0.0.0/16 благодаря BGP. Однако наши пакеты будут приходить с 1.2.3.4 в качестве адреса отправителя, поэтому таблица маршрутизации BPG не будет знать, куда отправлять возвращаемые пакеты. Чтобы исправить это, мы подключаемся к экземпляру StrongSwan и редактируем файл конфигурации /etc/quagga/zebra.conf для демона BPG Zebra, чтобы добавить статический маршрут:

ip route 1.2.3.4/32 169.254.152.245

Затем мы перезапускаем демон BGP с помощью команды service zebra restart. Обратите внимание, что IP-адрес 169.254.152.245 в приведенной выше строке конфигурации является «внутренним IP-адресом» виртуального частного шлюза одного из двух туннелей IPsec, созданных VPN-подключением типа «сеть-сеть». У вас будет другой адрес, который вы можете найти в текстовом файле общей конфигурации, который можно загрузить с экрана Site-to-Site VPN Connection консоли AWS.

Затем мы подключаемся к тестовой версии EC2 в подсети 20.0.5.0/16 и проверяем связь с тестовым экземпляром в подсети 172.31.0.0/16 клиента,

[ec2-user@ip-20-0-5-210 ~]$ ping 172.31.38.197  > /dev/null & sudo tcpdump -eni any icmp
15:49:14.787108 Out ... 20.0.5.210 > 172.31.38.197: ICMP echo request, ...
15:49:14.854690  In ... 172.31.38.197 > 20.0.5.210: ICMP echo reply ...

Мы также можем наблюдать за трафиком, проходящим через экземпляр шлюза NAT:

ec2-user@ip-20-0-6-20:~$ sudo tcpdump -eni any icmp
20:39:46.084613  In … 172.31.38.197 > 1.2.3.4: ICMP echo request, id 26627, seq 1 ...
20:39:46.084657 Out … 1.2.3.4 > 172.31.38.197: ICMP echo reply, id 26627, seq 1 ...

Следующие полезные команды интерфейса командной строки AWS выводят все конфигурации для всех VPN-подключений типа "сеть-сеть". Они включают в себя все параметры туннеля, в частности секретные предварительные ключи (PSK).

$ aws ec2 describe-vpn-connections
$ aws ec2 describe-transit-gateways
$ aws ec2 describe-transit-gateway-attachments

Устранение неполадок туннеля IPSec

К сожалению, AWS до сих пор не разработал способ отладки фактического установления туннеля IPSec. Это потому, что AWS не предоставил никаких журналов этого этапа. Сценарий использования, который стремится решить AWS, - это подключение собственной локальной сети к собственному AWS VPC в облаке. В таком случае мы могли бы отладить локальную сторону подключения IPsec. Однако, если клиент является третьей стороной и соединение IPSec не удается, мы оставляем на милость третьей стороны для устранения проблемы.

Терраформная конфигурация транзитного шлюза

Следующие команды Terraform настраивают транзитный шлюз и помогают дополнительно проиллюстрировать все настройки. Для краткости опущены бирки с именами и некоторые другие разделы.

Сначала мы определяем транзитный шлюз и отключаем все маршруты по умолчанию.

resource "aws_ec2_transit_gateway" "example_transit_gateway" {
  amazon_side_asn = 64512
  auto_accept_shared_attachments = "disable"
  default_route_table_association = "disable"
  default_route_table_propagation = "disable"
  description = "Example Transit Gateway."
  vpn_ecmp_support = "disable"
}

Поскольку мы сказали AWS не создавать таблицу маршрутов по умолчанию для TGW, мы должны создать ее вручную следующим образом:

resource "aws_ec2_transit_gateway_route_table" "example_transit_gateway" {
  transit_gateway_id = aws_ec2_transit_gateway.example_transit_gateway.id
}

Транзитный шлюз имеет раздельную конфигурацию «маршрутов» и «вложений». Маршруты указывают вложение в качестве пункта назначения. Сначала мы создаем подключение к подсети VPC, в которой находится экземпляр EC2 шлюза NAT (здесь он называется private_subnet6),

resource "aws_ec2_transit_gateway_vpc_attachment" "nat_vpc_attachment" {
  vpc_id             = module.vpc.id
  subnet_ids         = [ aws_subnet.private_subnet6.id ]
  transit_gateway_id = aws_ec2_transit_gateway.example_transit_gateway.id
  transit_gateway_default_route_table_association = false
  transit_gateway_default_route_table_propagation = false
}

Затем мы добавляем маршрут, который сообщает TGW о пересылке пакетов, предназначенных для 1.2.3.4, в подсеть VPC, которую мы только что подключили:

resource "aws_ec2_transit_gateway_route" "nat-egress-ip" {
  destination_cidr_block = "1.2.3.4/32"
  transit_gateway_attachment_id  = aws_ec2_transit_gateway_vpc_attachment.nat_vpc_attachment.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.example_transit_gateway.id
}

Пока что мы обработали возврат пакетов. Теперь давайте займемся прямым направлением. Сначала мы определяем клиентский шлюз и VPN-соединение типа «сеть-сеть», а затем говорим TGW пересылать ему пакеты.

resource "aws_customer_gateway" "example_customer" {
  bgp_asn    = 64520
  ip_address = '6.7.8.9' # this would be the public IP of the StrongSwan instance during test
  type       = "ipsec.1"
}

resource "aws_vpn_connection" "example_customer" {
  customer_gateway_id = aws_customer_gateway.example_customer.id
  transit_gateway_id  = aws_ec2_transit_gateway.example_transit_gateway.id
  type                = aws_customer_gateway.example_customer.type
  static_routes_only  = false
}

resource "aws_ec2_transit_gateway_route_table_association" "example_customer" {
  count=length(local.vpn_attachments)
  transit_gateway_attachment_id  = aws_vpn_connection.example_customer.transit_gateway_attachment_id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.example_transit_gateway.id
}

resource "aws_ec2_transit_gateway_route" "example_customer" {
  count=length(local.vpn_attachments)
  destination_cidr_block         = "172.31.0.0/16"
  transit_gateway_attachment_id  = aws_vpn_connection.example_customer.transit_gateway_attachment_id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.account_transit_gateway.id
}

Конфигурация Terraform настраиваемого экземпляра шлюза NAT

Задача автоматической настройки экземпляра EC2 шлюза NAT заключается в том, чтобы (а) назначить эластичный общедоступный IP-адрес и (б) использовать назначенный частный IP-адрес в iptables правилах. Решение состоит в том, чтобы сначала определить эластичный сетевой интерфейс (ENI), а затем использовать его в определении экземпляра:

resource "aws_network_interface" "nat_gw" {
  source_dest_check = false # must be disabled for NAT to work
  subnet_id = module.vpc.sn-private-nat-az1
  security_groups = [ ... ]
}

resource "aws_eip_association" "nat_gw" {
  network_interface_id = aws_network_interface.nat_gw.id
  allocation_id = "1.2.3.4"
}

resource "aws_instance" "nat_gw" {
  network_interface {
    device_index = 0
    network_interface_id = aws_network_interface.nat_gw.id
  }

  ... 
  
 user_data = <<EOF
#!/bin/bash
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -F
iptables -t nat -A POSTROUTING -d 172.31.0.0/16   -j SNAT --to-source 1.2.3.4
iptables -t nat -A PREROUTING  -d 1.2.3.4 -j DNAT --to-destination ${aws_network_interface.nat_gw.private_ip}
EOF
}

Соображения безопасности

Поскольку мы используем NAT, доступ к экземпляру за экземпляром NAT GW из сети клиента невозможен. Однако сам шлюз NAT может быть доступен из сети клиента. Доступ к портам ограничен группой безопасности AWS подсети, в которой находится шлюз NAT. Например, следующие правила группы безопасности для папки «Входящие» позволят стороне клиента только проверять связь с экземпляром и делать к нему HTTPS-запросы:

Доступ также можно заблокировать, ограничив правило DNAT в iptables. После ограничения его протоколом icmp, как показано ниже, удаленная сторона по-прежнему сможет пинговать шлюз NAT на 1.2.3.4, но не сможет подключиться к нему по протоколу HTTPS.

iptables -t nat -A PREROUTING  -d 1.2.3.4 -p imcp -j DNAT ...

Последние мысли

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

Мы надеемся, что если больше компаний будут использовать TGW для подключения к внешним сетям с помощью NAT, то AWS будет поддерживать этот вариант использования непосредственно в настройках Site-to-Site VPN, чтобы не было необходимости поддерживать экземпляр EC2 для выполнения NAT. Мы также хотим, чтобы AWS предоставлял возможность ведения журнала Site-to-Site VPN об установлении туннеля IPsec VPN, чтобы помочь с устранением неполадок на этом этапе.