1/50
Wirtualizacja sieci w Linux: bridge, VETH, Docker networking

Architektury sieci wirtualnych w systemie Linux

Prezentacja dotyczy zaawansowanych mechanizmów wirtualizacji sieci w systemie Linux, ze szczególnym uwzględnieniem mostków sieciowych (bridge), wirtualnych par Ethernet (VETH) oraz sieci w środowisku Docker. Materiał obejmuje zarówno podstawy teoretyczne, jak i praktyczne laboratorium.

Wirtualizacja sieci stanowi fundament nowoczesnych centrów danych, umożliwiając izolację ruchu, elastyczne zarządzanie przepustowością oraz separację dzierżawców w środowiskach wielodostępnych. W ramach wykładu przeanalizujemy działanie mostka L2 w przestrzeni jądra oraz sposoby łączenia odizolowanych przestrzeni nazw sieciowych.

Szczególna uwaga poświęcona jest Dockerowi i jego stosowi sieciowemu: od trybu bridge przez host aż po mechanizm publikowania portów z użyciem DNAT w iptables. Każdy mechanizm jest ilustrowany rzeczywistymi poleceniami i przykładem wyjścia.

Schemat architektury wirtualizacji sieci w systemie Linux

W dzisiejszych środowiskach chmurowych i kontenerowych wirtualizacja sieci stała się nieodzownym elementem infrastruktury IT. System Linux oferuje bogaty zestaw narzędzi wbudowanych bezpośrednio w jądro, które pozwalają na tworzenie zaawansowanych topologii sieciowych bez konieczności stosowania dedykowanego sprzętu sieciowego.

Mostek sieciowy (bridge) to wirtualny przełącznik L2 działający w przestrzeni jądra, który potrafi uczyć się adresów MAC i podejmować decyzje o przekazywaniu ramek na podstawie tablicy forwardingowej. W przeciwieństwie do przełączników fizycznych, mostek Linux jest w pełni programowalny i może być zarządzany przez narzędzia takie jak iproute2, bridge czy brctl.

Pary VETH (Virtual Ethernet) to wirtualne kable łączące dwa interfejsy w różnych przestrzeniach nazw. Są one fundamentalne dla izolacji sieciowej w kontenerach i maszynach wirtualnych. Każda para VETH działa jak przewód z dwoma końcami: ramka wysłana na jednym końcu pojawia się na drugim.

Docker rozszerza te mechanizmy o warstwę zarządzania sieciami, oferując gotowe sterowniki sieciowe takie jak bridge, host, overlay i macvlan. Najpopularniejszym trybem jest mostek docker0, który automatycznie tworzy prywatną podsieć dla kontenerów i konfiguruje reguły iptables do translacji adresów.

Zrozumienie tych mechanizmów jest kluczowe dla każdego administratora pracującego z kontenerami, ponieważ pozwala na świadome projektowanie architektury sieciowej, debugowanie problemów z łącznością oraz optymalizację wydajności.

2/50
Cel warsztatu

Cele dydaktyczne i praktyczne

  • Zrozumienie koncepcji Software Defined Networking w kontekście wbudowanych mechanizmów jądra Linux
  • Opanowanie budowy i konfiguracji mostka sieciowego L2 za pomocą narzędzi iproute2 oraz bridge
  • Praktyczna umiejętność tworzenia par VETH i łączenia przestrzeni nazw sieciowych (network namespaces)
  • Dogłębna analiza trybów sieciowych Dockera: bridge, host, none oraz sposobów ich konfiguracji
  • Zrozumienie mechanizmu publikowania portów w Dockerze opartego na DNAT w iptables
  • Umiejętność debugowania problemów sieciowych w środowiskach kontenerowych z użyciem tcpdump, ip, bridge i nsenter
Lista celów warsztatu

Pierwszym celem jest zrozumienie, w jaki sposób Linux implementuje funkcje przełącznika warstwy 2 bezpośrednio w jądrze systemu. Większość administratorów zna pojęcie przełącznika sieciowego jako urządzenia fizycznego, ale mało kto zdaje sobie sprawę, że to samo można zrealizować w czystym oprogramowaniu z wydajnością bliską sprzętowej.

Drugim celem jest praktyczna umiejętność konfiguracji mostka za pomocą poleceń takich jak ip link add type bridge oraz bridge fdb add. Uczestnik powinien być w stanie samodzielnie zbudować wirtualną sieć składającą się z wielu przestrzeni nazw połączonych mostkiem.

Trzeci cel to opanowanie koncepcji VETH i network namespaces. Uczestnik nauczy się tworzyć izolowane stosy sieciowe i łączyć je kablami wirtualnymi na różne sposoby: bezpośrednio, przez mostek, a także z użyciem trasowania.

Czwarty cel to dogłębna znajomość sieci Docker. Uczestnik przeanalizuje strukturę mostka docker0, reguły iptables tworzone automatycznie przez Dockera, oraz zrozumie różnice pomiędzy poszczególnymi trybami sieciowymi.

Piąty cel to mechanizm -p w Dockerze, który jest najczęściej używanym, a jednocześnie najmniej rozumianym aspektem sieci kontenerowej. Zrozumienie DNAT i masquerade pozwoli uczestnikowi na samodzielne debugowanie problemów z dostępem do usług w kontenerach.

Ostatni cel to praktyczne debugowanie. Uczestnik pozna narzędzia takie jak tcpdump do przechwytywania pakietów, nsenter do wchodzenia do przestrzeni nazw procesu, oraz bridge do przeglądania tablicy FDB.

3/50
Agenda

Plan prezentacji i laboratorium

  1. Ewolucja od przełączników fizycznych do SDN
  2. Linux Bridge: wirtualny przełącznik L2
  3. Budowa i konfiguracja mostka sieciowego
  4. VETH i Network Namespaces
  5. łączenie przestrzeni nazw przez VETH pair
  6. Wprowadzenie do sieci w Dockerze
  7. Tryby sieciowe Docker: none, host, bridge
  8. Port Publishing w Dockerze i DNAT
  9. Laboratorium: ręczna symulacja sieci Docker
  10. Podsumowanie: troubleshooting i cheat sheet
Plan prezentacji

Agenda została zaplanowana w taki sposób, aby stopniowo budować wiedzę od podstaw teoretycznych do zaawansowanych koncepcji praktycznych. Pierwsza część wykładu (slajdy 4-12) poświęcona jest ewolucji sieci komputerowych i mostkowi Linux. Zrozumienie tych fundamentów jest niezbędne do dalszej nauki.

Druga część (slajdy 13-20) wprowadza pary VETH i przestrzenie nazw sieciowych. To kluczowe koncepcje dla zrozumienia izolacji w kontenerach. Uczestnik nauczy się tworzyć odizolowane środowiska sieciowe i łączyć je na różne sposoby.

Trzecia część (slajdy 21-30) koncentruje się na sieciach Docker. Przeanalizujemy wszystkie tryby sieciowe, od całkowitej izolacji (none) przez współdzielenie stosu (host) po domyślny mostek (bridge). Szczególna uwaga poświęcona jest adresacji IP i komunikacji pomiędzy kontenerami.

Czwarta część (slajdy 31-38) to dogłębna analiza mechanizmu publikowania portów. Uczestnik dowie się, co dokładnie dzieje się w iptables po wykonaniu docker run -p.

Piąta część (slajdy 39-44) to laboratorium, w którym uczestnicy ręcznie odtworzą to, co Docker robi automatycznie. To kluczowy moment warsztatu, ponieważ pozwala na zrozumienie, a nie tylko mechaniczne używanie narzędzi.

Ostatnia część (slajdy 45-50) to troubleshooting i podsumowanie. Uczestnik otrzyma zestaw narzędzi i technik do samodzielnego rozwiązywania problemów sieciowych, a także ściągawkę z najważniejszymi poleceniami.

4/50
Ewolucja od przełączników fizycznych do wirtualizacji

Tradycyjne przełączniki L2 a SDN

Tradycyjne przełączniki sieciowe to urządzenia dedykowane (ASIC), które przetwarzają ramki w oparciu o tablicę adresów MAC. Decyzje o przekazywaniu ramek podejmowane są lokalnie na podstawie tablicy FDB (Forwarding Database). Przełącznik sam uczy się, które adresy MAC są dostępne na poszczególnych portach.

Wirtualizacja sieci przenosi te funkcje do warstwy oprogramowania. Mostek Linux jest wirtualnym przełącznikiem L2 działającym w przestrzeni jądra, który emuluje działanie fizycznego przełącznika przy użyciu zasobów CPU i pamięci RAM.

SDN (Software Defined Networking) idzie o krok dalej: separuje płaszczyznę sterowania od płaszczyzny danych. W przypadku mostka Linux płaszczyzna sterowania (uczenie się MAC, STP) jest zaimplementowana w jądrze, podczas gdy w pełnym SDN może być przeniesiona do zewnętrznego kontrolera.

Porównanie przełączników fizycznych i wirtualnych

przełączniki fizyczne wykorzystują wyspecjalizowane układy ASIC (Application-Specific Integrated Circuit) do przetwarzania ramek z szybkością linii. Każdy port przełącznika ma dedykowany bufor, a decyzje forwardingowe podejmowane są w czasie rzeczywistym na podstawie odczytu nagłówków ramek. Proces uczenia się adresów MAC polega na analizie adresów źródłowych w przychodzących ramkach i zapisywaniu ich w tablicy FDB z przypisaniem do numeru portu.

W mostku Linux cała ta funkcjonalność jest zaimplementowana w kodzie jądra (net/bridge/) i wykorzystuje ogólny mechanizm sieciowy Netlink. Mostek rejestruje się jako wirtualne urządzenie sieciowe i może agregować dowolną liczbę interfejsów fizycznych lub wirtualnych. Każda ramka przychodząca na którykolwiek z portów jest analizowana przez kod mostka.

SDN wprowadza rewolucję poprzez oddzielenie płaszczyzny sterowania (control plane) od płaszczyzny danych (data plane). W modelu OpenFlow kontroler SDN może dynamicznie zarządzać tablicami przepływów na przełączniku, co umożliwia programowalne zarządzanie ruchem sieciowym. Mostek Linux nie implementuje OpenFlow natywnie, ale może być rozszerzony przez Open vSwitch.

W kontekcie wirtualizacji centrów danych mostek Linux jest często używany jako prosty, wydajny przełącznik wirtualny dla maszyn wirtualnych (KVM, QEMU) i kontenerów (Docker, LXC). Jego zaletą jest zerowy koszt licencyjny, pełna integracja z jądrem oraz możliwość zarządzania standardowymi narzędziami.

W praktyce mostek Linux znajduje zastosowanie tam, gdzie potrzebna jest prosta łączność L2 pomiędzy wirtualnymi interfejsami. Dla bardziej zaawansowanych funkcji sieciowych (VXLAN, tunelowanie, QoS) stosuje się Open vSwitch, który oferuje bogatszy zestaw funkcji kosztem większego skomplikowania.

5/50
Software defined networking w Linux

Separacja płaszczyzny sterowania od danych

W architekturze SDN płaszczyzna sterowania (control plane) odpowiedzialna za podejmowanie decyzji o przekazywaniu pakietów jest oddzielona od płaszczyzny danych (data plane), która jedynie wykonuje te decyzje. W tradycyjnych przełącznikach obie płaszczyzny są zintegrowane w jednym urządzeniu.

Linux implementuje pewne elementy SDN poprzez mechanizmy takie jak:

  • tc (Traffic Control) - klasyfikacja i kształtowanie ruchu
  • nftables/iptables - filtrowanie i NAT na poziomie pakietów
  • Open vSwitch - pełna implementacja OpenFlow
  • eBPF/XDP - programowalne przetwarzanie pakietów w jądrze
  • VRF (Virtual Routing and Forwarding) - wirtualne tablicę routingu
Architektura SDN

Koncepcja SDN narodziła się na Uniwersytecie Stanforda jako projekt Ethane, a następnie została rozwinięta w protokół OpenFlow. Głównym założeniem było uczynienie sieci programowalną, tak jak programowalne są serwery. W modelu tradycyjnym każdy przełącznik podejmuje samodzielne decyzje na podstawie rozproszonych protokołów (STP, OSPF, BGP). W SDN decyzje są scentralizowane w kontrolerze.

Linux, jako system uniwersalny, oferuje wiele mechanizmów programowalnego przetwarzania pakietów. Traffic Control (tc) pozwala na definiowanie kolejek i dyscyplin dla poszczególnych interfejsów. Można tworzyć złożone hierarchie klasyfikacji ruchu z użyciem filtrów na podstawie adresów, portów, a nawet głębokiej inspekcji pakietów.

eBPF (Extended Berkeley Packet Filter) to przełomowa technologia, która pozwala na uruchamianie sandboxowanych programów w jądrze Linux bez konieczności pisania modułów jądra. Programy eBPF mogą być podpięte do punktów zaczepienia w stosie sieciowym, takich jak wejście pakietu (XDP), gniazdo, czy funkcja jądra. Dzięki temu można implementować własną logikę przetwarzania pakietów z wydajnością bliską natywnej.

Open vSwitch (OVS) to implementacja rozproszonego wirtualnego przełącznika wielowarstwowego, która obsługuje protokół OpenFlow i jest kompatybilna z SDN. OVS jest często używany w środowiskach chmurowych (OpenStack, OpenNebula) do realizacji zaawansowanych funkcji sieciowych, takich jak tunelowanie VXLAN, GRE, czy izolacja dzierżawców.

VRF (Virtual Routing and Forwarding) to mechanizm pozwalający na utrzymanie wielu niezależnych tablic routingu na jednym hoście. Każda VRF ma własną tablicę routingu, własny zestaw interfejsów i własne reguły forwardingowe. Jest to odpowiednik technologii MPLS L3VPN, ale zaimplementowany w przestrzeni użytkownika przy użyciu standardowych narzędzi Linux.

6/50
Linux bridge - wirtualny przełącznik L2

Mostek sieciowy w pamięci RAM

Linux Bridge to moduł jądra implementujący funkcje przełącznika warstwy 2. Mostek działa jak wirtualny switch, który może łączyć wiele interfejsów sieciowych (fizycznych i wirtualnych) w jedną domenę kolizyjną i broadcastową. Mostek operuje na ramkach Ethernet, podejmując decyzje na podstawie adresów MAC.

Główne cechy mostka Linux:

  • Uczenie się adresów MAC (MAC learning) - automatyczne budowanie tablicy FDB
  • Forwarding na podstawie docelowego adresu MAC
  • Obsługa STP (Spanning Tree Protocol) - zapobieganie pętlom
  • VLAN filtering - filtrowanie na podstawie tagów 802.1Q
  • Multicast snooping - optymalizacja ruchu multicast
Mostek Linux

Moduł bridge został dodany do jądra Linux w wersji 2.4 i od tego czasu jest stale rozwijany. Implementacja znajduje się w katalogu net/bridge/ źródła jądra. Mostek jest zarejestrowany jako wirtualne urządzenie sieciowe i może być konfigurowany za pomocą standardowych narzędzi z rodziny iproute2.

Wewnętrzna architektura mostka opiera się na tablicy haszującej przechowującej wpisy FDB. Każdy wpis zawiera adres MAC, identyfikator portu (interfejsu), znacznik czasu ostatniego odświeżenia oraz flagi (np. czy wpis jest stały czy dynamiczny). Tablica jest przeszukiwana przy każdej ramce, która nie jest przeznaczona dla samego mostka.

Mostek obsługuje trzy typy portów: porty członkowskie (member ports), port macierzysty (bridge master) oraz porty specjalne (np. port STP). Każdy interfejs dodany do mostka traci swoją niezależną konfigurację IP na rzecz interfejsu mostka. Adres IP mostka staje się adresem IP całej sieci wirtualnej.

Jedną z kluczowych cech mostka jest obsługa STP (Spanning Tree Protocol) zgodnie z IEEE 802.1D. STP zapobiega powstawaniu pętli w sieci poprzez blokowanie nadmiarowych portów. W mostku Linux STP jest domyślnie wyłączony, co w środowiskach kontenerowych zazwyczaj nie stanowi problemu, ponieważ topologie są z założenia acykliczne.

Od jądra 3.8 mostek obsługuje filtrowanie VLAN (802.1Q), co pozwala na tworzenie wirtualnych sieci w ramach jednego mostka. Funkcja ta jest szczególnie przydatna w środowiskach chmurowych, gdzie wymagana jest izolacja pomiędzy różnymi dzierżawcami na wspólnej infrastrukturze.

7/50
Budowa mostka sieciowego w Linux

Tworzenie mostka za pomocą iproute2 i brctl

Do zarządzania mostkiem można używać dwóch narzędzi: starszego brctl (z pakietu bridge-utils) oraz nowszego ip link z rodziny iproute2. Obecnie zalecane jest używanie iproute2, ponieważ jest to standardowe narzędzie w nowoczesnych dystrybucjach.

# Tworzenie mostka za pomocą iproute2
sudo ip link add name br0 type bridge
sudo ip link set br0 up

# Dodawanie interfejsów do mostka
sudo ip link set eth0 master br0
sudo ip link set eth1 master br0

# Alternatywnie za pomocą brctl
sudo brctl addbr br0
sudo brctl addif br0 eth0 eth1
Polecenia konfiguracji mostka

Polecenie ip link add name br0 type bridge tworzy nowe wirtualne urządzenie sieciowe typu bridge. W jądrze proces ten obejmuje alokację struktury net_device, inicjalizację tablicy FDB, utworzenie kolejki transmisyjnej oraz rejestrację urządzenia w systemie Netlink, co umożliwia zarządzanie mostkiem przez narzędzia przestrzeni użytkownika.

Po utworzeniu mostka należy go podnieść do stanu UP za pomocą ip link set br0 up. Bez tego mostek nie będzie przetwarzał ramek. Stan interfejsu można sprawdzić poleceniem ip link show br0, które wyświetli m.in. adres MAC mostka, MTU, stan oraz flagi.

Dodanie interfejsu do mostka za pomocą ip link set eth0 master br0 powoduje, że interfejs eth0 staje się portem członkowskim mostka. Od tego momentu eth0 przestaje być samodzielnym interfejsem L3 i działa wyłącznie na poziomie L2 w ramach mostka. Adres IP przypisany do eth0 przestaje być widoczny.

Starsze narzędzie brctl (bridge control) pochodzi z pakietu bridge-utils i było standardem w starszych dystrybucjach. Polecenia takie jak brctl addbr, brctl addif, brctl delif są intuicyjne, ale nie oferują wszystkich możliwości, jakie daje nowsze iproute2. Ponadto brctl jest uważane za przestarzałe w wielu nowoczesnych dystrybucjach Linux.

Usunięcie interfejsu z mostka wykonuje się poleceniem ip link set eth0 nomaster. Usunięcie całego mostka wymaga uprzedniego usunięcia wszystkich portów członkowskich, a następnie wywołania ip link delete br0 type bridge. W przypadku brctl odpowiada temu brctl delbr br0.

8/50
MAC learning i tablica przejść w bridge

Forwarding database w mostku

Mostek Linux buduje tablicę FDB (Forwarding Database) na podstawie analizy adresów MAC źródłowych w przychodzących ramkach. Proces ten nazywa się MAC learning i jest kluczowy dla poprawnego działania mostka jako przełącznika.

# Wywietlenie tablicy FDB
sudo bridge fdb show [br br0]

# Przykładowy wynik
00:1a:2b:3c:4d:5e dev eth0 master br0 stp_state=0
00:1a:2b:3c:4d:5f dev eth1 master br0 stp_state=0
00:1a:2b:3c:4d:60 dev veth0 master br0 stp_state=0

# Dodanie statycznego wpisu FDB
sudo bridge fdb add 00:11:22:33:44:55 dev eth0 master
Tablica FDB

Tablica FDB jest podstawową strukturą danych mostka. Każdy wpis zawiera adres MAC (48 bitów), identyfikator portu wyjściowego (dev), znacznik czasu ostatniego odświeżenia (aging timer) oraz flagę określającą typ wpisu: dynamiczny (uczony automatycznie), statyczny (dodany ręcznie) lub trwały (permanent). Domyślny czas życia wpisu dynamicznego to 300 sekund.

Proces uczenia działa następująco: gdy mostek otrzymuje ramkę na porcie P, odczytuje adres MAC źródłowy (SA) z nagłówka Ethernet. Jeśli SA nie istnieje w tablicy FDB, tworzony jest nowy wpis z adresem MAC, przypisaniem do portu P oraz bieżącym znacznikiem czasu. Jeśli wpis już istnieje, jest odświeżany.

Decyzja o przekazaniu ramki (forwarding) podejmowana jest na podstawie adresu MAC docelowego (DA). Jeśli DA znajduje się w tablicy FDB, ramka jest wysyłana tylko na port przypisany do tego adresu (unicast forwarding). Jeśli DA nie istnieje, ramka jest rozgłaszana na wszystkie porty poza portem źródłowym (flooding). Ramki broadcast (FF:FF:FF:FF:FF:FF) są zawsze rozgłaszane.

Polecenie bridge fdb show wyświetla całą tablicę FDB. Można ograniczyć wyniki do konkretnego mostka flagą br br0. W systemach z wieloma mostkami polecenie to jest nieocenione przy debugowaniu problemów z łącznością, pozwala bowiem sprawdzić, czy mostek nauczył się adresu MAC danego interfejsu.

Statyczne wpisy FDB są używane, gdy chcemy wymusić określoną ścieżkę dla ruchu do danego adresu MAC, na przykład w przypadku równoważenia obciążenia lub integracji z zewnętrznymi przełącznikami. Wpisy statyczne nie podlegają starzeniu i nie są usuwane automatycznie.

9/50
Forwarding w bridge

Jak mostek przekazuje ramki między interfejsami

Proces forwardingowy w mostku Linux można opisać w kilku krokach:

  1. Ramka przychodzi na interfejs portu członkowskiego
  2. Mostek sprawdza, czy ramka nie jest przeznaczona dla interfejsu mostka (bridge master)
  3. Jeśli tak - ramka jest przekazywana do warstwy L3 (stos IP mostka)
  4. Jeśli nie - mostek odczytuje docelowy adres MAC i przeszukuje tablicę FDB
  5. Znaleziony wpis determinuje port wyjściowy; brak wpisu powoduje flooding
Uwaga: Mostek nie forwarduje ramek z powrotem na port źródłowy. Zapewnia to, że ruch pomiędzy dwoma interfejsami na tym samym porcie nie zapętli się.
Forwarding w mostku

Algorytm forwardingu w mostku Linux jest zaimplementowany w funkcji br_handle_frame_finish w pliku net/bridge/br_forward.c. Gdy ramka dociera do tej funkcji, mostek najpierw sprawdza, czy docelowy adres MAC jest adresem samego mostka (np. adres MAC interfejsu br0). Jeśli tak, ramka jest kierowana do stosu IP, co umożliwia komunikację z samym hostem.

Jeśli docelowy adres MAC nie należy do mostka, rozpoczyna się proces forwardingu. Mostek przeszukuje tablicę FDB w poszukiwaniu wpisu pasującego do docelowego adresu MAC. Wyszukiwanie odbywa się w czasie O(1) dzięki zastosowaniu tablicy haszującej z 256 koszykami (dla adresów MAC o długości 48 bitów).

Znalezienie wpisu w FDB skutkuje przekazaniem ramki na port wskazany w tym wpisie, pod warunkiem że port ten różni się od portu źródłowego. Jeśli port źródłowy i docelowy są takie same, ramka jest odrzucana, co zapobiega cyrkulacji ruchu w obrębie jednego portu.

Brak wpisu w FDB powoduje flooding: ramka jest wysyłana na wszystkie porty członkowskie z wyjątkiem portu, na którym przyszła. Flooding jest kosztowny, ale konieczny do utrzymania łączności, gdy mostek nie zna jeszcze lokalizacji docelowego MAC. W sieciach o dużym ruchu flooding może prowadzić do znacznego obciążenia.

Warto zauważyć, że mostek Linux obsługuje również przekazywanie ramek z użyciem VLAN. Jeśli na moście skonfigurowano filtrowanie VLAN, ramka jest forwardowana tylko do tych portów, które należą do tej samej sieci VLAN. Mechanizm ten implementuje standard IEEE 802.1Q w jądrze systemu.

10/50
Bridge jako fundament wirtualizacji

Mostek w VM i kontenerach

Mostek Linux jest kluczowym elementem infrastruktury wirtualizacyjnej. Zarówno maszyny wirtualne (KVM, QEMU), jak i kontenery (Docker, LXC) wykorzystują mostek do łączenia swoich interfejsów sieciowych z resztą świata.

  • Maszyny wirtualne: Każda VM ma wirtualną kartę sieciową (np. virtio, e1000), która jest podłączona do mostka przez interfejs tap (np. vnet0, tap0). QEMU/KVM tworzy parę tap-bridge automatycznie
  • Kontenery Docker: Domyślnie Docker tworzy mostek docker0 i podłącza do niego kontenery przez pary VETH
  • LXC/LXD: Podobny mechanizm - każdy kontener ma parę VETH, której jeden koniec jest w mostku lxcbr0
Mostek w wirtualizacji

W przypadku maszyn wirtualnych KVM/QEMU mostek jest najczęściej używany w konfiguracji macvtap lub bridge. W trybie mostka fizycznego interfejs hosta (np. eth0) jest dodawany do mostka br0, a maszyny wirtualne podłączają się przez interfejsy tap. Dzięki temu maszyny wirtualne są widoczne w tej samej podsieci co host, bez translacji adresów.

Interfejsy TAP (Terminal Access Point) to wirtualne urządzenia sieciowe działające w warstwie L2. W przeciwieństwie do TUN (działających w L3), TAP obsługuje pełne ramki Ethernet. QEMU tworzy interfejs TAP dla każdej maszyny wirtualnej i przypisuje go do mostka. Nazwy interfejsów TAP mają zwykle postać vnet0, vnet1 itd.

Docker używa mostka jako domyślnego sterownika sieciowego. Podczas instalacji Docker tworzy mostek docker0 z domyślną podsiecią 172.17.0.0/16. Każdy nowo utworzony kontener otrzymuje parę VETH: jeden koniec (eth0 w kontenerze) znajduje się w przestrzeni nazw kontenera, drugi (vethXXXXX) jest podłączony do mostka docker0.

Zaletą używania mostka w wirtualizacji jest pełna transparentność L2: maszyny wirtualne i kontenery mogą komunikować się bezpośrednio ze sobą i z hostem, o ile znajdują się w tej samej domenie broadcastowej. Ponadto mostek nie wymaga konfiguracji trasowania IP, co upraszcza zarządzanie.

W środowiskach produkcyjnych często stosuje się zaawansowaną konfigurację z wieloma mostkami, każdy dla innej sieci VLAN, co pozwala na izolację ruchu pomiędzy różnymi grupami maszyn wirtualnych lub kontenerów bez użycia dodatkowych firewalli.

11/50
Polecenia do zarządzania bridge

Komendy ip, bridge, brctl

Poniżej zestawienie najważniejszych poleceń do zarządzania mostkiem sieciowym w Linux:

# iproute2 (zalecane)
sudo ip link add name br0 type bridge
sudo ip link set br0 up
sudo ip link set eth0 master br0
sudo ip addr add 192.168.1.1/24 dev br0

# bridge (część iproute2)
sudo bridge fdb show br br0
sudo bridge link set veth0 hairpin on

# brctl (starsze, bridge-utils)
sudo brctl addbr br0
sudo brctl addif br0 eth0
sudo brctl stp br0 on
Polecenia zarządzania mostkiem

Narzędzie ip z pakietu iproute2 jest obecnie standardowym narzędziem do konfiguracji sieci w Linux. Zastąpiło ono starsze narzędzia takie jak ifconfig, route i brctl. Główną zaletą iproute2 jest ujednolicony interfejs do zarządzania wszystkimi aspektami sieci: interfejsami, adresami, routingiem, tunelami i mostkami.

Polecenie ip link add name br0 type bridge tworzy mostek, ale nie jest to jedyny sposób. Można również użyć ip link add name br0 type bridge vlan_filtering 1, aby od razu włączyć filtrowanie VLAN. Dodatkowe parametry to m.in. ageing_time (czas starzenia wpisów FDB), group_fwd_mask (maska forwardowania grup) i stp_state (stan STP).

Narzędzie bridge (część iproute2) służy do szczegółowego zarządzania mostkiem. Polecenie bridge fdb show wyświetla tablicę FDB. bridge fdb add i bridge fdb del pozwalają na ręczne modyfikowanie wpisów. bridge link zarządza właściwościami portów, takimi jak hairpin mode (pozwala na przekazywanie ramek z powrotem na ten sam port).

Starszy pakiet bridge-utils zawiera narzędzie brctl, które jest prostsze, ale mniej elastyczne. Polecenia takie jak brctl show, brctl showmacs br0, brctl setpathcost br0 1 100 są nadal używane w starszych dokumentacjach i skryptach. W nowych instalacjach zaleca się jednak używanie iproute2.

Do diagnostyki mostka przydatne jest polecenie ip link show master br0, które wyświetla wszystkie interfejsy podłączone do mostka, oraz bridge monitor, które na bieżąco pokazuje zmiany w tablicy FDB i stan portów.

12/50
Troubleshooting mostka

STP, zapętlenia, MAC flapping

Podczas pracy z mostkiem mogą wystąpić różne problemy. Poniżej najczęstsze z nich:

ProblemObjawyRozwiązanie
MAC flappingCiągłe zmiany wpisów FDBSprawdź pętle, wyłącz STP lub skonfiguruj poprawnie
Zapętlenie ramekWysokie obciążenie CPU, broadcast stormWłącz STP: brctl stp br0 on
Brak łącznościPing nie działa między portamiSprawdź ip link show master br0, sprawdź adresację IP
Wolna komunikacjaDuże opóźnieniaSprawdź hairpin mode, MTU, przeciążenie mostka

Do debugowania używaj: bridge fdb show, ip link show, tcpdump -i br0

Tabela problemów mostka

MAC flapping to sytuacja, w której mostek wielokrotnie zmienić przypisanie tego samego adresu MAC do różnych portów w krótkim czasie. Jest to zazwyczaj oznaka pętli w sieci: ramki z tym samym adresem MAC źródłowym docierają na różne porty mostka, co zmusza go do ciągłej aktualizacji tablicy FDB. Każda taka zmiana jest rejestrowana w logach jądra, widocznych przez dmesg.

Zapętlenie ramek (broadcast storm) występuje, gdy w sieci istnieje cykl, w którym ramki broadcast są nieustannie przekazywane między mostkami. W skrajnych przypadkach może to doprowadzić do całkowitego paraliżu sieci z powodu 100% wykorzystania CPU przez procesy obsługi przerwan sieciowych. Rozwiązaniem jest włączenie STP (Spanning Tree Protocol), które blokuje nadmiarowe porty, przerywając cykl.

STP w mostku Linux jest domyślnie wyłączony, ponieważ w typowych konfiguracjach wirtualizacyjnych topologia jest acykliczna (drzewiasta). Jednak w przypadku połączenia mostka Linux z fizycznym przełącznikiem obsługującym STP, albo przy łączeniu wielu mostków między sobą, włączenie STP jest konieczne, aby uniknąć pętli.

Brak łączności między portami mostka może wynikać z kilku przyczyn: interfejs może nie być poprawnie dodany do mostka (sprawdź ip link show master br0), adresacja IP może być nieprawidłowa (mostek wymaga adresu IP tylko na interfejsie br0, nie na portach członkowskich), lub reguły firewalla (iptables) mogą blokować ruch między portami mostka.

Hairpin mode to specjalna konfiguracja portu mostka, która pozwala na przekazywanie ramek z powrotem na ten sam port, z którego przyszły. Jest to potrzebne w przypadku niektórych konfiguracji NAT i wirtualnych AP WiFi. Włącza się je poleceniem bridge link set dev veth0 hairpin on. Domyślnie hairpin mode jest wyłączone, co jest zgodne z zachowaniem fizycznego przełącznika.

13/50
Wirtualne pary Ethernet VETH

Koncepcja i działanie VETH

VETH (Virtual Ethernet) to para wirtualnych interfejsów sieciowych połączonych ze sobą jak rura (pipe). Ramka wysłana na jednym koncu pary pojawia się natychmiast na drugim koncu. VETH działa na poziomie warstwy 2 (Ethernet) i obsługuje pełne ramki, łącznie z nagłówkiem MAC.

Kluczowe cechy VETH:

  • Para interfejsów jest nierozerwalnie połączona - dane wysłane na veth0 pojawiają się na veth1 i odwrotnie
  • Każdy interfejs może znajdowaa się w innej przestrzeni nazw sieciowej (network namespace)
  • VETH nie wymaga żadnego wsparcia sprzętowego - działa w całości w pamięci RAM
  • Obsługuje standardowe funkcje sieciowe: MTU, adres MAC, promiscuous mode
  • Jest podstawowym mechanizmem łączenia kontenerów z siecią hosta
Para VETH

Implementacja VETH w jądrze Linux znajduje się w pliku drivers/net/veth.c. Jest to stosunkowo prosty sterownik, który tworzy dwa urządzenia sieciowe i łączy je kolejką pierścieniową (ring buffer). Gdy ramka jest wysyłana na jedno urządzenie, sterownik bezpośrednio przekazuje ją do kolejki odbiorczej drugiego urządzenia, z pominięciem całego stosu sieciowego nadawcy.

Para VETH jest tworzona atomowo: polecenie ip link add veth0 type veth peer name veth1 tworzy oba interfejsy jednocześnie. Nie można utworzyć pojedynczego interfejsu VETH w izolacji - zawsze powstaje para. Po utworzeniu oba interfejsy znajdują się w domyślnej przestrzeni nazw (root namespace).

Po przeniesieniu jednego końca pary do innej przestrzeni nazw (poleceniem ip link set veth1 netns ns1), interfejs znika z przestrzeni root i staje się dostępny tylko w docelowej przestrzeni nazw. Para VETH działa wtedy jako wirtualny kabel łączący dwie odizolowane przestrzenie nazw.

VETH jest używany przez Docker do każdego kontenera w trybie bridge. Docker tworzy parę VETH, umieszcza jeden koniec w przestrzeni nazw kontenera (jako eth0), a drugi podłącza do mostka docker0. Cały ruch kontenera przechodzi przez te parę VETH, co zapewnia pełną izolację sieciową między kontenerami na poziomie L2.

Ograniczeniem VETH jest to, że nie można go użyć do połączenia dwóch interfejsów w tej samej przestrzeni nazw (nie miałoby to sensu, ponieważ interfejsy mogłyby komunikować się bezpośrednio). Ponadto VETH nie obsługuje multicastu w sposób efektywny - każda ramka multicast jest kopiowana do wszystkich par VETH w systemie.

14/50
Tworzenie pary VETH

Praktyczne polecenia VETH

# Tworzenie pary VETH
sudo ip link add veth0 type veth peer name veth1

# Sprawdzenie utworzonych interfejsów
ip link show | grep veth

# Przeniesienie veth1 do przestrzeni nazw ns1
sudo ip link set veth1 netns ns1

# Konfiguracja adresacji w przestrzeni nazw
sudo ip netns exec ns1 ip addr add 192.168.100.1/24 dev veth1
sudo ip netns exec ns1 ip link set veth1 up

# Konfiguracja adresacji w przestrzeni root
sudo ip addr add 192.168.100.2/24 dev veth0
sudo ip link set veth0 up
Tworzenie pary VETH

Polecenie ip link add veth0 type veth peer name veth1 tworzy dwa nowe interfejsy sieciowe. W jądrze Linux proces ten obejmuje alokację dwóch struktur net_device, utworzenie kolejki transmisyjnej (tx_ring) dla każdego z nich oraz połączenie kolejek w pętle: tx_queue veth0 jest połączona z rx_queue veth1 i vice versa. Parametr peer name jest obowiązkowy - nie można utworzyć niepełnej pary.

Po utworzeniu oba interfejsy są w stanie DOWN i nie mają przypisanego adresu IP. Przed przeniesieniem do innej przestrzeni nazw można opcjonalnie ustawia adres MAC za pomocą ip link set dev veth0 address 00:11:22:33:44:55. Jeśli nie ustawimy adresu MAC, jądro wygeneruje go automatycznie na podstawie prefiksu producenta i losowej wartości.

Przeniesienie interfejsu do innej przestrzeni nazw poleceniem ip link set veth1 netns ns1 jest operacją niewidoczną dla użytkownika: interfejs znika z listy ip link show w przestrzeni root i pojawia się dopiero po wykonaniu ip netns exec ns1 ip link show. Po przeniesieniu interfejs traci swoją konfigurację IP i adres MAC (zostaje zachowany tylko typ).

Aby skonfigurować adresację IP w docelowej przestrzeni nazw, używamy ip netns exec ns1 ip addr add 192.168.100.1/24 dev veth1. Polecenie ip netns exec uruchamia dowolne polecenie w kontekcie wybranej przestrzeni nazw sieciowej. W tym przypadku konfigurujemy adres IP i podnosimy interfejs do stanu UP.

Po poprawnej konfiguracji obu końców pary VETH, komunikacja między przestrzeniami nazw (root i ns1) jest możliwa za pomocą standardowego ping. Ramki ICMP Echo Request wysłane z veth0 trafiają na veth1 przez wirtualne połączenie, a Echo Reply wraca tą samą drogą. Cały proces odbywa się bez udziału fizycznego sprzętu sieciowego.

15/50
Network namespaces

Izolacja stosu sieciowego w Linux

Network namespace (netns) to mechanizm izolacji przestrzeni nazw w Linux, który pozwala na posiadanie niezależnych stosów sieciowych w ramach jednego systemu operacyjnego. Każda przestrzeń nazw ma własne interfejsy sieciowe, tablicę routingu, reguły iptables i gniazda.

Kluczowe aspekty network namespaces:

  • Pełna izolacja L2/L3 - interfejsy w jednym netns nie są widoczne w innych
  • Niezależne tablicę routingu i ARP
  • Własne reguły iptables/nftables w każdej przestrzeni
  • Możliwość łączenia przestrzeni przez VETH, VLAN, mostki
  • Fundament izolacji sieciowej w Dockerze i LXC
  • Każdy proces w systemie należy do dokładnie jednej przestrzeni nazw sieciowej
Network Namespaces

Network namespace jest jednym z ośmiu typów przestrzeni nazw w Linux (obok mnt, pid, uts, ipc, user, cgroup, time). Zostało wprowadzone w jądrze 2.6.24 i jest podstawą izolacji sieciowej w kontenerach. Każda przestrzeń nazw sieciowa ma własną instancję protokołów TCP/IP, gniazd, interfejsów i reguł firewalla.

Główną zaletą netns jest możliwość tworzenia całkowicie odizolowanych środowisk sieciowych na jednym hoście fizycznym. Dwa procesy w różnych przestrzeniach nazw nie mogą komunikować się przez sieć, chyba że zostaną połączone przez VETH, mostek lub inny mechanizm. To właśnie ta izolacja jest fundamentem bezpieczeństwa w kontenerach.

Domyślnie wszystkie procesy w systemie znajdują się w root network namespace (domyślna przestrzeń nazw). Docker tworzy nową przestrzeń nazw dla każdego kontenera i umieszcza w niej proces kontenera. Dzięki temu kontener ma własny interfejs eth0, własną tablicę routingu i własne reguły iptables, co symuluje działanie niezależnego hosta.

Izolacja netns nie jest całkowita - pewne operacje sieciowe, takie jak multicast na poziomie jądra czy niektóre operacje na gniazdach raw, mogą mieć efekty globalne. Ponadto przestrzenie nazw nie izolują zasobów takich jak MTU interfejsów fizycznych czy konfiguracja mostka - te są globalne dla całego systemu.

Do zarządzania przestrzeniami nazw sieciowych służy narzędzie ip netns, które oferuje komendy add, del, exec, list, monitor i pids. Istnieje również możliwość zarządzania netns przez pliki w /var/run/netns/ (wiązanie przestrzeni nazw z deskryptorem pliku).

16/50
Tworzenie i zarządzanie namespaceami

Polecenia ip netns

# Tworzenie nowej przestrzeni nazw
sudo ip netns add red
sudo ip netns add blue

# Wywietlenie wszystkich przestrzeni nazw
sudo ip netns list

# Wykonanie polecenia w przestrzeni nazw
sudo ip netns exec red ip link show

# Uruchomienie bash w przestrzeni nazw (interaktywnie)
sudo ip netns exec red bash

# Usuniecie przestrzeni nazw
sudo ip netns del red
Polecenia ip netns

Polecenie ip netns add red tworzy nową przestrzeń nazw sieciową o nazwie red. Wewnętrznie tworzony jest plik w /var/run/netns/red, który służy jako punkt montowania dla przestrzeni nazw. Gdy proces jest przypisany do tej przestrzeni nazw, widzi tylko interfejsy i konfigurację sieciową należącą do red.

Po utworzeniu przestrzeń nazw zawiera tylko interfejs pętli zwrotnej (lo), który jest domyślnie w stanie DOWN. Aby sieć w przestrzeni nazw działała, należy podnieść interfejs lo: ip netns exec red ip link set lo up. Bez tego lokalne gniazda TCP/UDP w przestrzeni nazw nie będą działać.

Polecenie ip netns exec red <komenda> uruchamia podaną komendę w kontekcie przestrzeni nazw red. Jest to realizowane przez wywołanie systemowe setns(), które zmienia przestrzeń nazw bieżącego wątku na docelową. Po zakończeniu komendy wątek wraca do oryginalnej przestrzeni nazw.

Dla wygody można uruchomić powłokę bash w przestrzeni nazw: ip netns exec red bash. Wszystkie polecenia wykonane w tej powłoce będą działać w kontekcie przestrzeni red. Aby wyjść, należy użyć exit lub Ctrl+D. Jest to bardzo przydatne podczas debugowania i konfiguracji.

Usunięcie przestrzeni nazw poleceniem ip netns del red powoduje zamknięcie wszystkich gniazd i usunięcie wszystkich interfejsów wirtualnych w tej przestrzeni. Interfejsy fizyczne nie mogą być jednak usunięte - jeśli fizyczny interfejs został przeniesiony do netns, najpierw trzeba go przenieść z powrotem do root netns.

17/50
Łączenie namespaceów przez VETH pair

Połączenie bezpośrednie dwóch przestrzeni nazw

# Tworzenie dwóch przestrzeni nazw
sudo ip netns add ns1
sudo ip netns add ns2

# Tworzenie pary VETH
sudo ip link add veth1 type veth peer name veth2

# Przeniesienie końców do przestrzeni nazw
sudo ip link set veth1 netns ns1
sudo ip link set veth2 netns ns2

# Konfiguracja adresacji IP
sudo ip netns exec ns1 ip addr add 10.0.0.1/24 dev veth1
sudo ip netns exec ns1 ip link set veth1 up
sudo ip netns exec ns2 ip addr add 10.0.0.2/24 dev veth2
sudo ip netns exec ns2 ip link set veth2 up

# Test łącznoci
sudo ip netns exec ns1 ping 10.0.0.2
Połączenie VETH dwóch przestrzeni

Bezpośrednie połączenie dwóch przestrzeni nazw przez parę VETH jest najprostszym sposobem na umożliwienie komunikacji między odizolowanymi stosami sieciowymi. Schemat ten symuluje połączenie dwóch hostów kablem krosowanym (crossover cable). Każda przestrzeń nazw ma własny interfejs z adresem IP w tej samej podsieci.

W przedstawionym przykładzie tworzymy dwie przestrzenie nazw: ns1 i ns2. Para VETH (veth1-veth2) jest tworzona w root netns, a następnie każdy koniec jest przenoszony do innej przestrzeni. Po przeniesieniu interfejsy znikają z root netns i stają się dostępne tylko w docelowych przestrzeniach.

Konfiguracja adresacji IP odbywa się już w docelowych przestrzeniach nazw. Adresy 10.0.0.1/24 i 10.0.0.2/24 należy do tej samej podsieci /24, co pozwala na bezpośrednią komunikację bez routingu. Po podniesieniu interfejsów do stanu UP (ip link set veth1 up), ping z ns1 do ns2 powinien działać.

Warto zauważyć, że w takiej konfiguracji nie ma żadnego urządzenia pośredniego (mostka, routera). Komunikacja odbywa się bezpośrednio między dwoma interfejsami VETH, które są ze sobą połączone wirtualnym kablem. Jest to odpowiednik sieci typu point-to-point.

Ograniczeniem tego rozwiązania jest skomplikowanie przy większej liczbie przestrzeni nazw. Połączenie trzech przestrzeni (mesh) wymagałoby trzech par VETH odpowiednio skonfigurowanych, co szybko staje się niepraktyczne. W takich przypadkach stosuje się mostek, który łączy wiele przestrzeni w jedną sieć L2.

18/50
Łączenie namespaceów przez VETH z mostkiem

Topologia z mostkiem L2

# Tworzenie mostka i przestrzeni nazw
sudo ip link add br0 type bridge
sudo ip link set br0 up
sudo ip netns add ns1
sudo ip netns add ns2

# Tworzenie par VETH dla każdej przestrzeni
sudo ip link add veth1 type veth peer name veth1-br
sudo ip link add veth2 type veth peer name veth2-br

# Podłączenie do mostka i przestrzeni nazw
sudo ip link set veth1-br master br0
sudo ip link set veth1 netns ns1
sudo ip link set veth2-br master br0
sudo ip link set veth2 netns ns2

# Podniesienie interfejsów
sudo ip link set veth1-br up
sudo ip link set veth2-br up
sudo ip netns exec ns1 ip link set veth1 up
Mostek z przestrzeniami nazw

łączenie przestrzeni nazw przez mostek to najczęściej stosowana topologia w środowiskach kontenerowych. Mostek działa jako wirtualny przełącznik L2, do którego podłączone są wszystkie przestrzenie nazw (kontenery). Umożliwia to komunikację między dowolną parą przestrzeni nazw w tej samej podsieci L2.

W przykładzie tworzymy mostek br0 oraz dwie przestrzenie nazw: ns1 i ns2. Dla każdej przestrzeni tworzymy dedykowaną parę VETH (veth1-veth1-br dla ns1, veth2-veth2-br dla ns2). Jeden koniec każdej pary (veth1-br, veth2-br) jest dodawany do mostka jako port członkowski, drugi koniec trafia do przestrzeni nazw.

Interfejsy po stronie mostka (veth1-br, veth2-br) muszą być podniesione (ip link set up), aby mostek mógł przetwarzać ramki. Interfejsy w przestrzeniach nazw również muszą być podniesione i skonfigurowane z adresami IP z tej samej podsieci, np. 192.168.1.1/24 dla ns1 i 192.168.1.2/24 dla ns2.

Po skonfigurowaniu mostek uczy się, który adres MAC jest dostępny na którym porcie. Gdy ns1 pinguje ns2, ramka ICMP Echo Request kieruje się z veth1 (ns1) do veth1-br (mostek), mostek sprawdza tablicę FDB dla docelowego MAC, znajduje wpis veth2-br i przekazuje ramkę na veth2-br, skąd trafia do veth2 (ns2).

Zaletą topologii z mostkiem jest skalowalność: dodanie trzeciej, czwartej czy setnej przestrzeni nazw wymaga tylko utworzenia nowej pary VETH i dodania jej końca do mostka. Nie ma potrzeby tworzenia połączen bezpośrednich między każdą parą przestrzeni. Jest to dokładnie ta sama koncepcja, której używa Docker z mostkiem docker0.

19/50
Sprawdzanie izolacji

Weryfikacja łączności i izolacji

# Test łącznoci między przestrzeniami nazw
sudo ip netns exec ns1 ping -c 3 192.168.1.2

# Sprawdzenie, że root netns nie ma dostępu do przestrzeni
ping -c 1 192.168.1.1  # Powinien zwrócić Network is unreachable

# Podgląd tablicy routingu w przestrzeni nazw
sudo ip netns exec ns1 ip route show

# Sprawdzenie tablicy FDB mostka
sudo bridge fdb show br br0

# Monitorowanie ruchu na mo cie
sudo tcpdump -i br0 -n icmp
Weryfikacja izolacji

Weryfikacja izolacji sieciowej jest kluczowym elementem testowania poprawności konfiguracji. W pierwszym kroku testujemy łączność między przestrzeniami nazw: ping z ns1 do ns2 powinien działać, ponieważ obie przestrzenie są w tej samej podsieci i połączone przez mostek. Opcja -c 3 ogranicza liczbę pakietów do trzech.

Następnie sprawdzamy, że root network namespace (domyślna przestrzeń) nie ma dostępu do interfejsów w przestrzeniach ns1 i ns2. Polecenie ping 192.168.1.1 wykonane w root netns powinno zwrócić błąd Network is unreachable, ponieważ adres 192.168.1.1 jest skonfigurowany na interfejsie veth1, który znajduje się w przestrzeni ns1 i nie jest widoczny z root netns.

Podgląd tablicy routingu w przestrzeni nazw (ip netns exec ns1 ip route show) powinien pokazać trase lokalną do podsieci 192.168.1.0/24 przez interfejs veth1. W przestrzeni ns1 trasa domyślna może być nieobecna, chyba że została skonfigurowana ręcznie lub przez DHCP.

Tablica FDB mostka (bridge fdb show br br0) powinna zawierać dwa dynamiczne wpisy: adresy MAC interfejsów veth1 i veth2 (veth1-br i veth2-br w terminologii mostka). Jeśli ping z ns1 do ns2 został wykonany, mostek nauczył się adresu MAC veth2 (docelowego) i wyświetli go w tablicy.

Monitorowanie ruchu na moście za pomocą tcpdump pozwala na podgląd ramek przechodzących przez mostek w czasie rzeczywistym. Flaga -i br0 wskazuje interfejs mostka, -n wyłącza rozwiązywanie nazw, icmp filtruje tylko pakiety ICMP. Jest to doskonałe narzędzie do debugowania problemów z łącznością, ponieważ pokazuje, czy ramki docierają do mostka.

20/50
Zastosowania VETH i netns w praktyce

Kontenery, sandboxing, testowanie

  • Docker: Każdy kontener otrzymuje własną przestrzeń nazw netns i parę VETH do mostka docker0. Docker automatyzuje cały proces tworzenia netns, VETH i konfiguracji adresacji IP przez DHCP wewnętrzny
  • Systemd-nspawn: Używa netns i VETH do izolacji lekkich kontenerów systemowych
  • Testowanie sieci: Można symulować złożone topologie sieciowe (router, przełącznik, hosty) na jednym fizycznym hoście bez potrzeby posiadania wielu maszyn
  • Sandboxing aplikacji: Uruchamianie nieufnych aplikacji w izolowanej sieciowo przestrzeni, co uniemożliwia im komunikację z siecią zewnętrzną
  • OpenStack/Neutron: Wykorzystuje netns i VETH do izolacji sieci wirtualnych w chmurze obliczeniowej
Zastosowania VETH i netns

Głównym zastosowaniem VETH i netns w praktyce jest izolacja sieciowa kontenerów. Docker, jako najpopularniejsze narzędzie konteneryzacyjne, opiera się w całości na tych mechanizmach jądra Linux. Każdy kontener Docker to proces uruchomiony w zestawie przestrzeni nazw (mnt, pid, net, uts, ipc), z których sieciowa (netns) zapewnia izolację stosu TCP/IP.

Systemd-nspawn, alternatywa dla Dockera, również używa netns do izolacji. Polecenie systemd-nspawn --private-network tworzy nową przestrzeń nazw sieciową dla kontenera. Można również łączyć kontenery systemd-nspawn z siecią hosta przez pary VETH i mostek.

W testowaniu sieci, VETH i netns pozwalają na odtworzenie złożonych topologii na jednym hoście. Można stworzyć wirtualny router (z netns i włąconym ip_forward), kilka sieci L2 (mostki) i hosty w każdej sieci (netns z VETH do mostków). Całość działa w pamięci RAM, bez potrzeby fizycznych urządzeń sieciowych.

Sandboxing sieciowy jest ważny z punktu widzenia bezpieczeństwa. Aplikacja uruchomiona w izolowanej przestrzeni nazw nie ma dostępu do interfejsów sieciowych hosta ani do innych przestrzeni nazw. Nawet jeśli aplikacja zostanie skompromitowana, atakujący nie będzie mógł wykonać skanowania sieci ani przechwycić ruchu sieciowego hosta.

OpenStack Neutron, warstwa sieciowa platformy chmurowej OpenStack, wykorzystuje netns do izolacji sieci wirtualnych (tenant networks). Każda sieć wirtualna jest realizowana jako mostek Linux lub OVS w dedykowanej przestrzeni nazw, co zapewnia izolację między dzierżawcami chmury. VETH są używane do łączenia przestrzeni nazw z mostkami.

21/50
Wprowadzenie do sieci w Dockerze

Tryby sieciowe Docker

Docker oferuje kilka trybów (sterowników) sieciowych, które determinują sposób podłączenia kontenera do sieci. Wybór trybu wpływa na izolację, wydajność i dostępność kontenera z zewnątrz.

SterownikIzolacjaZastosowanie
nonePełnaKontenery bez sieci (offline processing)
hostBrakMaksymalna wydajność, monitoring
bridgeCzęściowaDomyślny, uniwersalny (docker0)
overlayPełnaKlastry multi-host (Swarm, Kubernetes)
macvlanBrak L2Integracja z fizyczną siecią L2
Tryby sieciowe Docker

Docker używa wbudowanych mechanizmów jądra Linux (network namespaces, VETH, mostki, iptables) do realizacji swoich sterowników sieciowych. Każdy sterownik to inna kombinacja tych mechanizmów, dostosowana do konkretnego przypadku użycia. Zrozumienie tych sterowników jest kluczowe dla poprawnego projektowania architektury kontenerowej.

Wyboru sterownika dokonuje się przy uruchamianiu kontenera flagą --network lub --net. Na przykład docker run --network=host nginx uruchomi kontener w trybie host. Domyślnym sterownikiem jest bridge, który jest automatycznie wybierany, gdy nie podano flagi --network.

Sterownik none zapewnia najwyższy poziom izolacji sieciowej. Kontener ma własną przestrzeń nazw, ale zawiera ona tylko interfejs pętli (lo). Nie ma żadnego połączenia z siecią zewnętrzną ani z innymi kontenerami. Jest to używane dla kontenerów wykonujących zadania offline, takie jak przetwarzanie danych, obliczenia naukowe itp.

Sterownik host eliminuje izolację sieciową: kontener współdzieli przestrzeń nazw z hostem. Oznacza to, że kontener widzi wszystkie interfejsy hosta i może nasłuchiwać na dowolnych portach bez mapowania. Jest to najszybszy tryb (brak pośrednictwa mostka), ale najmniej bezpieczny, ponieważ kontener ma pełny dostęp do stosu sieciowego hosta.

Sterownik overlay jest używany w klastrach Docker Swarm i Kubernetes do tworzenia wirtualnych sieci rozciągających się na wiele hostów fizycznych. Wykorzystuje tunelowanie VXLAN, które enkapsuluje ramki L2 w pakiety UDP i przesyła przez sieć IP. Dzięki temu kontenery na różnych hostach mogą komunikować się tak, jakby były w tej samej sieci L2.

22/50
Tryb none - całkowita izolacja sieciowa

Kontener bez dostępu do sieci

Tryb none jest najprostszym i najbardziej izolującym trybem sieciowym w Dockerze. Kontener otrzymuje własną przestrzeń nazw sieciową, ale nie jest do niej podłączona żadna para VETH ani inny interfejs poza pętlą zwrotną (lo).

# Uruchomienie kontenera w trybie none
docker run -it --network none alpine sh

# Wewnątrz kontenera - tylko interfejs lo
/ # ip link show
1: lo:  mtu 65536 qdisc noop state DOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

/ # ping 8.8.8.8
# ping: connect: Network is unreachable
Tryb none w Dockerze

Tryb none jest odpowiednikiem utworzenia przestrzeni nazw sieciowej (ip netns add) i niełączenia jej z żadnym interfejsem poza pętlą zwrotną. Docker tworzy nową przestrzeń nazw, ale nie tworzy pary VETH, nie podłącza jej do mostka i nie przypisuje adresu IP. Kontener ma własną, całkowicie pustą przestrzeń sieciową.

W trybie none kontener nie ma dostępu do Internetu, ani nie może komunikować się z innymi kontenerami. Jest to użyteczne w scenariuszach bezpieczeństwa, gdzie aplikacja nie potrzebuje dostępu do sieci, a chcemy uniknąć ryzyka wycieku danych lub ataku z zewnątrz.

Przykładowe zastosowania: kontenery wykonujące obliczenia kryptograficzne, przetwarzanie wrażliwych danych, testy jednostkowe offline, kompilacja kodu w izolowanym środowisku, czy narzędzia do analizy bezpieczeństwa, które nie powinny komunikować się z siecią.

W trybie none interfejs lo jest domyślnie w stanie DOWN. Jeśli aplikacja w kontenerze wymaga komunikacji przez localhost, należy ręcznie podnieść interfejs lo za pomocą ip link set lo up lub dodać odpowiednią komendę do Dockerfile (RUN).

Mimo braku dostępu do sieci zewnętrznej, kontener w trybie none nadal może używać gniazd Unixowych do komunikacji z procesami na hoście (jeśli woluminy są współdzielone). Sieć TCP/IP jest całkowicie wyłączona, ale IPC (Inter-Process Communication) przez gniazda Unix może działać.

23/50
Tryb host - współdzielenie stosu z hostem

Brak izolacji sieciowej

W trybie host kontener nie otrzymuje własnej przestrzeni nazw sieciowej, ale współdzieli ją z hostem. Oznacza to, że kontener widzi wszystkie interfejsy sieciowe hosta i może nasłuchiwać na dowolnych portach bez potrzeby mapowania.

# Uruchomienie kontenera w trybie host
docker run -d --network host nginx

# Nginx nasłuchuje na porcie 80 hosta bezpośrednio
curl http://localhost:80

# W kontenerze widaa interfejsy hosta
docker exec -it  ip link show
# Wywietla interfejsy fizyczne hosta (eth0, wlp2s0, docker0 itd.)
Tryb host w Dockerze

Tryb host jest implementowany przez pominicie tworzenia nowej przestrzeni nazw sieciowej dla kontenera. Zamiast tego Docker ustawia flage CLONE_NEWNET na false podczas wywołania systemowego clone() lub unshare(). W efekcie kontener działa w root network namespace, czyli tej samej przestrzeni nazw co host.

Główną zaletą trybu host jest wydajność. Ponieważ nie ma pośrednictwa mostka ani reguł iptables dla mapowania portów, ruch sieciowy trafia bezpośrednio do procesu w kontenerze bez żadnej dodatkowej translacji. Opóźnienia sieciowe są minimalne, a przepustowość maksymalna (ograniczona tylko przez fizyczny interfejs).

Wady trybu host to przede wszystkim brak izolacji. Dwa kontenery w trybie host nie mogą nasłuchiwać na tym samym porcie (konflikt portów). Ponadto kontener ma dostęp do wszystkich interfejsów sieciowych hosta, co może stanowić zagrożenie bezpieczeństwa w przypadku skompromitowania aplikacji.

Tryb host jest zalecany dla aplikacji wymagających maksymalnej wydajności sieciowej, takich jak serwery proxy o wysokim obciążeniu, serwery gier, aplikacje czasu rzeczywistego, czy narzędzia monitorujące sieć (np. tcpdump uruchomione w kontenerze).

W trybie host flagi -p (publish) i -P (publish-all) są ignorowane, ponieważ mapowanie portów nie ma sensu - aplikacja w kontenerze może bezpośrednio nasłuchiwać na porcie hosta. Docker wyświetli ostrzeżenie, jeśli spróbujemy użyć -p z --network host.

24/50
Tryb bridge - domyślna sieć Docker

Prywatna sieć docker0

Tryb bridge jest domyślnym sterownikiem sieciowym Dockera. Gdy Docker jest instalowany, automatycznie tworzy mostek docker0 (Linux bridge) z prywatną podsiecią, zwykle 172.17.0.0/16. Każdy nowy kontener otrzymuje parę VETH, której jeden koniec jest podłączony do mostka, a drugi znajduje się w przestrzeni nazw kontenera.

# Sprawdzenie mostka docker0
ip link show docker0
ip addr show docker0
# docker0: 172.17.0.1/16

# Uruchomienie kontenera w domyślnym trybie bridge
docker run -it --rm alpine sh
Mostek docker0

Podczas instalacji Docker demon tworzy mostek docker0 z adresem IP zwykle 172.17.0.1/16. Mostek ten służy jako brama domyślna dla wszystkich kontenerów w domyślnej sieci bridge. Docker konfiguruje również reguły iptables do masquerady, umożliwiające kontenerom dostęp do Internetu przez adres IP hosta.

Każdy kontener uruchomiony bez flagi --network otrzymuje nową przestrzeń nazw sieciową. Docker tworzy dla niego parę VETH: jeden koniec (nazwa losowa, np. vethła4b5c) jest podłączany do mostka docker0, drugi koniec (eth0) trafia do przestrzeni nazw kontenera. Kontener otrzymuje adres IP z puli mostka docker0 przez wbudowany serwer DHCP.

Kontenery w domyślnej sieci bridge mogą komunikować się ze sobą poprzez adresy IP, ale nie przez nazwy hostów (brak wbudowanego DNS). Aby kontenery mogły komunikować się przez nazwy, należy utworzyć zdefiniowaną przez użytkownika sieć bridge (user-defined bridge) z włąconym DNS.

Mostek docker0 zapewnia izolację na poziomie L2: kontenery widzą tylko własny interfejs eth0 i mostek. Nie widzą interfejsów fizycznych hosta ani innych kontenerów w innych sieciach. Ruch między kontenerami w tej samej sieci bridge odbywa się bezpośrednio przez mostek, bez translacji NAT.

Ograniczeniem domyślnego mostka jest to, że nie zapewnia on izolacji między kontenerami na poziomie portów - każdy kontener może próbować połączyć się z dowolnym portem innego kontenera w tej samej sieci. Dla większego bezpieczeństwa zaleca się używanie sieci zdefiniowanych przez użytkownika z opcją --internal.

25/50
Budowa mostka docker0

Adresacja IP i NAT

# Adres IP mostka docker0
ip addr show docker0
3: docker0:  mtu 1500 qdisc noqueue
    link/ether 02:42:ac:11:00:01 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0

# Reguły NAT dodane przez Docker
iptables -t nat -L -n
# Chain POSTROUTING: MASQUERADE dla 172.17.0.0/16
MASQUERADE  all  --  172.17.0.0/16  !172.17.0.0/16

# Sprawdzenie kontenerów w sieci bridge
bridge link show master docker0
Budowa docker0

Mostek docker0 ma domyślnie przypisany adres IP z zakresu 172.17.0.1/16. Adres ten staje się bramą domyślną dla wszystkich kontenerów w tej sieci. Gdy kontener wysyła pakiet do Internetu (np. 8.8.8.8), ramka trafia na mostek docker0, następnie jest przekazywana do stosu IP hosta, gdzie podlega routingowi.

Docker dodaje regułe MASQUERADE w łańcuchu POSTROUTING tabeli nat iptables. Reguła ta powoduje, że wszystkie pakiety wychodzące z podsieci 172.17.0.0/16 na zewnątrz (poza te podsieć) są poddawane translacji SNAT na adres IP interfejsu zewnętrznego hosta. Dzięki temu kontenery mogą łączyć się z Internetem, mimo że mają prywatne adresy IP.

Docker dynamicznie zarządza regułami iptables. Przy każdym uruchomieniu lub zatrzymaniu kontenera Docker aktualizuje reguły w łańcuchach DOCKER, DOCKER-ISOLATION i POSTROUTING, aby zapewnić poprawną izolację i routing. Ręczna modyfikacja tych reguł może być nadpisywana przez Docker.

Polecenie bridge link show master docker0 wyświetla wszystkie interfejsy VETH podłączone do mostka docker0. Każdy interfejs o nazwie vethXXXX to jeden koniec pary VETH prowadzącej do kontenera. Drugi koniec (eth0) znajduje się w przestrzeni nazw kontenera i nie jest widoczny z poziomu hosta.

Konfiguracja mostka docker0 może być modyfikowana przez plik /etc/docker/daemon.json, na przykład można zmieniać adresację IP (bip), MTU, mostek (bridge) na własny, czy wyłączyć iptables. Po zmianie konfiguracji należy zrestartować demon Dockera.

26/50
Komunikacja między kontenerami w sieci bridge

Bezpośrednia komunikacja L2

Kontenery w domyślnej sieci bridge mogą komunikować się ze sobą bezpośrednio na poziomie L2 przez mostek. Nie ma potrzeby konfigurowania trasowania ani NAT dla komunikacji między kontenerami w tej samej sieci.

# Uruchomienie dwóch kontenerów
docker run -d --name web1 nginx
docker run -it --name web2 alpine sh

# W kontenerze web2: ping do web1
/ # ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.123 ms

# Sprawdzenie adresów IP kontenerów
docker inspect web1 | grep IPAddress
Komunikacja kontenerów

Komunikacja między kontenerami w tej samej sieci bridge odbywa się w następujący sposób: kontener A wysyła ramkę Ethernet z adresem MAC kontenera B jako docelowym. Ramka trafia przez parę VETH na mostek docker0. Mostek sprawdza tablicę FDB, znajduje port veth drugiego kontenera i przekazuje ramkę bezpośrednio, bez wychodzenia na zewnętrzny interfejs hosta.

Ponieważ mostek docker0 działa w przestrzeni jądra, a oba interfejsy VETH są podłączone do tego samego mostka, ramki są przekazywane w całości w pamięci RAM, bez konieczności przechodzenia przez fizyczny interfejs sieciowy. Opóźnienia są rzędu mikrosekund (zazwyczaj poniżej 0.2 ms).

Aby sprawdzić adres IP kontenera, używamy docker inspect <nazwa> | grep IPAddress. Adresy IP są przydzielane z puli mostka docker0 przez wbudowany serwer DHCP w demonie Dockera. Docker gwarantuje, że każdy kontener otrzymuje unikalny adres IP w ramach sieci.

W domyślnej sieci bridge kontenery nie mogą komunikować się przez nazwy hostów. Aby połączyć się z web1, kontener web2 musi znać adres IP web1. Docker oferuje wbudowany DNS w sieciach zdefiniowanych przez użytkownika, gdzie nazwa kontenera jest automatycznie rejestrowana i rozwiązywana.

Izolacja między kontenerami w domyślnej sieci bridge jest ograniczona: każdy kontener może połączyć się z dowolnym portem innego kontenera. Jeśli wymagana jest większa izolacja (np. zaufany serwer www i niezaufana baza danych), należy umieścić je w osobnych sieciach zdefiniowanych przez użytkownika i połączyć tylko niezbędne porty.

27/50
Zarządzanie sieciami Docker

docker network ls, create, inspect

# Wywietlenie wszystkich sieci
docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
c3b5e8f9a1d2   bridge    bridge    local
a4d6e7f8b2c3   host      host      local
e5f6a7b8c9d0   none      null      local

# Utworzenie własnej sieci bridge
docker network create --driver bridge --subnet 10.0.0.0/24 mynet

# Inspekcja sieci
docker network inspect mynet

# Podłączenie kontenera do sieci
docker run -d --network mynet --name app1 nginx
Zarządzanie sieciami Docker

Polecenie docker network ls wyświetla wszystkie sieci dostępne w systemie Docker. Domyślnie Docker tworzy trzy sieci: bridge (mostek docker0), host (tryb współdzielenia stosu) i none (brak sieci). Każda sieć ma unikalne ID, nazwę, sterownik (driver) i zasięg (scope: local lub swarm).

Nową sieć tworzymy poleceniem docker network create. Możemy określić sterownik (--driver), podsieć (--subnet), bramę (--gateway), zakres adresów IP (--ip-range) oraz wiele innych opcji. Dla sterownika bridge można również włączyć izolację (--internal) i komunikację przez nazwy (--enable-ipv6).

Polecenie docker network inspect wyświetla szczegółowe informacje o sieci: konfigurację IPAM (pula adresów, brama), listę podłączonych kontenerów z ich adresami IP, opcje sterownika (np. com.docker.network.bridge.name - nazwa mostka w systemie Linux).

Kontener może być podłączony do jednej lub wielu sieci. Przy uruchamianiu kontenera flagą --network określamy główną sieć. Dodatkowe sieci można podłączyć dynamicznie poleceniem docker network connect mynet kontener. Odłączenie sieci odbywa się przez docker network disconnect.

Usunięcie sieci wykonuje się poleceniem docker network rm mynet. Przed usunięciem należy odłączyć wszystkie kontenery od tej sieci. Nie można usunąć domyślnych sieci (bridge, host, none) - Docker nie pozwala na to ze względów bezpieczeństwa.

28/50
Sieci zdefiniowane przez użytkownika

User-defined bridge - zalety

Oprócz domyślnego mostka docker0, Docker pozwala na tworzenie własnych sieci bridge przez użytkownika. Niosą one kilka istotnych korzyści w porównaniu z domyślnym mostkiem:

  • Automatyczne DNS: Kontenery mogą komunikować się przez nazwy hostów (nazwa kontenera = nazwa DNS)
  • Lepsza izolacja: Kontenery w różnych sieciach user-defined są całkowicie odizolowane, chyba że połączone przez mostek
  • Możliwość odłączenia i podłączenia: Kontenery mogą być dynamicznie dołączane do sieci w trakcie działania
  • Własna konfiguracja: Można określić podsieć, bramę, MTU i inne parametry
  • Większe bezpieczeństwo: Domyślnie izolacja między kontenerami jest wyłączona (--icc=false)
User-defined bridge

Najważniejszą zaletą sieci zdefiniowanych przez użytkownika jest wbudowany DNS. Docker uruchamia wewnętrzny serwer DNS (127.0.0.11 w każdym kontenerze), który automatycznie rejestruje nazwy kontenerów w sieci. Gdy kontener A chce połączyć się z kontenerem B, wystarczy użyć nazwy B jako adresu, np. curl http://web:80.

W sieci user-defined bridge izolacja między kontenerami jest domyślnie wyłączona (flaga --icc=false), co oznacza, że kontenery nie mogą komunikować się ze sobą, chyba że zostanie to jawnie dozwolone. Jest to przeciwieństwo domyślnego mostka docker0, gdzie --icc=true (kontenery mogą się swobodnie komunikować).

Tworzenie sieci user-defined bridge odbywa się przez docker network create mynet. Docker tworzy nowy mostek Linux w systemie (nazwa zwykle zaczyna się od br-) i konfiguruje go z domyślną podsiecią. Można również określić własną podsieć flagą --subnet, co jest przydatne przy integracji z istniejącą infrastrukturą.

Kontenery w sieci user-defined mogą być dynamicznie odłączane i podłączane bez restartu. Jest to szczególnie przydatne w scenariuszach typu blue-green deployment, gdzie nowa wersja aplikacji jest uruchamiana obok starej, a następnie ruch jest przekierowywany przez zmianę sieci.

W sieci user-defined bridge można skonfigurować opcje takie jak --internal (brak dostępu do zewnętrznej sieci), --enable-ipv6 (włączenie IPv6), czy --ipam-driver (własny sterownik IPAM). Te opcje nie są dostępne dla domyślnego mostka docker0.

29/50
Łączenie kontenerów z wieloma sieciami

Multi-network Docker

Kontener Docker może należeć do wielu sieci jednocześnie, co pozwala na realizacje złożonych topologii, takich jak serwer WWW podłączony do dwóch sieci (zewnętrznej i wewnętrznej) lub brama między sieciami.

# Tworzenie dwóch sieci
docker network create frontend
docker network create backend

# Kontener w dwóch sieciach
docker run -d --name proxy \
  --network frontend \
  --network backend \
  nginx

# Sprawdzenie adresów IP w każdej sieci
docker inspect proxy | grep -A2 Networks
Multi-network Docker

Kontener z wieloma sieciami ma w każdej sieci osobny interfejs eth0, eth1 itd., każdy z własnym adresem IP. Docker zarządza routingiem między sieciami: gdy kontener w sieci frontend chce komunikować się z kontenerem w sieci backend, pakiet jest kierowany przez stos IP kontenera, który musi mieć włącone ip_forward.

Wielosieciowość jest często używana w architekturze mikroserwisów. Na przykład serwer proxy (nginx) jest podłączony do sieci frontend (dostępnej z Internetu) i sieci backend (prywatnej, dostępnej tylko dla wewnętrznych usług). Kontenery backendu nie mają dostępu do sieci frontend, co zapewnia warstwe bezpieczeństwa.

Docker nie pozwala na podłączenie kontenera do wielu sieci, jeśli mają one nakładające się podsieci. Każda sieć musi mieć unikalną podsieć IP, ponieważ kontener nie może mieć dwóch interfejsów w tej samej podsieci (powodowałoby to problemy z routingiem i ARP).

Zarządzanie wieloma sieciami odbywa się przez docker network connect <sieć> <kontener>. Kontener może być podłączony do dodatkowej sieci w dowolnym momencie, bez restartu. Odłączenie sieci: docker network disconnect <sieć> <kontener>.

Wielosieciowość ma wpływ na routing pakietów w kontenerze. Jeśli kontener ma dwa interfejsy z trasami domyślnymi, tylko jedna trasa domyślna jest aktywna (ta z niżsą metryką). Ruch wychodzący na zewnątrz obu sieci może być nieprzewidywalny, dlatego zaleca się staranne projektowanie topologii i jawne definiowanie tras.

30/50
Ograniczenia sieci mostka domyślnego

Problemy i słabości docker0

OgraniczenieOpisRozwiązanie
Brak DNSKontenery nie mogą komunikować się przez nazwyUżyj user-defined bridge
Słaba izolacjaKontenery widzą wszystkie porty innych kontenerówUser-defined bridge z --icc=false
Brak dynamicznego dołączaniaNie można podłączyć/odłączyć sieci bez restartuUser-defined bridge lub overlay
Globalna konfiguracjaZmiany w docker0 wpływają na wszystkie konteneryTwórz dedykowane sieci per aplikacja
Brak kontroli nad adresacjąAdresy IP przydzielane automatycznieUżyj --subnet i --ip w user-defined
Ograniczenia docker0

Domyślny mostek docker0 został zaprojektowany jako proste rozwiązanie do szybkiego uruchamiania kontenerów. Nie oferuje on zaawansowanych funkcji sieciowych, które są potrzebne w środowiskach produkcyjnych. Najwikszym ograniczeniem jest brak wbudowanego DNS - kontenery muszą znać adresy IP innych kontenerów, co jest niepraktyczne przy dynamicznym skalowaniu.

Izolacja między kontenerami w docker0 jest minimalna. Flaga --icc (inter-container communication) nie może być zmieniona dla domyślnego mostka bez restartu demona Dockera. Domyślnie --icc=true, co oznacza, że każdy kontener może połączyć się z dowolnym portem innego kontenera, co stanowi potencjalne ryzyko bezpieczeństwa.

Brak możliwości dynamicznego dołączania kontenera do sieci oznacza, że aby zmienić przynależność sieciową kontenera, trzeba go zatrzymać i uruchomić ponownie z odpowiednią flagą --network. W środowiskach produkcyjnych, gdzie wymagana jest ciągłość działania, jest to niedopuszczalne.

Globalna konfiguracja mostka docker0 oznacza, że zmiana parametrów (np. podsieci, MTU) wpływa na wszystkie kontenery w tej sieci. Jeśli różne aplikacje wymagają różnych konfiguracji sieciowych, nie ma możliwości ich rozdzielenia w ramach jednego mostka docker0.

Z tych powodów Docker zaleca używanie sieci zdefiniowanych przez użytkownika (user-defined bridge) dla wszystkich środowisk produkcyjnych. Sieci te oferują DNS, lepszą izolację, dynamiczne zarządzanie i pełną kontrolę nad konfiguracją adresacji IP.

31/50
Ujawnianie portów - mechanizm -p w Dockerze

Publikowanie portów kontenera na hoście

Flaga -p (lub --publish) w Dockerze służy do udostępniania portów kontenera na interfejsie hosta. Dzięki temu ruch z zewnątrz może dotrzeć do usług działających wewnątrz kontenera, który ma prywatny adres IP w sieci mostka docker0.

# Podstawowe użycie -p
docker run -d -p 8080:80 nginx

# Różne warianty -p
docker run -d -p 80:80 nginx             # port 80 na hoście -> 80 w kontenerze
docker run -d -p 127.0.0.1:8080:80 nginx # tylko localhost:8080
docker run -d -p 8080:80/udp nginx        # UDP
docker run -d -p 8080:80 -p 443:443 nginx # wiele portów
Publikowanie portów -p

Mechanizm -p jest jednym z najczęściej używanych, a jednocześnie najmniej rozumianych aspektów sieci Docker. Wielu użytkowników myli, że Docker po prostu przekierowuje ruch z portu hosta na port kontenera, ale w rzeczywistości dzieje się o wiele więcej: Docker tworzy reguły DNAT w iptables, które modyfikują docelowy adres IP i port w pakiecie.

Podstawowa składnia to -p <port_hosta>:<port_kontenera>. Docker nasłuchuje na porcie hosta (domyślnie na wszystkich interfejsach 0.0.0.0) i przekierowuje ruch do odpowiedniego kontenera. Port hosta może być inny niż port kontenera (np. -p 8080:80 mapuje port 8080 hosta na port 80 kontenera).

Opcjonalnie można ograniczyć nasłuchiwanie do konkretnego adresu IP hosta: -p 127.0.0.1:8080:80 sprawia, że port jest dostępny tylko z localhosta, a nie z sieci zewnętrznej. Jest to przydatne dla usług administracyjnych, które nie powinny być publicznie dostępne.

Domyślnie -p tworzy reguły dla protokołu TCP. Aby opublikować port UDP, należy dodać /udp: -p 8080:80/udp. Można również publikować zarówno TCP, jak i UDP, podając flage wielokrotnie lub używając zakresu portów: -p 8000-8010:8000-8010.

W przypadku użycia --network host flaga -p jest ignorowana, ponieważ kontener ma bezpośredni dostęp do interfejsów hosta i nie potrzebuje mapowania portów. Docker wyświetli ostrzeżenie, ale kontener uruchomi się bez opublikowanych portów.

32/50
Co Docker robi pod maską - DNAT w iptables

Translacja adresów docelowych w iptables

Gdy wykonujemy docker run -p 8080:80 nginx, Docker wykonuje następujące czynności w tle:

  1. Tworzy kontener z własną przestrzeni nazw i interfejsem VETH podłączonym do mostka
  2. Dodaje regułe DNAT w łańcuchu PREROUTING tabeli nat iptables
  3. Dodaje regułe DNAT w łańcuchu DOCKER tabeli nat
  4. Dodaje regułe ACCEPT w łańcuchu DOCKER-USER (opcjonalnie)
  5. Konfiguruje filtrowanie w łańcuchu FORWARD
DNAT w iptables

DNAT (Destination NAT) to mechanizm iptables, który modyfikuje docelowy adres IP i/lub port w pakiecie. Gdy pakiet przychodzi na interfejs hosta z zewnątrz, jego docelowy adres IP to adres hosta (np. 192.168.1.10), a docelowy port to opublikowany port (np. 8080). DNAT zmienia te wartości na adres IP kontenera (np. 172.17.0.2) i port wewnętrzny (np. 80).

Docker dodaje reguły DNAT w łańcuchu PREROUTING tabeli nat, ale nie bezpośrednio. Zamiast tego tworzy łańcuch DOCKER w tabeli nat i dodaje do niego reguły DNAT. W PREROUTING znajduje się reguła odsyłająca do DOCKER: -j DOCKER. Dzięki temu Docker może zarządzać swoimi regułami niezależnie od reguł użytkownika.

Kolejność reguł w iptables ma znaczenie. Jeśli użytkownik doda regułę PREROUTING przed regułami Dockera, może ona przechwycić ruch przeznaczony dla kontenera. Dlatego ważne jest, aby reguły Dockera były pierwsze w łańcuchu PREROUTING, co Docker zapewnia poprzez dodawanie swoich reguł na początku łańcucha.

Dodatkowo Docker dodaje reguły ACCEPT w łańcuchu FORWARD, aby umożliwia forwarding pakietów z zewnętrznych interfejsów do mostka docker0. Bez tych reguł domyślna polityka DROP w łańcuchu FORWARD zablokowałaby ruch przychodzący z zewnątrz do kontenerów.

Wszystkie te reguły są widoczne po wykonaniu iptables-save lub iptables -t nat -L -n --line-numbers. Zrozumienie struktury tych reguł jest kluczowe dla debugowania problemów z dostępem do usług w kontenerach.

33/50
Przykład: docker run -p 8080:80 nginx

Analiza reguł DNAT krok po kroku

Po wykonaniu docker run -d -p 8080:80 nginx, sprawdmy reguły iptables:

# Reguły DNAT dla nowego kontenera
iptables -t nat -L DOCKER -n -v
Chain DOCKER (2 references)
 pkts bytes target     prot opt in     out     source
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0
         ADDRTYPE match dst-type LOCAL
         tcp dpt:8080 to:172.17.0.2:80

# Reguły FORWARD dla kontenera
iptables -L DOCKER -n -v
Chain DOCKER (1 references)
 pkts bytes target     prot opt in     out     source
    0     0 ACCEPT     tcp  --  !docker0 docker0
         0.0.0.0/0    172.17.0.2   tcp dpt:80
Reguły DNAT dla nginx

Analiza pierwszej reguły: jest to reguła DNAT w łańcuchu DOCKER tabeli nat. Mówi ona: dla pakietów TCP przychodzących na interfejs inny niż docker0 (czyli z zewnątrz), których docelowy port to 8080, zmien docelowy adres IP na 172.17.0.2, a docelowy port na 80. Warunek ADDRTYPE match dst-type LOCAL zapewnia, że reguła działa tylko dla pakietów adresowanych do lokalnego hosta.

Druga reguła to ACCEPT w łańcuchu DOCKER tabeli filter. Pozwala ona na forwarding pakietów TCP z dowolnego interfejsu (poza docker0) na interfejs docker0 z docelowym adresem IP kontenera 172.17.0.2 na porcie 80. Bez tej reguły domyślna polityka FORWARD (DROP) zablokowałaby ruch.

Po DNAT pakiet ma zmodyfikowany docelowy adres IP. Gdy pakiet dociera do mostka docker0, mostek sprawdza tablicę FDB i przekazuje ramkę na odpowiedni interfejs VETH prowadzący do kontenera. W kontenerze stos IP odbiera pakiet z adresem docelowym 172.17.0.2:80, co odpowiada nasłuchującemu procesowi nginx.

W odpowiedzi kontener wysyła pakiet z adresem źródłowym 172.17.0.2:80. Ten pakiet przechodzi przez mostek docker0 i trafia do stosu IP hosta. W łańcuchu POSTROUTING zachodzi masquerada (SNAT), która zmienić źródłowy adres na adres interfejsu zewnętrznego hosta, aby odpowiedź mogła wrócić do klienta.

Dzięki temu cały mechanizm jest transparentny dla użytkownika: klient łączy się z hostem:8080, a odpowiedź wraca do klienta, bez wiedzy o tym, że wewnątrz działa kontener z przekierowaniem portów.

34/50
Automatyczne zarządzanie iptables przez Docker

Jak Docker utrzymuje reguły iptables

Demon Dockera (dockerd) automatycznie zarządza regułami iptables w odpowiedzi na zdarzenia takie jak:

  • docker run -p: Dodaje reguły DNAT i ACCEPT
  • docker stop/kill: Usuwa reguły dla zatrzymanego kontenera
  • docker network create: Tworzy reguły dla nowej sieci
  • docker network connect: Aktualizuje reguły dla sieci wielokontenerowych
Ostrzeżenie: Ręczna modyfikacja reguł iptables dodanych przez Docker jest ryzykowna. Docker może nadpisać zmiany przy następnej operacji na kontenerze. Wszelkie własne reguły dodawaj do łańcucha DOCKER-USER.
Automatyczne iptables Dockera

Demon Dockera uruchamia wewnętrzny komponent o nazwie Docker Proxy (docker-proxy), który jest odpowiedzialny za implementacje publikowania portów w starszych wersjach. W nowszych wersjach (od Dockera 20.10) domyślnie używane są reguły iptables zamiast dockera-proxy, ale opcja --userland-proxy jest nadal dostępna dla zachowania wstecznej kompatybilności.

Docker używa mechanizmu Netlink do monitorowania zdarzeń w systemie. Gdy kontener jest uruchamiany, Docker wywołuje funkcje jądra do tworzenia interfejsów VETH, mostków i reguł iptables. Operacje te są wykonywane atomowo: jeśli która z nich się nie powiedzie, cała operacja uruchomienia kontenera jest przerywana.

Wszystkie reguły iptables są przechowywane w łańcuchach DOCKER (tabela nat i filter) oraz DOCKER-USER (tabela filter). Docker nigdy nie modyfikuje reguł w łańcuchu DOCKER-USER - jest on przeznaczony dla własnych reguł administratora systemu, które są sprawdzane przed regułami Dockera.

Jeśli chcemy całkowicie wyłączyć zarządzanie iptables przez Docker, możemy ustawia w pliku /etc/docker/daemon.json opcje "iptables": false. Wówczas Docker nie będzie tworzyć żadnych reguł, a my będziemy musieli ręcznie skonfigurować iptables dla publikowania portów i masquerady.

Od Dockera 20.10 dostępna jest również opcja "ip6tables": true do zarządzania regułami IPv6. Docker może zarządzać zarówno iptables (IPv4), jak i ip6tables (IPv6) jednocześnie, z zachowaniem symetrycznych reguł dla obu protokołów.

35/50
Sprawdzanie reguł iptables dla Dockera

Polecenia do inspekcji iptables

# Wywietlenie regułę NAT (DNAT dla publikowanych portów)
iptables -t nat -L DOCKER -n --line-numbers

# Wywietlenie regułę filter (ACCEPT dla forwardingu)
iptables -L DOCKER -n --line-numbers

# Wywietlenie regułę masquerady (SNAT dla ruchu wychodzącego)
iptables -t nat -L POSTROUTING -n

# Cała konfiguracja iptables w formacie zrozumiałym dla iptables-restore
iptables-save
Sprawdzanie iptables Dockera

Polecenie iptables -t nat -L DOCKER -n --line-numbers wyświetla reguły DNAT w łańcuchu DOCKER tabeli nat. Opcja -n wyłącza rozwiązywanie nazw (szybsze wyświetlanie), a --line-numbers pokazuje numery linii, co ułatwia późniejsze modyfikowanie reguł. W łańcuchu tym znajdują się reguły dla każdego opublikowanego portu.

Polecenie iptables -L DOCKER -n --line-numbers wyświetla reguły ACCEPT w łańcuchu DOCKER tabeli filter. Te reguły zezwalają na forwarding pakietów z zewnętrznych interfejsów do kontenerów. Dla każdego kontenera z opublikowanymi portami istnieje osobna reguła ACCEPT.

Polecenie iptables -t nat -L POSTROUTING -n wyświetla reguły masquerady, które są odpowiedzialne za SNAT ruchu wychodzącego z kontenerów. Zazwyczaj jest jedna reguła MASQUERADE dla podsieci mostka docker0 (172.17.0.0/16) oraz opcjonalne reguły dla sieci user-defined.

Polecenie iptables-save wyświetla całą konfigurację iptables w formacie, który można załadować za pomocą iptables-restore. Jest to najdokładniejszy sposób na podgląd wszystkich reguł, łącznie z tymi, które nie są widoczne w poszczególnych łańcuchach (np. reguły w INPUT, FORWARD, OUTPUT).

Dla IPv6 używamy analogicznych polecen: ip6tables -t nat -L DOCKER -n. Docker tworzy symetryczne reguły dla IPv6, jeśli w konfiguracji włączono ip6tables i w systemie dostępne jest IPv6.

36/50
ancuch DOCKER w iptables

Struktura i działanie łańcucha DOCKER

Docker tworzy trzy główne łańcuchy w iptables: DOCKER (tabela nat), DOCKER (tabela filter) oraz DOCKER-USER (tabela filter). Kolejność sprawdzania reguł ma kluczowe znaczenie dla poprawności działania.

# Kolejność w tabeli nat (PREROUTING)
PREROUTING -> DOCKER -> ... (DNAT trafia tutaj)

# Kolejność w tabeli filter (FORWARD)
FORWARD -> DOCKER-USER -> DOCKER -> ...

# Przykładowa reguła odsyłająca w FORWARD
iptables -L FORWARD -n -v
Chain FORWARD (policy DROP)
    DOCKER-USER  all  --  *      *       0.0.0.0/0  0.0.0.0/0
    DOCKER-ISOLATION  all  --  *      *    0.0.0.0/0  0.0.0.0/0
    DOCKER     all  --  *      *       0.0.0.0/0  0.0.0.0/0
ancuch DOCKER w iptables

W tabeli nat łańcuch DOCKER jest wywoływany z PREROUTING i OUTPUT. Reguła w PREROUTING odsyła pakiety do łańcucha DOCKER przed podjeciem decyzji routingu, co jest kluczowe dla DNAT - musimy zmienić docelowy adres IP przed routingiem, aby pakiet został przekazany do kontenera, a nie do lokalnego procesu hosta.

W tabeli filter łańcuch DOCKER jest wywoływany z FORWARD. Docker dodaje reguły ACCEPT, które zezwalają na forwarding pakietów z zewnętrznych interfejsów do kontenerów. Domyślna polityka FORWARD to zazwyczaj DROP (w systemach z włąconym firewallem), wiec bez tych reguł ruch do kontenerów byłby blokowany.

System izolacji DOCKER-ISOLATION zapobiega komunikacji między kontenerami w różnych sieciach. Jest to dodatkowy łańcuch wywoływany z FORWARD, który sprawdza, czy pakiet nie próbuje przejść z jednej sieci Docker do drugiej bez odpowiedniego zezwolenia.

W łańcuchu DOCKER-USER administrator może dodawać własne reguły, które będą sprawdzane przed regułami Dockera. Na przykład, jeżeli chcemy zablokować cały ruch przychodzący do kontenerów z wyjątkiem konkretnych adresów IP, dodajemy odpowiednie reguły DROP na początku łańcucha DOCKER-USER.

Kolejność reguł w łańcuchu DOCKER (tabela nat) jest zgodna z kolejnością uruchamiania kontenerów. Każda reguła DNAT ma warunek, który sprawdza docelowy port TCP/UDP oraz interfejs przychodzący. Reguły są sprawdzane od góry do dołu, wiec pierwsze dopasowanie wygrywa.

37/50
Port publishing z ograniczeniem do konkretnego IP

Ograniczanie dostępu do publikowanych portów

Flaga -p pozwala na ograniczenie publikowanego portu do konkretnego adresu IP hosta. Jest to przydatne, gdy chcemy, aby usługa była dostępna tylko z lokalnej sieci wewnętrznej, a nie z Internetu.

# Port dostępny tylko z localhosta
docker run -d -p 127.0.0.1:8080:80 nginx

# Port dostępny tylko z konkretnej sieci wewnętrznej
docker run -d -p 10.0.0.1:8080:80 nginx

# Sprawdzenie reguły DNAT z ograniczeniem IP
iptables -t nat -L DOCKER -n
DNAT tcp -- !docker0 *  127.0.0.1   tcp dpt:8080 to:172.17.0.2:80
Ograniczenie IP dla portów

Gdy określamy adres IP w flagach -p, Docker tworzy regułę DNAT z dodatkowym warunkiem na docelowy adres IP. W przykładzie -p 127.0.0.1:8080:80 reguła DNAT będzie pasować tylko do pakietów, których docelowym adresem IP jest 127.0.0.1 (localhost). Oznacza to, że tylko procesy na hoście mogą łączyć się z tym portem, a ruch z zewnętrznych interfejsów sieciowych zostanie odrzucony.

Jeśli określimy adres IP interfejsu wewnętrznego (np. 10.0.0.1), usługa będzie dostępna tylko z sieci wewnętrznej, ale nie z Internetu. Jest to często używane w architekturze z wieloma interfejsami: jeden interfejs jest publiczny, a drugi wewnętrzny. Usługi backendowe publikujemy tylko na wewnętrznym interfejsie.

W regule DNAT warunek na docelowy adres IP pojawia się przed warunkiem na port. Polecenie iptables -t nat -L DOCKER -n pokaże regułe z adresem IP w kolumnie destination. Jeśli adres nie jest określony, kolumna destination zawiera 0.0.0.0/0 (dopasowuje wszystkie adresy).

Docker nie tworzy automatycznie reguł w łańcuchu INPUT, co oznacza, że niezależnie od ograniczenia IP w DNAT, lokalne procesy na hoście mogą łączyć się z publikowanym portem przez localhost. Aby ograniczyć również dostęp lokalny, należy dodać własne reguły do łańcucha DOCKER-USER lub skonfigurować firewall systemowy.

W praktyce ograniczenie do konkretnego adresu IP jest używane dla usług takich jak serwery baz danych (Redis, PostgreSQL), interfejsy administracyjne (phpMyAdmin, Adminer) czy serwery metryk (Prometheus, Grafana), które nie powinny być wystawione na publiczny Internet.

38/50
Różnica między -p a --publish-all

Publikowanie selektywne vs wszystkie porty

Cecha-p (--publish)-P (--publish-all)
SelektywnośćRęczne wskazanie portówAutomatyczne publikowanie wszystkich portów z Dockerfile (EXPOSE)
Port hostaOkrelony przez użytkownikaLosowy port z zakresu 32768-60999
KontrolaPełna kontrola nad mapowaniemBrak kontroli - porty przydzielane losowo
ZastosowanieProdukcja, znane portyTestowanie, development (unikamy konfliktów)
Porównanie -p i -P

Flaga -p (lub --publish) wymaga jawnego określenia mapowania portów. Składnia to -p hostPort:containerPort z opcjonalnym adresem IP hosta i protokołem. Użytkownik ma pełną kontrolę nad tym, które porty są publikowane i na jakich portach hosta są dostępne. Jest to zalecane w środowiskach produkcyjnych.

Flaga -P (lub --publish-all) publikuje automatycznie wszystkie porty zadeklarowane w Dockerfile instrukcją EXPOSE. Dla każdego portu z EXPOSE Docker przydziela losowy port z zakresu 32768-60999 na interfejsie 0.0.0.0 hosta. Mapowanie można sprawdzić poleceniem docker port <kontener>.

-P jest przydatny w środowiskach deweloperskich, gdzie wiele kontenerów jest uruchamianych na jednym hoście i chcemy uniknąć konfliktów portów. Każdy kontener otrzymuje unikalne porty zewnętrzne niezależnie od wewnętrznych portów aplikacji.

W przypadku użycia -P, Docker tworzy reguły DNAT analogicznie jak dla -p, ale z losowym portem hosta. Reguły iptables będą zawierać DNAT z portem hosta przydzielonym losowo. Jest to mniej przewidywalne, ale wygodne podczas szybkiego testowania.

W środowiskach produkcyjnych zaleca się używanie -p z jawnie określonymi portami, co ułatwia zarządzanie regułami firewalla, monitorowanie ruchu i konfigurację równoważenia obciążenia. -P jest używane głównie w developmentcie i podczas demonstracji.

39/50
Wprowadzenie do laboratorium

Cel i środowisko laboratoryjne

W tej części wykonamy ręczną symulację tego, co Docker robi automatycznie. Celem jest zrozumienie mechanizmów działania sieci kontenerowej poprzez odtworzenie jej komponentów krok po kroku za pomocą standardowych narzędzi Linux.

  • Utworzymy przestrzenie nazw sieciowych (ip netns add)
  • Stworzymy pary VETH (ip link add type veth peer)
  • Skonfigurujemy mostek Linux (ip link add type bridge)
  • Przypiszemy adresy IP i uruchomimy usługi
  • Zweryfikujemy łączność pingiem i curl
  • Sprawdzimy reguły iptables (symulacja -p)
Wprowadzenie do laboratorium

Laboratorium jest zaprojektowane tak, aby uczestnik mógł krok po kroku odtworzyć identyczną konfigurację sieciową, jaką Docker tworzy automatycznie dla kontenera w trybie bridge. Zrozumienie każdego kroku pozwoli na wiadome debugowanie problemów sieciowych w środowisku kontenerowym.

Uczestnik będzie pracować na systemie Linux z zainstalowanymi narzędziami iproute2 (ip, bridge), iptables i opcjonalnie tcpdump. Wszystkie polecenia wymagają uprawnień root (sudo). Środowisko może być maszyną wirtualną, kontenerem uprzywilejowanym lub fizycznym hostem Linux.

Topologia laboratorium będzie składać się z dwóch przestrzeni nazw (ns1, ns2) połączonych przez mostek br0. Każda przestrzeń będzie miała interfejs VETH z adresem IP w podsieci 192.168.100.0/24. Mostek otrzyma adres 192.168.100.1/24, symulując bramę docker0.

Po skonfigurowaniu podstawowej łączności, dodamy reguły iptables DNAT, aby opublikować port z jednej przestrzeni nazw na porcie hosta, tak jak robi to Docker z flagą -p. Na koniec sprawdzimy poprawność działania za pomocą narzędzi diagnostycznych.

Wszystkie polecenia będą wykonywane w terminalu z uprawnieniami root. Uczestnik powinien mieć otwarty edytor tekstu do notowania wyników i ewentualnych błędów. Po zakończeniu laboratorium wszystkie zasoby zostaną posprzątane (usunięte przestrzenie nazw, mostek i pary VETH).

40/50
Krok 1: Tworzenie przestrzeni nazw

ip netns add

# Krok 1: Tworzenie przestrzeni nazw i mostka
sudo ip netns add ns1
sudo ip netns add ns2
sudo ip link add br0 type bridge
sudo ip link set br0 up
sudo ip addr add 192.168.100.1/24 dev br0

# Sprawdzenie
sudo ip netns list
sudo ip link show br0
sudo ip addr show br0
Tworzenie przestrzeni nazw

W pierwszym kroku tworzymy dwie przestrzenie nazw sieciowych: ns1 i ns2. Polecenie sudo ip netns add ns1 tworzy nową przestrzeń nazw o nazwie ns1. W systemie pojawia się plik /var/run/netns/ns1, który jest punktem montowania dla przestrzeni nazw. Każda przestrzeń początkowo zawiera tylko interfejs pętli zwrotnej (lo) w stanie DOWN.

Następnie tworzymy mostek sieciowy br0. Mostek ten będzie pełnił role wirtualnego przełącznika L2, do którego podłączymy obie przestrzenie nazw. Adres IP 192.168.100.1/24 przypisany do br0 będzie bramą domyślną dla przestrzeni nazw, analogicznie jak docker0 z adresem 172.17.0.1/16.

Stan mostka można sprawdzić poleceniem ip link show br0. Powinien być w stanie UP (flaga UP w wynikach). Adres IP mostka jest widoczny po ip addr show br0. Mostek jest gotowy do przyjmowania portów członkowskich.

W tej chwili przestrzenie nazw nie mają jeszcze żadnego interfejsu poza lo. Próba pingowania czegokolwiek z ns1 zakończy się błędem, ponieważ nie ma trasy do sieci zewnętrznej. Interfejs lo w ns1 można podnieść dla testów lokalnych: sudo ip netns exec ns1 ip link set lo up.

Lista wszystkich przestrzeni nazw jest dostępna przez sudo ip netns list. W środowisku laboratoryjnym mogą istnieć inne przestrzenie nazw utworzone przez Docker (jeżeli jest zainstalowany). Nasze przestrzenie ns1 i ns2 powinny być widoczne.

41/50
Krok 2: Tworzenie par VETH i podłączanie do mostka

ip link add type veth peer

# Krok 2: Tworzenie par VETH
sudo ip link add veth1 type veth peer name veth1-br
sudo ip link add veth2 type veth peer name veth2-br

# Podłączenie do mostka
sudo ip link set veth1-br master br0
sudo ip link set veth2-br master br0

# Przeniesienie do przestrzeni nazw
sudo ip link set veth1 netns ns1
sudo ip link set veth2 netns ns2

# Podniesienie interfejsów po stronie mostka
sudo ip link set veth1-br up
sudo ip link set veth2-br up
Tworzenie VETH i podłączanie

W drugim kroku tworzymy pary VETH. Dla każdej przestrzeni nazw potrzebujemy osobnej pary. Para veth1-veth1-br jest przeznaczona dla ns1: veth1 trafi do przestrzeni ns1, a veth1-br zostanie podłączony do mostka br0. Analogicznie veth2-veth2-br dla ns2.

Polecenie sudo ip link add veth1 type veth peer name veth1-br tworzy dwa interfejsy. Po utworzeniu oba znajdują się w root network namespace. Następnie veth1-br jest dodawany do mostka za pomocą ip link set veth1-br master br0, a veth1 jest przenoszony do ns1 za pomocą ip link set veth1 netns ns1.

Ważne jest, aby przed przeniesieniem do przestrzeni nazw nie konfigurować adresów IP na interfejsie, ponieważ zostaną one utracone podczas przenoszenia. Konfiguracja adresacji powinna odbywać się już w docelowej przestrzeni nazw.

Interfejsy po stronie mostka (veth1-br, veth2-br) muszą być podniesione do stanu UP, ale nie przypisujemy im adresów IP - działają wyłącznie na poziomie L2. Ich zadaniem jest przekazywanie ramek między mostkiem a parą VETH.

Stan interfejsów można sprawdzić poleceniem ip link show master br0, które pokaże wszystkie interfejsy podłączone do mostka. Po podniesieniu interfejsów mostek powinien je widzieć jako porty członkowskie w stanie UP.

42/50
Krok 3: Konfiguracja adresacji IP

Adresy IP w przestrzeniach nazw

# Krok 3: Konfiguracja adresów IP w przestrzeniach nazw
sudo ip netns exec ns1 ip addr add 192.168.100.2/24 dev veth1
sudo ip netns exec ns1 ip link set veth1 up
sudo ip netns exec ns1 ip link set lo up

sudo ip netns exec ns2 ip addr add 192.168.100.3/24 dev veth2
sudo ip netns exec ns2 ip link set veth2 up
sudo ip netns exec ns2 ip link set lo up

# Sprawdzenie konfiguracji
sudo ip netns exec ns1 ip addr show veth1
sudo ip netns exec ns2 ip addr show veth2
Konfiguracja adresacji IP

Trzeci krok to konfiguracja adresów IP w przestrzeniach nazw. Używamy polecenia sudo ip netns exec ns1 ... do wykonania polecen w kontekcie przestrzeni nazw ns1. Interfejsowi veth1 w ns1 przypisujemy adres 192.168.100.2/24, a veth2 w ns2 adres 192.168.100.3/24.

Po przypisaniu adresu należy podnieść interfejs do stanu UP: ip link set veth1 up. Bez tego interfejs nie będzie przetwarzał pakietów. Dodatkowo warto podnieść interfejs lo (petla zwrotna), co umożliwi komunikację wewnętrzną w przestrzeni nazw (np. localhost).

Sprawdzenie poprawności konfiguracji: ip netns exec ns1 ip addr show veth1 powinno pokazać adres 192.168.100.2/24 na interfejsie veth1 w stanie UP. Analogicznie dla ns2. Mostek br0 ma adres 192.168.100.1/24, który będzie bramą domyślną dla obu przestrzeni.

W tym momencie tablica routingu w ns1 (ip netns exec ns1 ip route show) powinna zawierać trase do podsieci 192.168.100.0/24 przez interfejs veth1. Nie ma jeszcze trasy domyślnej - dodamy ją, jeśli przestrzenie mają mieć dostęp do Internetu przez mostek.

Adresacja IP w laboratorium (192.168.100.0/24) jest celowo inna niż domyślna podsieć Docker (172.17.0.0/16), aby uniknąć konfliktów, jeśli Docker jest zainstalowany na tym samym hoście. W środowisku produkcyjnym można użyć dowolnej prywatnej podsieci.

43/50
Krok 4: Test łączności

Ping między przestrzeniami nazw

# Krok 4: Test łącznoci
sudo ip netns exec ns1 ping -c 3 192.168.100.3
PING 192.168.100.3 (192.168.100.3) 56(84) bytes of data.
64 bytes from 192.168.100.3: icmp_seq=1 ttl=64 time=0.089 ms
64 bytes from 192.168.100.3: icmp_seq=2 ttl=64 time=0.072 ms
64 bytes from 192.168.100.3: icmp_seq=3 ttl=64 time=0.095 ms

# Ping do bramy (mostka)
sudo ip netns exec ns1 ping -c 1 192.168.100.1

# Sprawdzenie izolacji: root netns nie powinien pingowaa ns1
ping -c 1 192.168.100.2
# Wynik: Network is unreachable
Test łączności ping

Po skonfigurowaniu adresacji IP już łączność między przestrzeniami nazw powinna działać. Ping z ns1 (192.168.100.2) do ns2 (192.168.100.3) powinien zwrócić odpowiedzi z czasem rzędu dziesiętnych części milisekundy, co potwierdza, że ramki przechodzą przez mostek w pamięci RAM bez opóźnień sieci fizycznej.

Mostek pełni role bramy: ping z ns1 do adresu mostka (192.168.100.1) również powinien działać. Oznacza to, że mostek jest osiągalny jako brama domyślna. Jeśli skonfigurujemy trase domyślną w ns1 (ip route add default via 192.168.100.1), przestrzeń będzie mogła łączyć się z siecią zewnętrzną.

Izolacja polega na tym, że root network namespace nie ma bezpośredniego dostępu do interfejsów w ns1 i ns2. Ping z hosta do 192.168.100.2 powinien zwrócić błąd Network is unreachable, ponieważ adres 192.168.100.2 znajduje się w przestrzeni ns1 i nie jest widoczny z root netns.

Widoczność mostka br0 z root netns jest możliwa, ponieważ mostek znajduje się w root netns. Można pingować adres mostka (192.168.100.1) z hosta, ale nie adresy interfejsów veth w przestrzeniach nazw. To właśnie zapewnia izolację sieciową kontenerów.

Do monitorowania ruchu na moście używamy sudo tcpdump -i br0 -n icmp. Powinien pokazać pakiety ICMP Echo Request i Echo Reply przechodzące przez mostek podczas pingowania między przestrzeniami. Jest to doskonałe narzędzie do weryfikacji, czy mostek prawidłowo przekazuje ramki.

44/50
Krok 5: Symulacja -p z iptables DNAT

Ręczne dodanie reguły DNAT

# Krok 5: Symulacja -p 8080:80
# Uruchom serwer HTTP w ns1 na porcie 80
sudo ip netns exec ns1 python3 -m http.server 80 &
# (lub nc -l -p 80 -v w ns1)

# Dodanie reguły DNAT - symulacja -p 8080:80
sudo iptables -t nat -A PREROUTING -p tcp --dport 8080 \
    -j DNAT --to-destination 192.168.100.2:80

# Dodanie reguły FORWARD
sudo iptables -A FORWARD -p tcp -d 192.168.100.2 --dport 80 \
    -j ACCEPT

# Test z hosta
curl http://localhost:8080  # Powinien pokazaa zawartość serwera ns1
Symulacja DNAT

W pitym kroku symulujemy mechanizm -p z Dockera: publikujemy port 80 z przestrzeni ns1 na porcie 8080 hosta. W kontenerze ns1 uruchamiamy prosty serwer HTTP (może to być python -m http.server, nginx, lub netcat). Następnie ręcznie dodajemy reguły iptables, które Docker dodałby automatycznie.

Reguła DNAT: iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.100.2:80. Mówi ona: wszystkie pakiety TCP przychodzące na port 8080 hosta mają mieć zmieniony docelowy adres IP na 192.168.100.2 i docelowy port na 80. To dokładnie to samo, co robi Docker z -p 8080:80.

Reguła FORWARD: iptables -A FORWARD -p tcp -d 192.168.100.2 --dport 80 -j ACCEPT. Zezwala na forwarding pakietów do kontenera. Domyślna polityka FORWARD to DROP, więc bez tej reguły pakiety po DNAT zostałyby odrzucone przy próbie przekazania do interfejsu mostka.

Po dodaniu reguł, curl http://localhost:8080 z hosta powinien pokazać odpowiedź z serwera HTTP uruchomionego w ns1. Jest to analogiczne do docker run -p 8080:80 nginx, ale wykonane ręcznie, krok po kroku.

Po zakończeniu testu usuwamy reguły: iptables -t nat -D PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.100.2:80 i iptables -D FORWARD -p tcp -d 192.168.100.2 --dport 80 -j ACCEPT. Kończymy również proces serwera w ns1.

45/50
Zapętlenia w mostku i STP

Spanning Tree Protocol w Linux bridge

Zapętlenia w mostku powstają, gdy istnieje wiele ścieżek transmisyjnych między mostkami. W sieci fizycznej zapętlenia są wynikiem nadmiarowości (redundant links). W mostku wirtualnym zapętlenie może powstać przez nieprawidłową konfigurację lub połączenie mostka Linux z fizycznym przełącznikiem.

# Włączenie STP na mostku
sudo ip link set br0 type bridge stp_state 1

# Alternatywnie przez brctl
sudo brctl stp br0 on

# Sprawdzenie stanu STP
sudo bridge link show
STP w mostku

Spanning Tree Protocol (STP) zgodny z IEEE 802.1D jest zaimplementowany w mostku Linux i może być włącony dla każdego mostka niezależnie. STP zapobiega powstawaniu pętli w sieci Ethernet poprzez blokowanie nadmiarowych portów. Algorytm STP wybiera jeden mostek główny (root bridge) i blokuje porty, które tworzyłyby cykl.

W mostku Linux STP jest domyślnie wyłączony, ponieważ w typowych konfiguracjach wirtualizacyjnych (mostek z kontenerami) topologia jest drzewiasta. Jednakże, gdy mostek Linux jest połączony z fizycznym przełącznikiem obsługującym STP, a istnieją redundantne połączenia, włączenie STP jest niezbędne.

Objawy pętli w mostku: wysokie obciążenie CPU (kernel processing), broadcast storm (ramki broadcast rozchodzą się w nieskończoność), MAC flapping (ten sam adres MAC pojawia się na różnych portach). W skrajnych przypadkach sieć może stać się niedostępna.

Polecenie bridge link show pokazuje stan STP dla każdego portu mostka. W kolumnie stp_state wartość 0 oznacza STP wyłączone, 1 - STP włącone. Dodatkowo bridge -d link show pokazuje szczegółowe informacje o stanie każdego portu: role (Root, Designated, Alternate, Backup) i stan (Forwarding, Listening, Learning, Blocking, Disabled).

Czas konwergencji STP w mostku Linux wynosi domyślnie 30 sekund (MAX_AGE + FORWARD_DELAY), co może być zbyt długie w środowiskach wirtualizacyjnych. Można go skrócić przez zmianę parametrów mostka: bridge link set dev veth1-br max_age 6 i bridge link set dev veth1-br forward_delay 4.

46/50
Błędy konfiguracji VETH

Problemy z VETH i typowe błędy

BłądObjawPrzyczyna
Brak pary VETHInterfejs nie pojawia się w przestrzeni nazwPrzed przeniesieniem nie sprawdzono istnienia pary
Interfejs DOWNPing nie działa, Network is unreachableZapomniano podnieść interfejs (ip link set up)
Brak adresu IPPing nie działa, Destination unreachableNie przypisano adresu IP w przestrzeni nazw
Zły masterInterfejs nie jest w mostkuNie dodano do mostka (ip link set master br0)
Dwie pary skrzyżowaneLosowe problemy z łącznościąPomylono veth1 z veth2 podczas podłączania
Błędy konfiguracji VETH

Najczęstszym błędem przy pracy z VETH jest zapomnienie o podniesieniu interfejsu do stanu UP. Po utworzeniu pary i przeniesieniu do przestrzeni nazw, interfejs jest w stanie DOWN. Bez jawnego wywołania ip link set veth1 up (w kontekcie przestrzeni nazw), interfejs nie będzie przetwarzał pakietów, a ping zwróci błąd Network is unreachable.

Drugim częstym błędem jest przypisanie adresu IP do interfejsu przed przeniesieniem go do docelowej przestrzeni nazw. Adres IP jest tracony podczas przenoszenia, ponieważ konfiguracja interfejsu jest resetowana. Należy najpierw przenieść interfejs, a następnie skonfigurować adresację już w docelowej przestrzeni.

Problemy z mostkiem: interfejs veth1-br musi być dodany do mostka za pomocą ip link set veth1-br master br0, a następnie podniesiony. Bez tego mostek nie będzie widział interfejsu jako portu członkowskiego, co można sprawdzić poleceniem ip link show master br0.

Pomylenie interfejsów VETH między przestrzeniami nazw prowadzi do trudnych do zdiagnozowania problemów. Zaleca się stosowanie czytelnych nazw: veth1 dla ns1, veth1-br dla strony mostka ns1, veth2 dla ns2, veth2-br dla strony mostka ns2. Nazwy powinny jednoznacznie wskazywać, która przestrzeń do której pary należy.

Do debugowania problemów z VETH używamy: ip netns exec ns1 ip link show (sprawdzenie interfejsów w przestrzeni), ip link show master br0 (sprawdzenie portów mostka), bridge fdb show br br0 (sprawdzenie tablicy FDB), tcpdump -i veth1-br (nasłuch na interfejsie mostka).

47/50
Problemy z DNAT w Dockerze

Kolejność reguł iptables

Najczęstsze problemy z publikowaniem portów w Dockerze wynikają z nieprawidłowej kolejnoci reguł iptables lub konfliktów z innymi regułami firewalla.

# Problem: Port jest zajęty
Error starting userland proxy: listen tcp4 0.0.0.0:80: bind: address already in use

# Rozwiązanie: ZnaleYa proces zajmujący port
sudo netstat -tulpn | grep :80
sudo lsof -i :80

# Problem: DNAT nie działa (reguła za późno)
# Sprawdzenie kolejności reguł
iptables -t nat -L PREROUTING -n --line-numbers
Problemy z DNAT

Problem bind: address already in use występuje, gdy port hosta, który chcemy opublikować, jest już zajęty przez inny proces (np. inny kontener, serwer WWW na hoście, lub wcześniejszy kontener, który nie został poprawnie zatrzymany). Rozwiązaniem jest znalezienie procesu zajmującego port (netstat -tulpn, lsof -i) i zatrzymanie go, lub użycie innego portu hosta.

Drugim częstym problemem jest nieprawidłowa kolejność reguł w łańcuchu PREROUTING. Jeśli inna reguła (np. REDIRECT, DNAT ustawiona ręcznie) znajduje się przed regułami Dockera, może przechwycić ruch przeznaczony dla kontenera. W takim przypadku reguła Dockera nigdy nie zostanie dopasowana, a ruch nie trafi do kontenera.

Docker dodaje swoje reguły na początku łańcucha PREROUTING, ale tylko podczas restartu demona (dockerd). Reguły dodane ręcznie przez administratora po restarcie Dockera mogą znaleźć się przed regułami Dockera, blokując je. Rozwiązaniem jest dodawanie własnych reguł do łańcucha DOCKER-USER, który jest sprawdzany przed DOCKER.

Problem z masqueradą: kontener może łączyć się z Internetem, ale odpowiedzi nie wracają. Jest to zazwyczaj spowodowane brakiem reguły MASQUERADE lub nieprawidłową konfiguracją routingu na hoście. Sprawdzenie: iptables -t nat -L POSTROUTING -n -v powinno pokazać regułę MASQUERADE dla podsieci Docker.

Problem z izolację: kontenery w różnych sieciach mogą komunikować się między sobą, co stanowi zagrożenie bezpieczeństwa. Docker dodaje reguły w łańcuchu DOCKER-ISOLATION, które blokują taką komunikację. Jeśli reguły te są nieprawidłowe lub brakuje ich, izolacja nie działa. Sprawdzenie: iptables -L DOCKER-ISOLATION -n -v.

48/50
Narzędzia do debugowania

Zestaw narzędzi diagnostycznych

NarzędzieZastosowaniePrzykład
tcpdumpPrzechwytywanie pakietów na interfejsietcpdump -i br0 -n icmp
ipKonfiguracja i diagnostyka interfejsówip link show, ip netns exec
bridgeZarządzanie mostkiem i tablicą FDBbridge fdb show, bridge link show
nsenterWejcie do przestrzeni nazw procesunsenter -t PID -n ip addr show
iptablesInspekcja reguł firewalla i NATiptables -t nat -L -n -v
docker inspectSzczegółowa konfiguracja konteneradocker inspect web1 | grep IPAddress
Narzędzia debugowania

tcpdump to podstawowe narzędzie do przechwytywania i analizy ruchu sieciowego. Przy debugowaniu mostka używamy tcpdump -i br0 -n do podglądu wszystkich ramek przechodzących przez mostek. Flaga -n wyłącza rozwiązywanie nazw DNS, co przyspiesza wyświetlanie. Można filtrować po protokole (icmp, tcp, udp), adresie IP (host 192.168.100.2) lub porcie (port 80).

iproute2 to zestaw narzędzi do konfiguracji sieci. Polecenia ip link, ip addr, ip route, ip netns są niezastąpione przy diagnostyce. Szczególnie przydatne: ip netns exec ns1 ip addr show (sprawdzenie interfejsów w przestrzeni), ip route show (tablica routingu), ip link show master br0 (porty mostka).

bridge to narzędzie do zarządzania mostkiem. Polecenie bridge fdb show wyświetla tablicę FDB, co pozwala sprawdzić, czy mostek nauczył się adresów MAC. bridge link show pokazuje stan portów, łącznie z STP. bridge monitor pokazuje zmiany w czasie rzeczywistym.

nsenter pozwala na wejście do przestrzeni nazw dowolnego procesu w systemie. Jest to szczególnie przydatne, gdy chcemy zbadaa konfigurację sieciową działającego kontenera Docker bez używania docker exec. Składnia: nsenter -t PID -n ip addr show, gdzie PID to numer procesu kontenera (można go znaleźć przez docker inspect).

docker inspect wyświetla szczegółową konfigurację kontenera w formacie JSON. Można je filtrować przez grep lub jq: docker inspect web1 | jq '.[].NetworkSettings.IPAddress'. Zawiera informacje o adresie IP, sieciach, portach, dNS, gateway, mostku i regułach iptables.

49/50
Podsumowanie - najważniejsze koncepcje

Kluczowe koncepcje wirtualizacji sieci

  • Linux Bridge: Wirtualny przełącznik L2 w jądrze Linux, obsługuje MAC learning, forwarding, STP, VLAN filtering. Budowany przez ip link add type bridge. Stanowi podstawę izolacji sieciowej w KVM i Dockerze
  • VETH Pair: Wirtualny kabel Ethernet łączący dwa interfejsy. Umożliwia komunikację pomiędzy przestrzeniami nazw. Tworzony przez ip link add type veth. Używany przez Docker do łączenia kontenerów z mostkiem
  • Network Namespace: Przestrzeń nazw izolująca stos sieciowy. Każda przestrzeń ma własne interfejsy, routing, iptables. Tworzona przez ip netns add. Podstawa izolacji kontenerów
  • Docker Bridge: Mostek docker0, domyślna sieć dla kontenerów. Adresacja 172.17.0.0/16. Automatyczne zarządzanie iptables (NAT, forward)
  • Port Publishing (-p): DNAT w iptables przekierowujący port hosta na port kontenera. Mechanizm dockera-proxy lub reguły iptables DOCKER
Podsumowanie koncepcji

Wirtualizacja sieci w Linux opiera się na trzech filarach: bridge (przełącznik L2), VETH (kabel wirtualny) i network namespaces (izolacja). Zrozumienie tych trzech mechanizmów pozwala na wiadome zarządzanie siecią kontenerową bez traktowania Dockera jako czarnej skrzynki.

Mostek Linux jest implementacją przełącznika L2 w jądrze, która potrafi uczyć się adresów MAC i przekazywać ramki na odpowiednie porty. Może obsługiwać STP do zapobiegania pętlom i filtrowanie VLAN do izolacji ruchu. Jest używany zarówno przez KVM/QEMU, jak i Docker.

Pary VETH są wirtualnymi kablami, które łączą dwie przestrzenie nazw. Jeden koniec pary jest w kontenerze (eth0), drugi w mostku hosta (vethXXXXX). Ramka wysłana na jednym koncu pojawia się na drugim, co umożliwia komunikację między kontenerem a hostem.

Network namespaces zapewniają izolację stosu sieciowego. Każdy kontener Docker działa we własnej przestrzeni nazw, co oznacza, że ma własne interfejsy, tablicę routingu, reguły iptables i gniazda. Dwa kontenery nie mogą komunikować się bezpośrednio, chyba że zostaną połączone przez mostek lub sieć overlay.

Docker automatyzuje tworzenie tych mechanizmów: demon Dockera tworzy mostek docker0, pary VETH dla każdego kontenera, przestrzenie nazw sieciowe, oraz zarządza regułami iptables dla NAT i publikowania portów. Zrozumienie tego, co dzieje się pod maską, pozwala na efektywne debugowanie i projektowanie architektury kontenerowej.

50/50
Cheat Sheet - wszystkie najważniejsze polecenia

Podręczna ściąga poleceń

## --- MOSTEK (BRIDGE) ---
ip link add name br0 type bridge                 # Tworzenie mostka
ip link set br0 up                              # Podniesienie mostka
ip link set eth0 master br0                     # Dodanie interfejsu do mostka
ip link set eth0 nomaster                       # Usuniecie interfejsu z mostka
bridge fdb show br br0                          # Tablica FDB
bridge link show                                # Porty mostka

## --- VETH ---
ip link add v0 type veth peer name v1           # Para VETH
ip link set v1 netns ns1                       # Przeniesienie do netns

## --- NETWORK NAMESPACE ---
ip netns add ns1                                # Tworzenie netns
ip netns exec ns1 ip addr add 10.0.0.1/24 dev v0 # Adresacja w netns
ip netns exec ns1 ip link set v0 up             # Podniesienie interfejsu
ip netns del ns1                                # Usuniecie netns

## --- DOCKER ---
docker run -p 8080:80 nginx                     # Publikowanie portu
docker network create mynet                      # Własna sieć
docker network connect mynet kontener            # Podłączenie do sieci
docker inspect kontener | grep IPAddress         # Adres IP

## --- DIAGNOSTYKA ---
iptables -t nat -L DOCKER -n --line-numbers      # Reguły NAT Dockera
tcpdump -i br0 -n icmp                          # Monitoring mostka
nsenter -t PID -n ip addr show                   # Wejcie w netns procesu
Cheat Sheet

Powyższa ściąga zawiera wszystkie najważniejsze polecenia omówione podczas prezentacji. Mostek (bridge): ip link add type bridge do tworzenia, ip link set master do dodawania interfejsów, bridge fdb show do podglądu tablicy FDB. Pamiętaj, że mostek wymaga adresu IP tylko na interfejsie br0, a interfejsy członkowskie nie mają adresów L3.

VETH: para tworzona przez ip link add type veth peer. Jeden koniec można przenieść do przestrzeni nazw (ip link set netns), drugi podłączyć do mostka (ip link set master). VETH działa jak wirtualny kabel - dane wysłane na jednym koncu pojawiają się na drugim. Bez podniesienia interfejsu (ip link set up) nie przetwarza pakietów.

Network namespace: tworzone przez ip netns add, usuwane przez ip netns del. Polecenia w przestrzeni nazw wykonujemy przez ip netns exec. Każda przestrzeń ma własne interfejsy, routing, iptables. Interfejs lo jest domyślnie DOWN - trzeba go podnieść. Po usunięciu netns wszystkie interfejsy wirtualne w niej są usuwane.

Docker: najczęściej używane polecenia to docker run z -p lub --network, docker network create/ls/inspect. Pamiętaj, że domyślny mostek docker0 ma ograniczenia (brak DNS, słaba izolacja) - w produkcji używaj user-defined bridge. Reguły iptables Dockera są w łańcuchach DOCKER (nat i filter) oraz DOCKER-USER dla własnych reguł.

Diagnostyka: iptables -t nat -L DOCKER do podglądu DNAT, tcpdump -i br0 do monitorowania ruchu na moście, nsenter -t PID -n do wejścia w przestrzeń nazw procesu (przydatne, gdy docker exec nie działa). Pamiętaj o sudo dla wszystkich poleceń wymagających uprawnień root.