VPC и правила файрвола

Разбираемся, как в Google Cloud устроена сеть: где живут виртуальные машины, кто может до них достучаться и почему один неаккуратный флаг открывает базу данных всему интернету.

VPC (Virtual Private Cloud) — ваша собственная виртуальная сеть внутри Google Cloud: изолированное адресное пространство, в котором ресурсы видят друг друга по приватным IP, а всё остальное решают правила файрвола.

Когда вы создаёте виртуальную машину, она не висит в вакууме. У неё есть сетевая карта, приватный IP-адрес, соседи по подсети и набор правил, которые разрешают или запрещают трафик. В своём датацентре за это отвечал бы админ с железным свитчом; в облаке — вы, парой команд gcloud. И это одновременно и удобно, и опасно: правило, которое открывает SSH всему миру, пишется на десять символов короче, чем правило, которое открывает его только вам.

Зачем вообще думать о сети

Типичный сценарий: приложение на виртуальной машине, база данных рядом, и очень не хочется, чтобы база торчала наружу. Практических задач ровно три:

  • Изоляция. База должна отвечать только приложению, а не всему интернету. В облаке за это отвечает не «отдельный сервер за забором», а сеть и правила файрвола.
  • Контролируемый вход. Веб-порты 80/443 открыты всем, SSH — только вам, всё остальное закрыто по умолчанию.
  • Контролируемый выход. Машины без внешнего IP должны уметь скачивать пакеты и обновления — но не быть доступными снаружи.

Хорошая новость: сама VPC, подсети и правила файрвола в Google Cloud ничего не стоят. Платите вы за трафик наружу (egress) и за некоторые сетевые сервисы вроде Cloud NAT и балансировщиков. Входящий трафик бесплатен.

Как устроена VPC: глобальная сеть и региональные подсети

Здесь Google отличается от AWS, и это важно понять сразу. В GCP VPC — глобальная: одна сеть охватывает все регионы планеты. Внутри неё живут подсети (subnets), и вот они уже региональные — у каждой свой диапазон адресов и свой регион.

Что это даёт на практике: машина в europe-west1 и машина в us-central1, находящиеся в одной VPC, общаются по приватным адресам, без выхода в интернет и без VPN-туннелей между регионами. Никаких peering-плясок, как в других облаках.

# Создаём VPC с ручным режимом подсетей (custom) — так делают в проде.
# auto-режим создаёт подсети сразу во всех регионах, а это лишние адреса и лишний риск.
gcloud compute networks create app-vpc --subnet-mode=custom

# Подсеть для веб-слоя в Бельгии
gcloud compute networks subnets create web-subnet \
  --network=app-vpc \
  --region=europe-west1 \
  --range=10.10.0.0/24

# Подсеть для базы данных — отдельный диапазон
gcloud compute networks subnets create db-subnet \
  --network=app-vpc \
  --region=europe-west1 \
  --range=10.20.0.0/24

10.10.0.0/24 — это CIDR-нотация: диапазон приватных адресов, где /24 означает, что первые 24 бита адреса зафиксированы, а под хосты остаётся 8 бит. Считаем, сколько машин влезет (Google резервирует в каждой подсети 4 адреса — сетевой, шлюз и два служебных):

import ipaddress

subnet = ipaddress.ip_network("10.10.0.0/24")
print("Подсеть:", subnet)
print("Всего адресов:", subnet.num_addresses)
print("Доступно для VM:", subnet.num_addresses - 4)

# Диапазон, из которого приходит Google IAP — пригодится ниже
iap = ipaddress.ip_network("35.235.240.0/20")
for ip in ["35.235.243.17", "203.0.113.9"]:
    addr = ipaddress.ip_address(ip)
    print(ip, "-> из диапазона IAP" if addr in iap else "-> чужой адрес")

Результат:

Подсеть: 10.10.0.0/24
Всего адресов: 256
Доступно для VM: 252
35.235.243.17 -> из диапазона IAP
203.0.113.9 -> чужой адрес

Приватные и публичные адреса

У каждой VM всегда есть внутренний (приватный) IP из её подсети. Внешний (публичный) IP — опция, и по умолчанию его лучше не давать.

ЧтоВнутренний IPВнешний IP
Кто видиттолько ресурсы вашей VPCвесь интернет
Нужен ливсегда, выдаётся автоматическитолько если сервис публичный
Стоит ли денегнетда, почасово — и зарезервированный, но неиспользуемый статический IP стоит дороже используемого
Выход в интернетчерез Cloud NATнапрямую

Машина без внешнего IP не может сама сходить в интернет — а ей это нужно, чтобы поставить пакеты или сходить в чужой API. Для этого есть Cloud NAT: шлюз, который выпускает трафик наружу, но не пускает никого внутрь. Это ровно та схема, которую вы хотите для бэкенда.

gcloud compute routers create nat-router \
  --network=app-vpc --region=europe-west1

gcloud compute routers nats create app-nat \
  --router=nat-router --region=europe-west1 \
  --auto-allocate-nat-external-ips \
  --nat-all-subnet-ip-ranges

Cloud NAT — платный: берут почасовую плату за шлюз плюс за обработанные гигабайты. Для учебного проекта это копейки, но забытый NAT в пустом проекте всё равно капает в счёт.

Правила файрвола: главное — теги

Файрвол в GCP работает на уровне VPC, но применяется к каждой машине отдельно. Правило состоит из четырёх частей: направление (ingress — входящий, egress — исходящий), приоритет, кого касается (target) и откуда/куда (source/destination) плюс порты.

Ключевая идея, которую новички упускают: цель правила задаётся не IP-адресом машины, а сетевым тегом — произвольной меткой вроде web или db. Вы вешаете тег на VM, и правило автоматически на неё распространяется. Пересоздали машину, добавили десять новых — правила менять не нужно, достаточно повесить тот же тег.

# HTTP/HTTPS — всему интернету, но только машинам с тегом web
gcloud compute firewall-rules create allow-web \
  --network=app-vpc --direction=INGRESS --priority=1000 \
  --action=ALLOW --rules=tcp:80,tcp:443 \
  --source-ranges=0.0.0.0/0 --target-tags=web

# Postgres — только от машин с тегом web к машинам с тегом db.
# Источник — не адрес, а ТЕГ: так правило переживает пересоздание VM.
gcloud compute firewall-rules create allow-web-to-db \
  --network=app-vpc --direction=INGRESS --priority=1000 \
  --action=ALLOW --rules=tcp:5432 \
  --source-tags=web --target-tags=db

# SSH — только из диапазона Google IAP, а не из 0.0.0.0/0
gcloud compute firewall-rules create allow-ssh-iap \
  --network=app-vpc --direction=INGRESS --priority=1000 \
  --action=ALLOW --rules=tcp:22 \
  --source-ranges=35.235.240.0/20 --target-tags=ssh-allowed

Тег на машину вешается при создании (или потом через gcloud compute instances add-tags):

gcloud compute instances create web-1 \
  --zone=europe-west1-b --subnet=web-subnet \
  --tags=web,ssh-allowed \
  --no-address        # без внешнего IP: наружу — через Cloud NAT

# Заходим по SSH через IAP-туннель: внешний IP не нужен вообще
gcloud compute ssh web-1 --zone=europe-west1-b --tunnel-through-iap

Типичная безопасная схема

  1. Веб-слой (тег web): принимает 80/443 из 0.0.0.0/0. Обычно — за балансировщиком, тогда трафик пускают только из диапазонов health-check и балансировщика 130.211.0.0/22 и 35.191.0.0/16.
  2. Слой данных (тег db): принимает 5432 только от тега web. Внешнего IP нет.
  3. Доступ администратора: SSH только через IAP (35.235.240.0/20) или через один bastion-хост. Порт 22 в интернет не открыт нигде.
  4. Выход наружу: Cloud NAT на приватную подсеть.

Как это работает

Файрвол GCP — не отдельная «железка на входе в сеть», а распределённый фильтр: правила применяются прямо на виртуальном сетевом интерфейсе каждой VM, ещё до того, как пакет доедет до гостевой ОС. Отсюда три следствия, которые надо держать в голове:

  • Правила stateful. Разрешили входящее соединение — ответный трафик пойдёт автоматически, обратное правило писать не нужно.
  • Есть неявные правила. В любой VPC действуют два невидимых правила с приоритетом 65535: разрешить весь исходящий и запретить весь входящий. То есть по умолчанию сеть закрыта на вход — и всё, что вы пишете, это исключения из запрета.
  • Приоритет решает всё. Число от 0 до 65535, по умолчанию 1000; меньше — важнее. Правила проверяются от самого приоритетного; первое совпавшее применяется, дальше движок не смотрит. Если приоритеты равны и правила конфликтуют, побеждает DENY.

Проверить, что реально применится к машине, можно так:

gcloud compute firewall-rules list \
  --format="table(name,network,direction,priority,sourceRanges.list(),targetTags.list(),allowed[].map().firewall_rule().list())"

Частые ошибки

  • Сеть default и её подарки. В новом проекте автоматически создаётся сеть default с правилами default-allow-ssh (tcp:22 из 0.0.0.0/0!), default-allow-rdp, default-allow-icmp и default-allow-internal. Люди поднимают VM «по-быстрому», она попадает в default — и SSH-порт уже смотрит в интернет. Боты находят его за минуты. Для реального проекта создавайте свою VPC, а из default лишние правила удаляйте.
  • --source-ranges=0.0.0.0/0 на админ-порт. Самая дорогая опечатка в облаке. Открытый 22/3389/5432/6379/27017 для всего интернета — это не «риск», это вопрос дней. Скомпрометированную VM обычно используют для майнинга, и счёт за неё приходит вам.
  • Забыли тег. Правило написано на --target-tags=web, а на машине тега нет. Трафик не идёт, а вы час дебажите nginx. Первое, что проверяем при «сайт не открывается»: gcloud compute instances describe web-1 --format="value(tags.items)".
  • Правило создали не в той VPC. Правила принадлежат конкретной сети. Забыли --network=app-vpc — правило легло в default и на ваши машины не действует.
  • Пересекающиеся диапазоны подсетей. Внутри одной VPC CIDR-диапазоны подсетей не должны пересекаться, и особенно больно это выстреливает потом, при VPN или peering с офисной сетью, где тоже 10.0.0.0/8. Планируйте адресацию заранее.
  • Ошибки, стоящие денег. Внешние IP тарифицируются, причём зарезервированный и никем не используемый статический адрес стоит дороже работающего — Google так штрафует за удержание дефицитных адресов. Второй счёт-сюрприз — egress: исходящий трафик в интернет и между регионами платный (входящий — нет). Гоняете гигабайты между europe-west1 и us-central1 — платите за каждый.
  • Логи файрвола выключены. По умолчанию отброшенные пакеты нигде не видно. Включайте --enable-logging на важных правилах — но помните, что логи идут в Cloud Logging и объём тоже стоит денег.

Итоги

  • VPC в GCP — глобальная, подсети — региональные; машины из разных регионов одной VPC общаются по приватным адресам.
  • Входящий трафик закрыт неявным правилом deny — всё, что вы пишете, это осознанные исключения.
  • Цель правила задавайте сетевым тегом, а не IP: правила переживут пересоздание машин.
  • Приоритет 0–65535, меньше — важнее; при равенстве побеждает DENY. Правила stateful — обратное правило не нужно.
  • Бэкенду не нужен внешний IP: выход наружу — через Cloud NAT, вход админа — через IAP-туннель.
  • Сама VPC и правила бесплатны; деньги едят внешние IP, egress-трафик и Cloud NAT.
Проверьте себя
1. В новом проекте вы создали VM и написали правило файрвола с --target-tags=web, но сайт не открывается. Что проверить в первую очередь?
AПовешен ли тег web на саму машину и в той ли VPC создано правило
BХватает ли машине оперативной памяти
CСоздано ли обратное правило для исходящего ответного трафика
DНе закончились ли адреса в подсети
2. Какое поведение действует в VPC по умолчанию, если вы не создали ни одного правила?
AВесь трафик разрешён в обе стороны
BВесь трафик запрещён в обе стороны
CВходящий запрещён, исходящий разрешён
DВходящий разрешён, исходящий запрещён