Dnes se budu věnovat síťovým jmenným prostorům (network namespaces). Ty umožní vytvořit na jednom počítači několik v podstatě nezávislých síťových konfigurací. Chování je obdobné, jako by se jednalo o virtuální stroje, ale odpadá režie s násobně spuštěným systémovým jádrem a emulací hardware. K napsání článku mě inspiroval požadavek na spuštění několika kopií prohlížeče, které při testování webové prezentace budou používat různé nameservery, potažmo vlastní kopie /etc/hosts
.
Využití je zjevné, téměř každý vývojář někdy potřebuje ladit aplikaci na testovací instanci, ale s použitím skutečné webové adresy aplikace. Běžné řešení je přepisovat dokola záznamy v /etc/hosts
, ale to je jednak pracné a jednak může být současně aktivní jen jedna konfigurace. Ukážeme si, jak mít vedle sebe dva prohlížeče, kde každý uvidí pod stejným doménovým jménem jinou IP adresu.
Příprava konfigurací
Prvním krokem je příprava konfigurací, které budeme chtít mít v novém jmenném prostoru odlišné. Zvolíme si jeho název, pro potřeby tohoto článku to bude devel
, a připravíme si v /etc
potřebné soubory. Velkou část následujících příkazů je potřeba zadávat pod uživatelem root, tedy s pomocí sudo
a nebo z shellu roota. Pro přehlednost nebudu každé volání demonstrovat s použitím sudo
, pokud bude tuto konfiguraci používat neprivilegovaný uživatel, předpokládám, že administrátor ze všech příkazů sestaví skript a ten potom bude přes sudo
spuštěn.
V našem případě budeme přepisovat soubor /etc/hosts
, postup je následující:
# mkdir -p /etc/netns/devel/
# cp /etc/hosts /etc/netns/devel/
V kopii souboru /etc/netns/devel/hosts
si připravíme potřebné změny.
Nyní tedy máme původní /etc/hosts
, který stále používají všechny aplikace, a potom jeho upravenou kopii v /etc/netns/devel/hosts
. Vtip jmenného prostoru je, že prvně hledá používané konfigurační soubory v adresáři /etc/netns/své_jméno
a teprve když je tam nenajde, použije ty výchozí.
Technické okénko: to „hledání“ je reálně zajištěno tím, že dojde k bind mountu přemapovávaných souborů (případně adresářů) přes jejich originální/systémové verze. Ale to nemusíte nutně vědět.
Vytvoření jmenného prostoru
Dalším krokem je vytvoření nového jmenného prostoru. Použijeme samozřejmě příkaz ip, protože ifconfig
je v Linuxu dávno zastaralý a takto pokročilé věci už vůbec nevidí. Název jmenného prostoru bude devel
a syntaxe příkazu je
# ip netns add devel
Pokud vše proběhlo správně, příkaz nevypisuje žádný výstup, takže si ověříme, že jmenný prostor existuje:
# ip netns list
Tohle nám zobrazí seznam existujících jmenných prostorů. Aplikaci v našem novém jmenném prostoru spustíme prefixováním příkazu, který chceme zadat, příkazem:
# ip netns exec devel
Pro první pohled „dovnitř“ nového jmenného prostoru to tedy bude příkaz:
# ip netns exec devel ip addr sh
Ten nám zobrazí:
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
Vypadá to trochu zacykleně, ale v principu jsem jen ve jmenném prostoru devel
spustil příkaz:
# ip addr sh
Hned vidíme, že máme jen jedno rozhraní loopback
, které dokonce ani není aktivní. To je správně, nový jmenný prostor totiž je úplně čistý, jako jádro po bootu.
Loopback je celkem potřebná věc, proto si ho nakonfigurujeme i uvnitř jmenného prostoru devel
:
# ip netns exec devel ip link set lo up
Pokud se na rozhraní lo
automaticky nepřidá ip adresa, musíme ji přidat sami:
# ip netns exec devel ip addr add 127.0.0.1/8 dev lo
Většina distribucí ji ale přidá i bez tohoto příkazu chvíli poté, co se rozhraní lo
aktivuje předchozím příkazem.
Konektivita
Dále potřebujeme do nového jmenného prostoru dopravit konektivitu. To je úplně stejné, jako když máme virtualizovaný stroj: můžeme si udělat pomocnou síť, kterou zanatujeme za IP adresu svého počítače, můžeme si vytvořit virtuální přepínač (switch) a daný virtuální prostor bude na síti vidět jako samostatný počítač, nebo dokonce můžeme do jmenného prostoru přiřadit celou síťovou kartu. Pokud jich samozřejmě máme víc.
Já předvedu prostřední variantu, protože potřebuji IPv6 a natovat IPv6 přeci zbytečně nechceme.
Propoje mezi jmennými prostory se vytvářejí přes virtuální ethernetová rozhraní typu veth
. Tato rozhraní musejí vždycky existovat v páru a každá strana může být součástí jednoho jmenného prostoru. Není to tak, že by všechny musely končit na hlavním „počítači“, mohu si propojovat libovolné jmenné prostory. Dá se takto vytvořit pěkný packet storm, když se to propojí nešťastným způsobem.
Narazíte i na návody, kdy se pro virtuální rozhraní použije místo veth
typ macvlan
. Princip je podobný, ale macvlan
nám naklonuje existující fyzickou kartu. Já používám veth
, protože když na počítači již běží několik virtuálních strojů, tak se používá bridge nebo virtuální přepínač a přidat do něj další rozhraní mi přijde přehlednější, než kombinovat macvlan
a veth
.
Vytvoříme si tedy dvojici rozhraní:
# ip link add veth0 type veth peer name veth0devel
a vzdálenější konec veth0devel
si přestěhuji do svého jmenného prostoru:
# ip link set veth0devel netns devel
Opět zkontroluji výsledek předchozí operace:
# ip netns exec devel ip addr sh
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
26: veth0devel@if27: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 1e:92:74:bd:18:4a brd ff:ff:ff:ff:ff:ff link-netnsid 0
Poznámka: Kolega při oponentuře článku namítá, že pro čtenáře bude matoucí, že rozhraní je vypsáno jako veth0devel@if27
, tak se dohodněme, že se nenecháte zmást a části za @
si nebudete všímat, je to jen vazba sloužící k identifikaci rodičovského rozhraní a v dalších příkazech ji nebudeme zadávat.
Bližší konec ( veth0
) si dám do bridge, který mám již připravený:
# ip link set veth0 master br0 # ip link set veth0 up
Pokud bridge nemáte, vytvoříte jej obecně příkazem:
# ip link add name br0 type bridge # ip link set dev br0 up
Pak do něj přidáte svou ethernetovou kartu. Podstatné je, že před přidáním karty do bridge musíte přestěhovat IP adresu z ethernetové karty na ten bridge a až následně kartu do bridge dát. Postup tedy může vypadat přibližně takto, předpokládaje, že máte IP adresu 10.10.10.2/24 a rozhraní se jmenuje eth0:
# ip addr del 10.10.10.2/24 dev eth0 # ip addr add 10.10.10.2/24 dev br0 # ip ro add default via 10.10.10.1 # ip link set eth0 master br0
Přidání výchozí routy je nutné, protože smazáním konfigurace rozhraní, přes které byla dostupná, zanikla i ta routa.
Předchozí postup funguje, ale protože se pohybujeme na pracovní stanici, je téměř jisté, že ho bude potřeba obměnit, protože na stanici poběží správce síťových připojení, jako např. Network Manager.
Síť uvnitř
Posuneme se k síti uvnitř nově vytvořeného jmenného prostoru. Zde můžeme síť nakonfigurovat s pomocí DHCP klienta pro IPv4, IPv6 se nastaví přes SLAAC. Někdy DHCP klient spustit nejde. Například když je na počítači aktivní wicked, je spuštění další instance v jiném jmenném prostoru celkem složité (podpora pro jmenné prostory je ve vývoji) a potom nezbude, než nastavit IP adresu staticky, zcela klasickými příkazy, jen opět prefixovanými ip netns exec
:
# ip netns exec devel ip link set dev veth0devel up # ip netns exec devel ip addr add 10.10.10.10/24 dev veth0devel # ip netns exec devel ip route add default via 10.10.10.1
Aby to celé fungovalo, budeme muset v jádře povolit forwarding paketů:
# sysctl -w net.ipv4.ip_forward=1 # sysctl -w net.ipv6.conf.all.forwarding=1
Případně musíme také povolit FORWARD v IPtables. V tuto chvíli nám v novém jmenném prostoru funguje síť. Mimochodem, všimněte si, že jmenný prostor má vlastní IPtables/Nftables:
# ip netns exec devel iptables -L -v -n
Dozvíme se, že jsou úplně prázdné, takže pokud potřebujeme chránit i daný jmenný prostor, musíme je naplnit.
Spouštíme proces
Pomalu se dostáváme na konec. Je celkem pravděpodobné, že následující kroky nebudou někomu fungovat, systém se bude chovat jako by viděl původní obsah /etc/hosts
. Důvodem je nscd
, program zrychlující DNS odpovědi s použitím cache, který zůstal běžet v základním jmenném prostoru. Je možné spustit jeho druhou kopii, ale nejrychlejší je ho prostě vypnout:
# systemctl stop nscd
Posledním krokem bude spuštění prohlížeče. To není tak jednoduché, jak to na první pohled vypadá. Většina dnešních prohlížečů totiž zjistí, že již běží a místo spuštění dalšího procesu jen přidá další panel ke stávajícímu oknu. Druhý problém je, že spustit proces v našem novém jmenném prostoru může jen root nebo uživatel obdařený schopností (capatibility) CAP_NET_ADMIN
, kterou běžně žádný uživatel nemá. Budeme tedy muset spuštění programu podepřít příkazem su
, případně sudo
, aby celou sekvenci mohl spustit neprivilegovaný uživatel.
Programátor s uživatelským jménem guru
tedy bude muset zadat:
# sudo ip netns exec devel su guru -c "firefox -P --no-remote"
Parametr -P
nám umožní vybrat jiný profil prohlížeče a --no-remote
zabrání Firefoxu, aby přidal další panel již existujícímu oknu.
Pro konkurenční Chrome je syntaxe lehce odlišná. Předpokládáme, že uživatel má v domovském adresáři připravený adresář .local/chrome-profiles
pro profily prohlížeče:
# sudo ip netns exec devel su guru -c "/opt/google/chrome/google-chrome --user-data-dir=/home/guru/.local/chrome-profiles/devel"
Výsledek je ale totožný: máme nové okno prohlížeče, které má vlastní /etc/hosts
a tak dokáže pro vybrané doménové jméno zobrazovat obsah různých instancí aplikace.
Pokud nevadí, že mezi prohlížečem a aplikací je proxy, je možné v separátním jmenném prostoru spustit např. Squid a pak problémy s použitím sudo
odpadají a postačí nám více profilů prohlížeče s různě nastavenou proxy. Proxy může být potom společná pro více vývojářů.
Samozřejmě možností, jak to využít, je mnohem více. Tento článek je jen malou ukázkou možností současného linuxového jádra.