Шестой из семи уровней. Ты умеешь пускать трафик – теперь про то, как его фильтровать. Межсетевой экран (firewall) – не "стена", а решение для каждого пакета: пропустить, отбросить молча или отказать явно. Разница между DROP и REJECT однажды сэкономит тебе час отладки "зависшего" клиента.

"Межсетевой экран – не стена, а фильтр. Он не "блокирует всё", а решает для каждого пакета: пропустить (ACCEPT), отбросить молча (DROP) или отказать явно (REJECT)."


Откуда это пошло

1988 – первый сетевой червь (Morris Worm). Заразил ~6000 машин из ~60000 подключённых к интернету. Мир осознал: сети нужна защита.

1989–1992 – пакетные фильтры (packet filter firewalls). DEC, AT&T Bell Labs. Идея: проверять каждый пакет по набору правил (src/dst IP, порт, протокол). Если правило совпало – пропустить или заблокировать.

1998 – iptables (Rusty Russell). Замена ipchains, в ядре с Linux 2.4 (2001). Стал стандартом на 20 лет. Netfilter – подсистема ядра, iptables – интерфейс к ней.

2014 – nftables. Замена iptables: единый синтаксис, лучшая производительность, атомарные обновления. В 2026 году – стандарт в RHEL 9, Debian 12.

2017 – Kubernetes NetworkPolicy. Межсетевой экран как код: YAML вместо правил iptables. Calico, Cilium – реализации.


iptables – фундамент (до сих пор на собесах)

Архитектура: таблицы → цепочки → правила

Пакет проходит через цепочки Netfilter в строгом порядке – на каждой его можно отфильтровать или подменить ему адрес:

Пакет приходит
┌─────────────┐
│  PREROUTING │  ← DNAT (подменить dst IP)
└──────┬──────┘
  ┌────▼────┐   Для этого хоста?
  │ Routing │───── Нет ──→ FORWARD → POSTROUTING → наружу
  └────┬────┘                  (транзит: хост как роутер)
       │ Да
┌──────────────┐
│    INPUT     │  ← фильтрация входящего трафика
└──────┬───────┘
   [Приложение]
┌──────────────┐
│    OUTPUT    │  ← фильтрация исходящего трафика
└──────┬───────┘
┌──────────────┐
│ POSTROUTING  │  ← SNAT/MASQUERADE (подменить src IP)
└──────────────┘

Практическое правило: свои порты закрываешь в INPUT, а Docker/K8s и NAT живут в FORWARD и POSTROUTING. Транзитный трафик (когда хост работает маршрутизатором) минует INPUT/OUTPUT.

Базовые команды

# Показать все правила:
sudo iptables -L -n -v --line-numbers
# -L = list, -n = числовые адреса, -v = verbose, --line-numbers = номера

# Показать NAT-правила:
sudo iptables -t nat -L -n -v

# Разрешить ответы на уже установленные соединения – СТАВИТЬ ПЕРВЫМ
# (большинство пакетов попадёт сюда и не будет проверять правила ниже):
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# (-m state --state ESTABLISHED,RELATED – устаревший синоним, всё ещё работает)

# Разрешить SSH (порт 22):
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# Разрешить HTTP/HTTPS:
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# Заблокировать всё остальное (доходят только новые входящие, не покрытые выше):
sudo iptables -A INPUT -j DROP

# Удалить правило по номеру:
sudo iptables -D INPUT 3

# Сбросить все правила:
sudo iptables -F

Порядок правил – КРИТИЧЕСКИ ВАЖЕН

# ПРАВИЛА ПРОВЕРЯЮТСЯ СВЕРХУ ВНИЗ, ПЕРВОЕ СОВПАДЕНИЕ ПОБЕЖДАЕТ

# Правильно:
iptables -A INPUT -p tcp --dport 22 -j ACCEPT     # 1. SSH – ОК
iptables -A INPUT -p tcp --dport 80 -j ACCEPT     # 2. HTTP – ОК
iptables -A INPUT -j DROP                          # 3. Всё остальное – DROP

# Неправильно (заблокирует ВСЁ, включая SSH):
iptables -A INPUT -j DROP                          # 1. DROP всё ← !!! SSH тоже
iptables -A INPUT -p tcp --dport 22 -j ACCEPT     # 2. Никогда не достигнется

20/80: 90% iptables работы – это цепочка INPUT с ACCEPT для нужных портов и DROP/REJECT по умолчанию.


DROP против REJECT – почему это важно

# DROP – молча отбросить пакет:
iptables -A INPUT -p tcp --dport 8080 -j DROP
# Клиент: "Connection timed out" (ждёт 30+ секунд)
# Атакующий: не знает, есть ли хост

# REJECT – отправить RST/ICMP:
iptables -A INPUT -p tcp --dport 8080 -j REJECT
# Клиент: "Connection refused" (мгновенно)
# Атакующий: знает, что хост жив

# Рекомендация:
# - Внешний firewall (internet-facing): DROP (не раскрывать информацию)
# - Внутренний firewall (между сервисами): REJECT (быстрая диагностика)

Kubernetes NetworkPolicy

Без NetworkPolicy – всё открыто

По умолчанию в K8s: ЛЮБОЙ pod может общаться с ЛЮБЫМ pod-ом.
Это как офис без дверей – удобно, но небезопасно.

С NetworkPolicy – белый список (whitelist)

# Разрешить трафик к api ТОЛЬКО от frontend (namespace=prod):
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-allow-frontend
  namespace: prod
spec:
  podSelector:
    matchLabels:
      app: api                  # применяется к pod-ам с label app=api
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend     # разрешить от frontend
      ports:
        - protocol: TCP
          port: 8080

Типичные NetworkPolicy для DevOps

# 1. Deny all ingress (потом разрешать явно):
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-ingress
  namespace: prod
spec:
  podSelector: {}               # все pod-ы в namespace
  policyTypes:
    - Ingress                   # блокировать входящий
  # ingress: [] – пустой список = ничего не разрешено

---
# 2. Egress ТОЛЬКО на DNS. ВНИМАНИЕ: в одиночку эта политика отрежет ВЕСЬ
#    остальной исходящий трафик у выбранных pod-ов. Применять в паре с
#    default-deny-egress как "дырку" для DNS (иначе pod-ы не резолвят имена):
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
  namespace: prod
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - to: []
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

---
# 3. Разрешить трафик из monitoring namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-prometheus-scrape
  namespace: prod
spec:
  podSelector: {}
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: monitoring
      ports:
        - protocol: TCP
          port: 9090

Важно: NetworkPolicy работает только если CNI-плагин поддерживает их. Calico, Cilium – поддерживают. Flannel – НЕ поддерживает (политика создастся, но действовать не будет).


Security Groups (облака) – межсетевой экран для виртуальных машин (VM)

Правило Security Group (AWS)Эквивалент в iptables
Inbound: TCP 22 из 0.0.0.0/0iptables -A INPUT -p tcp --dport 22 -j ACCEPT
Inbound: TCP 80 из 0.0.0.0/0iptables -A INPUT -p tcp --dport 80 -j ACCEPT
Inbound: TCP 8080 из 10.0.0.0/16iptables -A INPUT -s 10.0.0.0/16 -p tcp --dport 8080 -j ACCEPT
Outbound: всё из 0.0.0.0/0iptables -A OUTPUT -j ACCEPT

Ключевое отличие от iptables:

  • Security Groups – с учётом состояния (stateful): если разрешён входящий TCP 80, ответные пакеты автоматически разрешены
  • iptables по умолчанию – без учёта состояния (stateless): нужно явно разрешать ESTABLISHED,RELATED

Диагностика межсетевого экрана – чеклист

# 1. Проверить, есть ли подключение БЕЗ firewall:
nc -zv target-host 8080
# timeout → firewall DROP или хост недоступен
# refused → порт не слушает (firewall не при чём)

# 2. Проверить iptables INPUT:
sudo iptables -L INPUT -n -v --line-numbers | grep 8080
# Если нет правила ACCEPT для порта → заблокировано

# 3. Проверить K8s NetworkPolicy:
kubectl get networkpolicy -n prod
kubectl describe networkpolicy api-allow-frontend -n prod

# 4. Снять блокировку для диагностики (ТОЛЬКО временно, ОПАСНО в production!):
sudo iptables -F INPUT           # сбросить ВСЕ правила цепочки INPUT
# Осторожно: "iptables -P INPUT ACCEPT" меняет лишь политику по умолчанию и НЕ
# поможет, если трафик режется явным правилом "-j DROP" (оно сработает раньше)

# 5. Логировать отбрасываемые пакеты. LOG не терминальный, поэтому пару
#    "LOG, затем DROP" добавляют в КОНЕЦ цепочки – LOG обязан идти ПЕРЕД DROP,
#    иначе пакет отбросится раньше, чем залогируется:
sudo iptables -A INPUT -j LOG --log-prefix "DROPPED: " --log-level 4
sudo iptables -A INPUT -j DROP
# Логи: dmesg | grep DROPPED

Подвохи для собеса

Подвох 1: "Чем iptables отличается от Security Groups?"

iptablesSecurity Groups
ГдеLinux-хостОблачная ВМ (AWS, GCP, Azure)
Учёт состоянияБез состояния (stateless)С учётом состояния (stateful)
УправлениеCLI (iptables -A)API / консоль
СохранностьНе сохраняется при перезагрузкеСохраняется всегда
ДетализацияПакет, интерфейс, цепочкаТолько правила входящего/исходящего
ПроизводительностьДеградирует при >10K правилОптимизировано облаком

На собесе: "iptables – межсетевой экран на уровне хоста (host-level) в ядре Linux, без учёта состояния (stateless) по умолчанию, требует явного разрешения ответных пакетов. Security Groups – облачный межсетевой экран, с учётом состояния (stateful), автоматически разрешает ответы. В K8s NetworkPolicy – ещё один уровень, работающий на уровне подов (pod-level) через CNI (Calico/Cilium)."


Подвох 2: "Почему после добавления NetworkPolicy pod перестал работать?"

Ответ:

NetworkPolicy – белый список (whitelist). Как только хотя бы одна NetworkPolicy применяется к pod-у, весь не-описанный трафик блокируется.

# Добавили deny-all → pod не может:
# - резолвить DNS (нет egress на UDP 53)
# - ходить к другим сервисам (нет egress rules)
# - получать health check от kubelet (нет ingress от node)

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

  1. Забыли разрешить DNS (UDP 53)
  2. Забыли разрешить проверку живости (health check) от kubelet
  3. Забыли разрешить egress к внешним сервисам
  4. Неправильный селектор меток (label selector) – политика не применяется к нужным pod-ам

На собесе: "NetworkPolicy – аддитивный белый список (additive whitelist). Первая политика блокирует всё неявное. Типичная ошибка – забыть DNS egress: pod не может резолвить имена и все HTTP-вызовы падают с "Name or service not known", хотя это проблема сети, а не DNS."


Код-челлендж

Задача: напиши Kubernetes NetworkPolicy, которая:

  1. Применяется к pod-ам с меткой (label) app: database в namespace prod
  2. Разрешает ingress ТОЛЬКО от pod-ов с меткой app: api на порту TCP 5432
  3. Разрешает ingress от Prometheus (namespace monitoring) на порту TCP 9187
  4. Блокирует весь остальной ingress
Решение
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: database-ingress
  namespace: prod
spec:
  podSelector:
    matchLabels:
      app: database
  policyTypes:
    - Ingress
  ingress:
    # Разрешить API → Database:
    - from:
        - podSelector:
            matchLabels:
              app: api
      ports:
        - protocol: TCP
          port: 5432
    # Разрешить Prometheus → Database (metrics):
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: monitoring
      ports:
        - protocol: TCP
          port: 9187
    # Всё остальное неявно заблокировано
    # (потому что policyTypes содержит Ingress)

Дальше → Уровень 6

Ты умеешь настраивать межсетевой экран: iptables для хостов, NetworkPolicy для K8s. Понимаешь DROP против REJECT, разницу "с учётом состояния" и "без" (stateful/stateless), подход белого списка (whitelist).

Но в проде трафик идёт не напрямую к pod-у. Между клиентом и приложением – балансировщик нагрузки (L4 или L7), обратный прокси (reverse proxy, nginx), Ingress-контроллер. В сервисной сетке (service mesh) – ещё и прокси-контейнер (sidecar, Envoy). Как всё это работает вместе?

→ Уровень 6: Балансировка нагрузки и сети Kubernetes