AWS: власний EC2 в ролі NAT Gateway замість AWS Managed NAT Gateway
5 (1)

Автор |  12/03/2026
Click to rate this post!
[Total: 1 Average: 5]

Подивився я на costs по інфраструктурі, яка описана в попередньому пості AWS: сетап базової інфраструктури для WordPress, і тяжко зітхнув:

Один NAT Gateway – це чверть витрат на AWS, і навіть маючи AWS Credits мене трошки душить жаба.

Є, звісно, варіант прибрати NAT Gateway зі схеми взагалі:

  • можна просто перевести EC2 в Public Subnet: насправді, описаний сетап з використанням AWS Load Balancer дійсно трохи overkill для проекту типу якогось невеликого блогу – але мені все ж хочеться мати “красиву” інфрастуктуру, побудовану “по канонам” та більш наближену до AWS Well-Architected Framework (свідомо без Reliability – тільки одна Availability Zone, та без infrastructure as code – див. AWS Well-Architected Framework Design Principles)
  • можна прибрати NAT Gateway взагалі – але залишити EC2 в Private Subnet:
    • по факту, в описаному сетапі NAT GW використовується тільки на самому початку, коли встановлюються пакети nginx, php та скачується архів з WordPress
    • але потім, по-перше – треба встановлювати апгрейди із зовнішніх репозиторіїв, по-друге – сам WordPress може ходити в інтернет при запуску своїх cron jobs або робити виклики з плагінів (цікавий приклад є в пості TCP/IP: SYN flood атака на сервер RTFM, та “Hacker News hug of death”, де плагін Page View Count постійно робив запити з серверу RTFM до Cloudflare)

Але є і третій варіант – це власний підняти “poor man’s NAT gateway“: просто запустити окремий мінімальний t3.nano EC2, на ньому мати Linux чи FreeBSD, а там налаштувати звичайний NAT.

Бо по суті AWS VPC можна розглядати по аналогії з домашньою мережею, де в ролі NAT Gateway виступає роутер – TP-Link, MikroTik тощо, і цей роутер можна замінити на будь-який ноутбук або ПК з Linux/FreeBSD та налаштувати там NAT з блекджеком і моніторингом.

В коментах до цього поста підсказали про проект fck-nat – вже готовий до використання AWS AMI, до того ж для fck-nat є Terraform модуль terraform-aws-fck-nat.

Плюси-мінуси

Звісно, для якогось production сетапу якогось реального проекту простіше і – головне – надійніше мати стандартний AWS Managed NAT Gateway: zero геморою з апгрейдами, zero проблем з availability – за все відповідає AWS.

Якщо ж мати власний NAT Gateway – то це додатковий час на встановлення апгрейдів, моніторинг, ну і якщо такий інстанс впаде – то інстанси в приватних сабнетах залишаться без доступу в Internet.

Ну і якщо робити автоматизацію – то описати з Terraform один чи кілька NAT Gateways набагато простіше, ніж описувати налаштування окремого EC2 – фактично, достатньо просто вказати один параметр у VPC module resource.

Але якщо подивитись на вартість…

А шо по грошам? 

Давайте порівняємо вартість AWS Managed NAT Gateway та власного NAT на t3.nano – бо тут є один цікавий нюанс.

NAT Gateway vs t3.nano: per-hour pricing

Перше, і саме відчутне – це погодинна оплата:

  • AWS Managed NAT Gateway: $0.048/година – це ~$32 в місяць
  • AWS EC2 t3.nano: $0.0059/год – це ~$4.3 на місяць

t3.nano в ролі NAT Gateway для якогось невеликого проекту – з головою: всі операції виконуються в ядрі системи, навантаження на CPU/RAM мінімальне. Єдине. на що варто звертати увагу, це network bandwidth – але для t3.nano маємо до 5 Gbps burst – з запасом, див. Amazon EC2 instance network bandwidth.

NAT Gateway vs t3.nano: per-gigabyte pricing

Другий важливий момент – це вартість трафіку, бо:

  • AWS Managed NAT Gateway: тут ми платимо за кожен processed bytes, тобто і ingress (з інтернету – до NAT Gateway – потім всередину VPC), і egress (трафік із VPC в інтернет)
    • ціна $0.045/GB
    • тут окремо уточню:
      • processed bytes рахується для трафіку, який був ініційований з приватного сабнету назовні, тобто коли EC2 робить запит назовні – тоді рахується і запит (egress) і відповідь (ingress)
      • трафік, який NAT Gateway отримує ззовні напряму – в мережу не проходить і не рахується, бо NAT GW не приймає вхідних з’єднань
  • AWS EC2 instance: платимо тільки за egress
    • ціна $0.09/GB

І тут насправді AWS Managed NAT Gateway може бути вигіднішим: якщо у нас трафік переважно out, egress – то ми за нього платимо $0.045, а не $0.09 як при використанні EC2 – у два рази дешевше.

Втім, враховуючи різницю в оплаті per hour, то якщо ми відправляємо менше ~644 GB на місяць – вигода по трафіку AWS Managed NAT Gateway “з’їдається” ціною інстансу:

  • різниця вартості AWS Managed NAT Gateway та t3.nano == $28 на місяць
  • на кожному переданому у “світ” гігабайті AWS Managed NAT Gateway економить нам $0.045
  • $28 / $0.045 приблизно дає 622 GB/міс

Тобто, якщо ми передаємо менше ніж 622 – то за рахунок різниці у вартості інстансів загальна ціна за місяць при використанні власного EC2 з типом t3.nano все одно вийде менша.

Але якщо ми віддаємо в інтернет більше ніж 622 гігабайт – то AWS Managed NAT Gateway стає вигіднішим за рахунок своєї ціни за трафік.

Втім, якщо ми плануємо інфрастуктуру для невеликого веб-сайту чи особистого блогу – що в місяць можна на 622 гігабайт передавати?

Трафік від/до клієнтів піде тільки через ALB, а через NAT Gateway – тільки апгрейди операційної системи та пакетів.

План дій

Отже, що будемо робити:

  • запустимо EC2 в публічній мережі
  • Security Group:
    • дозволяємо тільки out трафік – на вхід з інтернету нічого не має приходити
    • дозволяємо весь ingress з Private Subnets – наш EC2 буде передавати дані назовні
    • дозволяємо SSH з VPC або користуємось EC2 Instance Connect
  • на EC2 маємо якийсь Linux, на якому з iptables налаштовуємо NAT
  • в Route Tables для VPC і Private Subnets налаштуємо default route – 0.0.0.0/0 через Private IP цього EC2

Вибір операційної системи – в принципі, будь-який Linux, тут будемо робити з Amazon Linux, бо дійсно менше геморою ніж з Debian.

Хоча спочатку взагалі думав про FreeBSD – бо FreeBSD традиційно сильна саме в networking, але як вже основний сервер для самого блогу на Amazon Linux, то не буду розводити зоопарк.

На Amazon Linux 2023 по-дефолту встановлений nftables – але iptables теж є як wrapper, встановлюється з репозиторію окремим пакетом.

Створення NAT Gateway Security Group

Переходимо в VPC, додаємо нову групу:

Створення EC2

Задаємо ім’я, вибираємо операційну систему, тут буде Amazon Linux:

Вибираємо тип інстансу, вибираємо або створюємо ключ для SSH:

В Network settings вибираємо VPC, в Subnet вказуємо той, в якому “main EC2” з блогом – аби уникнути cross Availability Zone traffic (див. Overview of Data Transfer Costs for Common Architectures), та включаємо Auto-assign public IP – бо NAT Gateway повинен мати Public IP, на який буде отримувати пакети від клієнтів в інтернеті:

Тут все – запускаємо створення інстансу.

Після старту робимо важливу зміну в його налаштуваннях – треба виключити перевірку Source/Destination check: вона вказує EC2 приймати і відправляти тільки той трафік, де source або destination співпадає з його власним IP.

Для NAT це треба вимкнути, бо NAT instance по визначенню пересилає чужий трафік, де ні source, ні destination не є його власним IP:

Задаємо Stop:

Налаштування Linux в ролі NAT Gateway

Підключаємось до EC2 (EC2 Instance Connect робили в попередньому пості):

[setevoy@setevoy-work ~]  $ aws --region eu-west-1 --profile setevoy ec2-instance-connect ssh --instance-id i-0e54f36ce6bb90da4 --connection-type eice
...
[ec2-user@ip-10-0-1-79 ~]$

Перевіряємо параметр net.ipv4.ip_forward, який задає ядру системи дозвіл на пересилання пакетів між мережевими інтерфейсами – саме це і є основою будь-якого NAT і роутингу:

[ec2-user@ip-10-0-1-79 ~]$ sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 0

0 – відключений, вмикаємо його:

[ec2-user@ip-10-0-1-79 ~]$ sudo sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1

Зміни з sysctl -w застосовуються зараз, але після перезавантаження системи зникнуть, тому додаємо в конфіг /etc/sysctl.conf:

[ec2-user@ip-10-0-1-79 ~]$ echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
net.ipv4.ip_forward = 1

Перечитуємо конфіг, застосовуємо зміни:

[ec2-user@ip-10-0-1-79 ~]$ sudo sysctl -p
net.ipv4.ip_forward = 1

Перевіряємо ще раз:

[ec2-user@ip-10-0-1-79 ~]$ sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

Налаштування iptables та MASQUERADE

Можна зробити з nftables, але iptables частіше зустрічається, та і більш звично.

Детальніше про iptables писав ще у 2014 році (OMG!), див. Linux: IPTABLES – руководство: часть 1 – основы IPTABLES – кардинальних змін в основах не було, хоча сам iptables вже поступово замінюється на nftables.

Перевіряємо імена інтерфейсів:

[ec2-user@ip-10-0-1-79 ~]$ ip a s
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
...
2: enX0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP group default qlen 1000
...

Встановлюємо пакет iptables:

[ec2-user@ip-10-0-1-79 ~]$ sudo dnf install -y iptables

Додаємо правило:

[ec2-user@ip-10-0-1-79 ~]$ sudo iptables -t nat -A POSTROUTING -o enX0 -j MASQUERADE

Тут:

  • -t nat: вносимо зміни в таблицю nat
  • -A POSTROUTING: додаємо правило в POSTROUTING chain – тобто правило спрацьовує після того, як ядро вже вирішило куди відправити пакет – але ще не відправило отримувачу
  • -o enX0: правило тільки для пакетів що виходять через інтерфейс enX0 (наш публічний інтерфейс)
  • -j MASQUERADE: підміняємо source IP пакету (Private IP інстансу EC2, на якому буде блог) на IP інтерфейсу enX0 – публічний IP нашого інстансу EC2 з NAT Gateway

Перевіряємо правила:

[ec2-user@ip-10-0-1-79 ~]$ sudo iptables -t nat -L POSTROUTING -n -v
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 MASQUERADE  all  --  *      enX0    0.0.0.0/0            0.0.0.0/0

Аби правило зберігалось при рестартах інстансу – додаємо (вже має бути) пакет iptables-services, зберігаємо правила у файл /etc/sysconfig/iptables:

[ec2-user@ip-10-0-1-79 ~]$ sudo dnf install -y iptables-services

[ec2-user@ip-10-0-1-79 ~]$ sudo service iptables save

[ec2-user@ip-10-0-1-79 ~]$ sudo systemctl enable iptables

Зміни в AWS VPC Route Tables

У нас є окремі таблиці маршрутизації на кожну Private Subnet:

В яких зараз маршрут до інтернету, тобто 0.0.0.0/0, заданий через AWS Managed NAT Gateway:

Додаємо нове правило, вказуємо новий EC2 інстанс:

Видаляємо маршрут через NAT gateway – тут можливий короткий downtime, бо активні підключення із VPC в інтернет будуть розірвані:

Повторюємо зміни для обох Route Tables, і перевіряємо.

Підключаємось на EC2, дивимось результат ifconfig.me – і отримуємо Public IP нашого нового self-managed NAT Gateway:

[ec2-user@ip-10-0-3-146 ~]$ curl -s ifconfig.me
108.130.182.54

Власне, на цьому і все.

Можна видаляти AWS Managed NAT Gateway:

Bonus: Amazon Linux auto-upgrades

Ну і аби не ходити на інстанс з ручними апгрейдами – налаштуємо аналог unattended-upgrades з Debian.

В RHEL/CentOS/Fedora/Amazon Linux це пакет dnf-automatic:

[ec2-user@ip-10-0-1-79 ~]$ sudo dnf install -y dnf-automatic

Редагуємо конфіг /etc/dnf/automatic.conf, мінімально – задаємо apply_updates = yes.

В upgrade_type можна задати security, а не встановлювати всі – бо це все ж gateway:

...
# default                            = all available upgrades
# security                           = only the security upgrades
upgrade_type = security

...

apply_updates = yes
...

[email]
# TODO in the following rtfm.co.ua posts about setting up AWS

Про відправку email з AWS EC2 інстансів може буду писати окремо в частині по налаштуванню AWS Simple Email Service.

Додаємо запуск по крону за розкладом:

[ec2-user@ip-10-0-1-79 ~]$ sudo systemctl enable --now dnf-automatic.timer
Created symlink /etc/systemd/system/timers.target.wants/dnf-automatic.timer → /usr/lib/systemd/system/dnf-automatic.timer.

Готово.

Loading