Перейти до змісту

NAT і iptables: як контейнер бачить інтернет

NAT і iptables: як контейнер бачить інтернет

Written by:

Igor Gorovyy
DevOps Engineer Lead & Senior Solutions Architect

LinkedIn


У попередній частині ми дали контейнеру IP-адресу через bridge і veth. Але 10.20.0.x - це приватна адреса, яку не маршрутизує жоден роутер. Щоб контейнер міг вийти в інтернет, потрібні дві речі: IP forwarding і NAT.

Два кроки до інтернету

// В функції ensureBridge():

// 1. Вмикаємо IP forwarding
os.WriteFile("/proc/sys/net/ipv4/ip_forward", []byte("1"), 0644)

// 2. NAT для вихідного трафіку
run("iptables", "-t", "nat", "-A", "POSTROUTING",
    "-s", BridgeSubnet, "-j", "MASQUERADE")

Два рядки коду. Розберемо, що вони роблять.

IP forwarding

За замовчуванням Linux не передає пакети між мережевими інтерфейсами. Записуючи 1 в /proc/sys/net/ipv4/ip_forward, ми говоримо ядру: "пересилай пакети з одного інтерфейсу на інший".

Без цього пакет від контейнера (10.20.0.2) дійде до bridge (sheep0), але далі не піде.

MASQUERADE

MASQUERADE - це тип NAT, який замінює source IP пакета на IP вихідного інтерфейсу хоста. Коли пакет від контейнера виходить в інтернет, його source IP змінюється з 10.20.0.2 на IP хоста (наприклад, 192.168.1.100). Відповідь повертається на хост, а ядро знає, що її потрібно переслати контейнеру.

Шлях пакета

sequenceDiagram
    participant C as Контейнер<br/>10.20.0.2
    participant BR as sheep0 bridge<br/>10.20.0.1
    participant NAT as iptables NAT
    participant ETH as eth0 хоста<br/>192.168.1.100
    participant NET as Інтернет

    C->>BR: src: 10.20.0.2, dst: 8.8.8.8
    BR->>NAT: IP forwarding
    NAT->>ETH: src: 192.168.1.100, dst: 8.8.8.8<br/>(MASQUERADE)
    ETH->>NET: пакет в інтернет

    NET->>ETH: src: 8.8.8.8, dst: 192.168.1.100
    ETH->>NAT: conntrack: це відповідь контейнеру
    NAT->>BR: src: 8.8.8.8, dst: 10.20.0.2<br/>(де-MASQUERADE)
    BR->>C: пакет доставлений

Ядро відстежує з'єднання через conntrack (connection tracking). Воно пам'ятає: "пакет з 10.20.0.2:12345 на 8.8.8.8:80 вийшов через eth0 як 192.168.1.100:54321". Коли приходить відповідь на 192.168.1.100:54321, conntrack повертає все назад.

Чому MASQUERADE, а не SNAT?

SNAT (Source NAT) вимагає вказати конкретний IP: -j SNAT --to-source 192.168.1.100. MASQUERADE автоматично бере IP вихідного інтерфейсу. Це зручніше, якщо IP хоста може змінитися (DHCP, cloud instances).

Але тут є проблема: MASQUERADE повільніший за SNAT, тому що при кожному пакеті перевіряє IP інтерфейсу. Для продакшну з тисячами контейнерів краще SNAT. Для навчального проекту - MASQUERADE зручніший.

Маршрут за замовчуванням

У попередній частині ми налаштували маршрут всередині контейнера:

nsRun(pid, "ip", "route", "add", "default", "via", BridgeGateway)

Це говорить контейнеру: "все, що не в локальній мережі, відправляй на 10.20.0.1 (bridge)". А bridge вже знає, що робити далі.

Комунікація між контейнерами

Контейнери в одній підмережі (10.20.0.0/16) можуть спілкуватися напряму через bridge. Пакет від 10.20.0.2 до 10.20.0.3 не виходить з bridge, тому що bridge працює як switch - пересилає пакети між підключеними портами.

graph LR
    C1["Container 1<br/>10.20.0.2"] --- BR["sheep0 bridge"]
    C2["Container 2<br/>10.20.0.3"] --- BR
    C1 -.->|"пряма комунікація<br/>через bridge"| C2

Що не реалізовано

У Docker є більше:
- Port mapping (-p 8080:80) через iptables DNAT
- Кілька мереж (bridge networks) для ізоляції
- DNS resolution між контейнерами за іменами
- Network policies

Наша мережа проста: один bridge, одна підмережа, NAT для виходу в інтернет. Контейнери бачать один одного за IP. Цього достатньо, щоб зрозуміти, як працює container networking.

Спробуй сам

# Подивись iptables NAT rules:
sudo iptables -t nat -L POSTROUTING -n
# Перевір ip_forward:
cat /proc/sys/net/ipv4/ip_forward
# Зсередини контейнера спробуй ping:
ping -c 1 8.8.8.8

Мережа працює. Далі - Image Management: як tar-архів стає файловою системою контейнера.

Ресурси

Попередня: Bridge Networking | Наступна: Image Management