Wstęp od tłumacza: W dobie masowego wprowadzenia różnego rodzaju kontenerów do naszego życia, warto poznać technologie, od których to wszystko się zaczynało. Niektóre z nich można wciąż z powodzeniem stosować, ale nie wszyscy pamiętają o tych metodach (lub nie wiedzą, jeśli nie byli świadkami ich dynamicznego rozwoju). Jedną z takich technologii jest User Mode Linux. Autorka oryginału dokładnie zbadała, co z dawnych rozwiązań wciąż działa, a co już nie, i przygotowała coś w rodzaju szczegółowej instrukcji, jak samodzielnie uruchomić domowy UML w 2019 roku. Tak, zaprosiliśmy do Habr autorkę pierwotnego posta. , więc jeśli macie pytania — pytajcie po angielsku w komentarzach.

User Mode w Linuxie to w zasadzie port jądra Linux na samego siebie. Tryb ten pozwala na uruchomienie pełnoprawnego jądra Linux jako procesu użytkownika i zazwyczaj jest stosowany przez deweloperów do testowania sterowników. Ale ten tryb przydaje się także jako narzędzie ogólnej izolacji, której zasady są podobne do działania maszyn wirtualnych. Zapewnia on większą izolację niż Docker, ale mniejszą niż pełnoprawna maszyna wirtualna, taka jak KVM czy Virtual Box.
Ogólnie rzecz biorąc, User Mode może wydawać się dziwnym i skomplikowanym narzędziem, ale ma swoje obszary zastosowań. To pełnoprawne jądro Linux działające jako użytkownik bez uprawnień. Ta cecha pozwala na uruchamianie potencjalnie niebezpiecznego kodu bez zagrożeń dla maszyny hosta. A ponieważ jest to pełnoprawne jądro, jego procesy są izolowane od maszyny hosta, co oznacza, że procesy działające wewnątrz User Mode nie będą widoczne dla hosta. To nie jest podobne do znanego kontenera Docker, w przypadku którego maszyna hosta zawsze widzi procesy wewnątrz repozytorium. Spójrz na ten fragment pstree z jednego z moich serwerów:
containerd─┬─containerd-shim─┬─tini─┬─dnsd───19*[{dnsd}]
│ │ └─s6-svscan───s6-supervise
│ └─10*[{containerd-shim}]
├─containerd-shim─┬─tini─┬─aerial───21*[{aerial}]
│ │ └─s6-svscan───s6-supervise
│ └─10*[{containerd-shim}]
├─containerd-shim─┬─tini─┬─s6-svscan───s6-supervise
│ │ └─surl
│ └─9*[{containerd-shim}]
├─containerd-shim─┬─tini─┬─h───13*[{h}]
│ │ └─s6-svscan───s6-supervise
│ └─10*[{containerd-shim}]
├─containerd-shim─┬─goproxy───14*[{goproxy}]
│ └─9*[{containerd-shim}]
└─32*[{containerd}]Porównaj to z pstree jądra Linux w trybie użytkownika:
linux─┬─5*[linux]
└─slirpPracując z kontenerami Docker, mogę zobaczyć z hosta nazwy procesów uruchomionych w systemie gościa. To jest niemożliwe w trybie użytkownika Linux. Co to oznacza? Oznacza to, że narzędzia do monitorowania, działające przez podsystem audytu Linux (Linux’s auditing subsystem) nie widzą procesów uruchamianych w systemie gościa. Ale w pewnych sytuacjach ta cecha może stać się mieczem obosiecznym.
Właściwie cały poniższy post to zestaw badań i prób osiągnięcia zamierzonego rezultatu. Do tego musiałem korzystać z różnych starych narzędzi, czytać źródła jądra, intensywnie debugować kod, który był napisany w czasach, kiedy jeszcze chodziłem do podstawówki, a także grzebać w kompilacjach Heroku za pomocą specjalnego pliku binarnego w poszukiwaniu potrzebnych mi narzędzi. Cała ta praca doprowadziła do tego, że ludzie w moim IRC zaczęli nazywać mnie czarodziejką (magic). Mam nadzieję, że ten post będzie dla kogoś solidną dokumentacją, aby powtórzyć to samo, ale już z nowszymi jądrami i wersjami OS.
Konfiguracja
Ustawienie trybu użytkownika Linux przebiega w kilku etapach:
- instalacja zależności na hoście;
- pobranie jądra Linux;
- konfiguracja kompilacji jądra;
- kompilacja jądra;
- instalacja pliku binarnego;
- konfiguracja systemu plików gościa;
- dobór parametrów uruchomienia jądra;
- konfiguracja sieci gościa;
- uruchomienie gościnnego jądra.
Zakładam, że jeśli zdecydujecie się na samodzielne przeprowadzenie tego wszystkiego, najprawdopodobniej będziecie działać w systemie podobnym do Ubuntu lub Debian. Próbowałam zrealizować wszystko, co opisano powyżej, w mojej ulubionej dystrybucji – Alpine, ale nic z tego nie wyszło, najwidoczniej z powodu tego, że jądro Linux ma ścisłą więź z glibc-izmami dla sterowników w trybie użytkownika. Planuję zgłosić to do upstreamu po tym, jak ostatecznie rozwiążę problem.
Instalacja zależności na hoście
Ubuntu wymaga przynajmniej następujących pakietów do kompilacji jądra Linux (zakładając czystą instalację):
- 'build-essential'
- 'flex'
- 'bison'
- 'xz-utils'
- 'wget'
- 'ca-certificates'
- 'bc'
- 'linux-headers'
Możesz je zainstalować za pomocą następującej komendy (z uprawnieniami roota lub używając sudo):
apt-get -y install build-essential flex bison xz-utils wget ca-certificates bc
linux-headers-$(uname -r) Zauważ, że uruchomienie programu konfiguracji menu dla jądra Linux wymaga zainstalowania libncurses-dev. Proszę upewnij się, że jest on zainstalowany za pomocą następującej komendy (z uprawnieniami roota lub używając sudo):
apt-get -y install libncurses-devPobieranie jądra
Określ lokalizację do pobrania i późniejszej kompilacji jądra. Ta operacja będzie wymagać około 1,3 GB miejsca na dysku twardym, więc upewnij się, że je masz.
Następnie przejdź do i uzyskaj URL do pobrania najnowszej stabilnej wersji jądra. W momencie pisania tego posta jest to:
Pobierz ten plik za pomocą 'wget':
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.1.16.tar.xz I rozpakuj go za pomocą 'tar':
tar xJf linux-5.1.16.tar.xzTeraz wchodzimy do katalogu utworzonego podczas rozpakowywania tarballa:
cd linux-5.1.16Konfiguracja kompilacji jądra
System kompilacji jądra to zestaw z z wieloma narzędziami i skryptami użytkownika do automatyzacji procesu. Na początek otwórz interaktywny program konfiguracji:
make ARCH=um menuconfig Częściowo przeprowadzi kompilację i wyświetli okno dialogowe. Gdy na dole okna pojawi się ‘[Select]‘, będziesz mógł zająć się konfiguracją przy użyciu klawiszy Spacja lub Enter. Nawigacja po oknie, jak zwykle, za pomocą strzałek na klawiaturze „w górę” i „w dół”, a wybieranie elementów – „w lewo” lub „w prawo”.
Wskaźnik „->” oznacza, że znajdujesz się w podmenu, do którego wejście odbywa się klawiszem Enter. Wyjście z niego, oczywiście, poprzez ‘[Exit]‘.
Włącz następujące opcje w ‘[Select]‘ i upewnij się, że obok nich znajduje się symbol ‘[*]’:
Opcje specyficzne dla UML:
- System plików hosta
Wsparcie dla sieci (włącz to, aby pokazać podmenu):
- Opcje sieciowe:
- Sieć TCP/IP
Urządzenia sieciowe UML:
- Wirtualne urządzenie sieciowe
- Transport SLiRP Wszystko, z tego okna możesz wyjść, wybierając kolejno „[Exit]“. Tylko upewnij się, że na końcu zaproponuje zapisanie konfiguracji i wybierzesz „[Tak]‘.
Zalecam, abyś pobawił się opcjami kompilacji jądra po przeczytaniu tego wpisu. Dzięki tym eksperymentom wiele zyskasz w kwestii zrozumienia mechanizmów jądra niskiego poziomu oraz wpływu różnych flag na jego kompilację.
Kompilacja jądra
Jądro Linux to duży program zajmujący się wieloma rzeczami. Nawet przy tak minimalnej konfiguracji na starym sprzęcie jego kompilacja może zająć dużo czasu. Dlatego skompiluj jądro za pomocą następującego polecenia:
make ARCH=um -j$(nproc) Dlaczego? To polecenie powie naszemu kompilatorowi, aby użył wszystkich dostępnych rdzeni i wątków procesora podczas kompilacji. Polecenie $(nproc) na końcu kompilacji podstawia wynik polecenia nproc, które jest częścią coreutils w standardowej kompilacji Ubuntu.
Po pewnym czasie nasze jądro zostanie skompilowane do pliku wykonywalnego ./linux.
Instalacja binarki
Ponieważ User Mode w Linuxie tworzy zwykłą binarkę, możesz ją zainstalować jak każdą inną aplikację. Oto jak to zrobiłem:
mkdir -p ~/bin
cp linux ~/bin/linux Warto również upewnić się, że ~/bin znajduje się w twoim $PATH:
export PATH=$PATH:$HOME/binKonfiguracja systemu plików gościa
Utwórz katalog dla systemu plików gościa:
mkdir -p $HOME/prefix/uml-demo
cd $HOME/prefix
Odwiedź alpinelinux.org i w znajdź aktualny link do pobrania MINI ROOT FILESYSTEM. W momencie pisania tej publikacji było to:
http://dl-cdn.alpinelinux.org/alpine/v3.10/releases/x86_64/alpine-minirootfs-3.10.0-x86_64.tar.gzPobierz ten tarball, korzystając z wget:
wget -O alpine-rootfs.tgz http://dl-cdn.alpinelinux.org/alpine/v3.10/releases/x86_64/alpine-minirootfs-3.10.0-x86_64.tar.gzTeraz wejdź do katalogu systemu plików gościa i rozpakuj archiwum:
cd uml-demo
tar xf ../alpine-rootfs.tgzOpisane działania stworzą mały szablon systemu plików. Z powodu specyfiki działania systemu instalacja pakietów przez menedżera apk Alpine będzie niezwykle trudna. Jednak ten system plików wystarczy, aby ocenić ogólną ideę.
Będziemy potrzebować również narzędzia do ograniczenia zużycia pamięci naszego jądra gościa.
wget -O tini https://github.com/krallin/tini/releases/download/v0.18.0/tini-static
chmod +x tiniTworzenie wiersza poleceń jądra
W jądrze Linux, jak w większości innych programów, znajdują się argumenty linii poleceń, z którymi można się zapoznać, podając klucz --help.
Sam —help
linux --help
User Mode Linux v5.1.16
dostępny pod http://user-mode-linux.sourceforge.net/
--showconfig
Wyświetla plik konfiguracyjny, z którego ten binarny UML został wygenerowany.
iomem=,
Konfiguruje jako region pamięci IO o nazwie .
mem=
Kontroluje, ile "fizycznej" pamięci jądro przydziela
dla systemu. Rozmiar określa się jako liczba, za którą
następuje jedno z 'k', 'K', 'm', 'M', co ma oczywiste znaczenie.
To nie jest związane z ilością pamięci w hoście. Może
być większa, a nadmiar, jeśli kiedykolwiek zostanie użyty, po prostu zostanie wymieniony.
Przykład: mem=64M
--help
Wyświetla tę wiadomość.
debug
ta flaga nie jest potrzebna do uruchomienia gdb na UML w trybie skas
root=
Jest to właściwie używane przez ogólne jądro dokładnie w ten sam
sposób, jak w każdym innym jądrze. Jeśli skonfigurujesz liczne urządzenia blokowe
i chcesz uruchomić z czegoś innego niż ubd0, musiałbyś użyć czegoś takiego jak:
root=/dev/ubd5
--version
Wyświetla numer wersji jądra.
umid=
Używane do przypisania unikalnej tożsamości tej maszyny UML i
jest używane do nazywania pliku pid i gniazda konsoli zarządzającej.
con[0-9]*=
Podłącz konsolę lub linię szeregową do kanału hosta. Zobacz
http://user-mode-linux.sourceforge.net/old/input.html po pełen
opis tego przełącznika.
eth[0-9]+=,
Konfiguracja urządzenia sieciowego.
aio=2.4
Używane do wymuszenia, aby UML używał 2.4 stylu AIO, nawet gdy 2.6 AIO jest
dostępne. 2.4 AIO to jeden wątek, który obsługuje jedno żądanie na raz,
synchronicznie. 2.6 AIO to wątek, który używa interfejsu 2.6 AIO
do obsługi dowolnej liczby oczekujących żądań. 2.6 AIO
nie jest dostępne w trybie tt, na hostach 2.4, ani gdy UML jest zbudowane z
/usr/include/linux/aio_abi.h nie jest dostępne. Wiele dystrybucji nie zawiera
aio_abi.h, więc musisz skopiować go z drzewa jądra do
swojego /usr/include/linux, aby zbudować UML zdolny do AIO
nosysemu
Wyłącza emulację wywołań systemowych dla ptrace (SYSEMU).
SYSEMU to poprawka wydajności wprowadzona przez Laurent Vivier. Zmienia
zachowanie ptrace() i pomaga zmniejszyć tempo zmian kontekstu hosta.
Aby to działało, potrzebujesz poprawki jądra dla swojego hosta również.
Zobacz http://perso.wanadoo.fr/laurent.vivier/UML/ po dalsze
informacje.
uml_dir=
Lokalizacja do umieszczania plików pid i umid.
quiet
Wyłącza komunikaty informacyjne podczas uruchamiania.
hostfs=,,...
Używane do ustawiania parametrów hostfs. Argument katalogu root
jest używany do ograniczenia wszystkich montażów hostfs w określonym drzewie katalogów
na hoście. Jeśli nie jest to określone, to użytkownik wewnątrz UML może
zamontować cokolwiek na hoście, co jest dostępne dla użytkownika, który go uruchamia.
Obecnie jedyną obsługiwaną flagą jest 'append', która określa, że wszystkie
pliki otwierane przez hostfs będą otwierane w trybie dołączania.Ten banner przedstawia podstawowe parametry uruchamiania. Uruchommy jądro z minimalnym zestawem opcji:
linux
root=/dev/root
rootfstype=hostfs
rootflags=$HOME/prefix/uml-demo
rw
mem=64M
init=/bin/shPowyższe wiersze mówią naszemu jądru, co ma zrobić:
- Załóżmy, że system plików root jest pseudo-urządzeniem
/dev/root. - Wybierz jako sterownik głównego systemu plików.
- Zamontuj gościnny system plików, który stworzyliśmy w urządzeniu root.
- Tak, w trybie odczytu-zapisu.
- Użyj tylko 64 megabajtów pamięci RAM (możesz użyć znacznie mniej, w zależności od tego, co zamierzasz robić, ale 64 MB wydają się optymalną wielkością).
- Jądro uruchamia automatycznie
/bin/shjakinit-proces.
Uruchom tę komendę, a powinieneś otrzymać coś podobnego do poniższego:
Jeszcze jeden arkusz
Limity zrzutu pamięci:
miękki - 0
twardy - BRAK
Sprawdzanie, czy ptrace może zmieniać numery wywołań systemowych...OK
Sprawdzanie poprawki emulacji wywołań systemowych dla ptrace...OK
Sprawdzanie zaawansowanej poprawki emulacji wywołań systemowych dla ptrace...OK
Sprawdzanie zmiennych środowiskowych dla katalogu tymczasowego...nie znaleziono
Sprawdzanie, czy /dev/shm jest na tmpfs...OK
Sprawdzanie PROT_EXEC mmap w /dev/shm...OK
Dodawanie 32137216 bajtów do pamięci fizycznej, aby uwzględnić lukę w exec-shield
Wersja jądra Linux 5.1.16 (cadey@kahless) (gcc wersja 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)) #30 Ndz lip 7 18:57:19 UTC 2019
Zbudowano 1 listy stref, grupowanie mobilności włączone. Całkowita liczba stron: 23898
Linia poleceń jądra: root=/dev/root rootflags=/home/cadey/dl/uml/alpine rootfstype=hostfs rw mem=64M init=/bin/sh
Wpisy w tabeli pamięci podręcznej dentry: 16384 (zamówienie: 5, 131072 bajtów)
Wpisy w tabeli pamięci podręcznej inode: 8192 (zamówienie: 4, 65536 bajtów)
Pamięć: 59584K/96920K dostępnych (2692K kodu jądra, 708K rwdata, 588K rodata, 104K init, 244K bss, 37336K zarezerwowane, 0K zarezerwowane cma)
SLUB: HWalign=64, Zamówienie=0-3, MinObjects=0, CPU=1, Węzły=1
NR_IRQS: 15
źródło zegara: timer: mask: 0xffffffffffffffff max_cycles: 0x1cd42e205, max_idle_ns: 881590404426 ns
Kalibrowanie pętli opóźnienia... 7479.29 BogoMIPS (lpj=37396480)
pid_max: domyślne: 32768 minimum: 301
Wpisy w tabeli pamięci podręcznej zamontowanych katalogów: 512 (zamówienie: 0, 4096 bajtów)
Wpisy w tabeli pamięci podręcznej punktów montażu: 512 (zamówienie: 0, 4096 bajtów)
Sprawdzanie, czy ptys w hoście obsługują sygnał SIGIO...Tak
Sprawdzanie, czy ptys w hoście obsługują SIGIO przy zamknięciu...Nie, włączam obejście
devtmpfs: zainicjowany
random: get_random_bytes wywołane z setup_net+0x48/0x1e0 z crng_init=0
Używanie 2.6 host AIO
źródło zegara: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
Wpisy w tabeli hash dla futexów: 256 (zamówienie: 0, 6144 bajtów)
NET: Zarejestrowano rodzinę protokołów 16
źródło zegara: Przełączono na źródło zegara timer
NET: Zarejestrowano rodzinę protokołów 2
tcp_listen_portaddr_hash wpisy w tabeli hash: 256 (zamówienie: 0, 4096 bajtów)
TCP zarejestrowane wpisy w tabeli hash: 1024 (zamówienie: 1, 8192 bajtów)
TCP bind wpisy w tabeli hash: 1024 (zamówienie: 1, 8192 bajtów)
TCP: Tabele hash skonfigurowane (ustalony 1024 bind 1024)
UDP wpisy w tabeli hash: 256 (zamówienie: 1, 8192 bajtów)
UDP-Lite wpisy w tabeli hash: 256 (zamówienie: 1, 8192 bajtów)
NET: Zarejestrowano rodzinę protokołów 1
konsola [stderr0] wyłączona
mconsole (wersja 2) zainicjowana w /home/cadey/.uml/tEwIjm/mconsole
Sprawdzanie, czy host MADV_REMOVE obsługuje...OK
workingset: timestamp_bits=62 max_order=14 bucket_order=0
Warstwa blokowa SCSI ogólnego zastosowania (bsg) sterownik wersja 0.4 załadowany (major 254)
scheduler wejścia-wyjścia noop zarejestrowany (domyślny)
scheduler wejścia-wyjścia bfq zarejestrowany
loop: moduł załadowany
NET: Zarejestrowana rodzina protokołów 17
Zainicjowano sterownik konsoli stdio
Używanie typu kanału, który jest skonfigurowany poza UML
setup_one_line nie powiódł się dla urządzenia 1: Konfiguracja nie powiodła się
Używanie typu kanału, który jest skonfigurowany poza UML
setup_one_line nie powiódł się dla urządzenia 2: Konfiguracja nie powiodła się
Używanie typu kanału, który jest skonfigurowany poza UML
setup_one_line nie powiódł się dla urządzenia 3: Konfiguracja nie powiodła się
Używanie typu kanału, który jest skonfigurowany poza UML
setup_one_line nie powiódł się dla urządzenia 4: Konfiguracja nie powiodła się
Używanie typu kanału, który jest skonfigurowany poza UML
setup_one_line nie powiódł się dla urządzenia 5: Konfiguracja nie powiodła się
Używanie typu kanału, który jest skonfigurowany poza UML
setup_one_line nie powiódł się dla urządzenia 6: Konfiguracja nie powiodła się
Używanie typu kanału, który jest skonfigurowany poza UML
setup_one_line nie powiódł się dla urządzenia 7: Konfiguracja nie powiodła się
Używanie typu kanału, który jest skonfigurowany poza UML
setup_one_line nie powiódł się dla urządzenia 8: Konfiguracja nie powiodła się
Używanie typu kanału, który jest skonfigurowany poza UML
setup_one_line nie powiódł się dla urządzenia 9: Konfiguracja nie powiodła się
Używanie typu kanału, który jest skonfigurowany poza UML
setup_one_line nie powiódł się dla urządzenia 10: Konfiguracja nie powiodła się
Używanie typu kanału, który jest skonfigurowany poza UML
setup_one_line nie powiódł się dla urządzenia 11: Konfiguracja nie powiodła się
Używanie typu kanału, który jest skonfigurowany poza UML
setup_one_line nie powiódł się dla urządzenia 12: Konfiguracja nie powiodła się
Używanie typu kanału, który jest skonfigurowany poza UML
setup_one_line nie powiódł się dla urządzenia 13: Konfiguracja nie powiodła się
Używanie typu kanału, który jest skonfigurowany poza UML
setup_one_line nie powiódł się dla urządzenia 14: Konfiguracja nie powiodła się
Używanie typu kanału, który jest skonfigurowany poza UML
setup_one_line nie powiódł się dla urządzenia 15: Konfiguracja nie powiodła się
Konsola zainicjowana w /dev/tty0
konsola [tty0] włączona
konsola [mc-1] włączona
Nie udało się zainicjować urządzenia ubd 0: Nie można określić rozmiaru pliku urządzenia
VFS: Zamontowano root (system plików hostfs) na urządzeniu 0:11.
devtmpfs: zamontowane
Ta architektura nie ma ochrony pamięci jądra.
Uruchom /bin/sh jako proces init
/bin/sh: nie można uzyskać dostępu do tty; kontrola zadania wyłączona
random: szybkie inicjowanie zakończone
/ # Powyższe działania przyniosą nam minimalny system gościnny, bez takich rzeczy jak /proc czy przypisany hostname. Na przykład, spróbuj wykonać następujące polecenia:
- uname -av
- cat /proc/self/pid
- hostname
Aby wyjść z systemu gościnnego, wpisz exit lub naciśnij control-d. To zakończy powłokę z następującym kernel panic:
/ # exit
Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000000
fish: “./linux root=/dev/root rootflag…” terminated by signal SIGABRT (Abort) Otrzymaliśmy ten kernel panic, ponieważ jądro Linuxa uważa, że proces inicjalizacji zawsze działa. Bez niego system nie może już funkcjonować i zamyka się. Jednakże, ponieważ jest to proces w trybie użytkownika, uzyskany wynik wysyła siebie do SIGABRT, co prowadzi do wyjścia.
Konfiguracja sieci gościnnej
A tutaj zaczynamy mieć problemy. Sieć w User Mode Linux to miejsce, gdzie cała koncepcja ograniczonego "trybu użytkownika" zaczyna się rozpadać. Zazwyczaj na poziomie systemowym sieć jest ograniczona przywilejami trybów wykonywania, ze znanych nam powodów.
Przyp. tłum.: więcej o różnych opcjach pracy z siecią w UML można poczytać .
Podróż do slirp
Jednak istnieje stary i praktycznie nie wspierany program o nazwie , który pozwala User Mode Linux na interakcję z siecią. Działa on w zasadzie jak stos TCP/IP na poziomie użytkownika i nie wymaga żadnych uprawnień systemowych do uruchomienia. Narzędzie to zostało wydane w 1995 roku, a ostatnia aktualizacja miała miejsce w 2006 roku. Slirp jest bardzo stary. Przez czas braku wsparcia i aktualizacji kompilatory poszły tak daleko, że to narzędzie można scharakteryzować tylko jako .
Więc, zainstalujmy Slirp z repozytoriów Ubuntu i spróbujmy go uruchomić:
sudo apt-get install slirp
/usr/bin/slirp
Slirp v1.0.17 (BETA)
Copyright (c) 1995,1996 Danny Gasparovski i inni.
Wszystkie prawa zastrzeżone.
Ten program jest objęty prawami autorskimi, jest oprogramowaniem typu open source.
Proszę zapoznać się z plikiem COPYRIGHT, który został dołączony do pakietu Slirp
w celu uzyskania warunków ochrony praw autorskich.
Adres IP hosta Slirp: 127.0.0.1
Adres(y) IP twojego DNS: 1.1.1.1, 10.77.0.7
Twój adres to 10.0.2.15
(lub cokolwiek innego chcesz)
Wpisz pięć zer (0), aby wyjść.
[autodetect SLIP/CSLIP, MTU 1500, MRU 1500, 115200 baud]
SLiRP Gotowy ...
fish: "/usr/bin/slirp" zakończony sygnałem SIGSEGV (Błąd granicy adresu)Ojej. Zainstalujmy debugger dla Slirp i zobaczmy, czy możemy rozwiązać ten problem:
sudo apt-get install gdb slirp-dbgsym
gdb /usr/bin/slirp
GNU gdb (Ubuntu 8.1-0ubuntu3) 8.1.0.20180409-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
.
Find the GDB manual and other documentation resources online at:
.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /usr/bin/slirp...Reading symbols from /usr/lib/debug/.build-id/c6/2e75b69581a1ad85f72ac32c0d7af913d4861f.debug...done.
done.
(gdb) run
Starting program: /usr/bin/slirp
Slirp v1.0.17 (BETA)
Copyright (c) 1995,1996 Danny Gasparovski and others.
All rights reserved.
This program is copyrighted, free software.
Please read the file COPYRIGHT that came with the Slirp
package for the terms and conditions of the copyright.
IP address of Slirp host: 127.0.0.1
IP address of your DNS(s): 1.1.1.1, 10.77.0.7
Your address is 10.0.2.15
(or anything else you want)
Type five zeroes (0) to exit.
[autodetect SLIP/CSLIP, MTU 1500, MRU 1500, 115200 baud]
SLiRP Ready ...
Program received signal SIGSEGV, Segmentation fault.
ip_slowtimo () at ip_input.c:457
457 ip_input.c: No such file or directory.Błąd występuje w . Sprawdźmy stacktrace, może tam znajdziemy coś pomocnego:
(gdb) bt full
#0 ip_slowtimo () at ip_input.c:457
fp = 0x55784a40
#1 0x000055555556a57c in main_loop () at ./main.c:980
so =
so_next =
timeout = {tv_sec = 0, tv_usec = 0}
ret = 0
nfds = 0
ttyp =
ttyp2 =
best_time =
tmp_time =
#2 0x000055555555b116 in main (argc=1, argv=0x7fffffffdc58) at ./main.c:95
No locals.Widzimy, że błąd występuje podczas uruchamiania głównej pętli, gdy slirp próbuje sprawdzić czasy oczekiwania. W tym momencie powinnam zrezygnować z próby debugowania. Ale sprawdźmy, czy Slirp skompilowany z źródeł działa. Ponownie pobrałam archiwum bezpośrednio ze strony , ponieważ pobieranie czegokolwiek z wiersza poleceń to kłopot:
cd ~/dl
wget https://xena.greedo.xeserv.us/files/slirp-1.0.16.tar.gz
tar xf slirp-1.0.16.tar.gz
cd slirp-1.0.16/src
./configure --prefix=$HOME/prefix/slirp
make Widzimy alerty dotyczące niezdefiniowanych funkcji wbudowanych, co oznacza, że nie można zlinkować powstałego pliku binarnego. Wygląda na to, że w okresie między 2006 rokiem a obecnym momentem gcc przestał tworzyć symbole używane w wbudowanych funkcjach plików skompilowanych pośrednio. Spróbujmy zastąpić słowo kluczowe inline pustym komentarzem i sprawdźmy wynik:
vi slirp.h
:6
a
#define inline /**/
:wq
makeNie. To także nie działa. Wciąż nie mogę znaleźć symboli tych funkcji.
Na tym etapie się poddałam i zaczęłam szukać na Githubie . Moja teoria opierała się na tym, że w jakimś pakiecie kompilacji Heroku będą potrzebne mi pliki binarne. Ostatecznie poszukiwania doprowadziły mnie . Ściągnęłam i rozpakowałam uml.tar.gz i znalazłam następujące:
total 6136
-rwxr-xr-x 1 cadey cadey 79744 Dec 10 2017 ifconfig*
-rwxr-xr-x 1 cadey cadey 373 Dec 13 2017 init*
-rwxr-xr-x 1 cadey cadey 149688 Dec 10 2017 insmod*
-rwxr-xr-x 1 cadey cadey 66600 Dec 10 2017 route*
-rwxr-xr-x 1 cadey cadey 181056 Jun 26 2015 slirp*
-rwxr-xr-x 1 cadey cadey 5786592 Dec 15 2017 uml*
-rwxr-xr-x 1 cadey cadey 211 Dec 13 2017 uml_run*To jest plik binarny slirp! A czy on działa?
.\/slirp
Slirp v1.0.17 (BETA) FULL_BOLT
Copyright (c) 1995,1996 Danny Gasparovski i inni.
Wszystkie prawa zastrzeżone.
Ten program jest objęty prawami autorskimi i jest oprogramowaniem wolnym.
Proszę zapoznać się z plikiem COPYRIGHT, który dołączono do pakietu Slirp,
aby poznać warunki związane z prawami autorskimi.
Adres IP hosta Slirp: 127.0.0.1
Adres IP twojego DNS(u): 1.1.1.1, 10.77.0.7
Twój adres to 10.0.2.15
(lub cokolwiek innego, co chcesz)
Wpisz pięć zer (0), aby wyjść.
[autodetect SLIP\/CSLIP, MTU 1500, MRU 1500]
SLiRP Gotowy ...
Nie pada — więc powinno działać! Spróbujmy umieścić ten binarny plik w ~\/bin\/slirp:
cp slirp ~\/bin\/slirpNa wypadek, gdyby twórca pakietu go usunął, .
Konfiguracja sieci
Teraz skonfigurujmy sieć na naszym gościnym jądrze. :
linux
root=\/dev\/root
rootfstype=hostfs
rootflags=$HOME\/prefix\/uml-demo
rw
mem=64M
eth0=slirp,,$HOME\/bin\/slirp
init=\/bin\/shTeraz włączmy sieć:
mount -t proc proc proc\/\nmount -t sysfs sys sys\/\n\nifconfig eth0 10.0.2.14 netmask 255.255.255.240 broadcast 10.0.2.15
route add default gw 10.0.2.2 Pierwsze dwie komendy konfiguracji /proc i /sys są niezbędne do działania ifconfig, która ustawia interfejs sieciowy do komunikacji ze Slirp. Komenda trasa ustawia tabelę routingu jądra, aby wymusić przesyłanie całego ruchu przez tunel Slirp. Sprawdźmy to za pomocą zapytania DNS:
nslookup google.com 8.8.8.8\nSerwer: 8.8.8.8\nAdres 1: 8.8.8.8 dns.google\n\nNazwa: google.com\nAdres 1: 172.217.12.206 lga25s63-in-f14.1e100.net\nAdres 2: 2607:f8b0:4006:81b::200e lga25s63-in-x0e.1e100.netDziała!
Przyp. red.: Wygląda na to, że oryginalny post był pisany na desktopie z przewodową kartą sieciową lub inną konfiguracją, która nie wymaga dodatkowych sterowników. Na laptopie z WiFi 8265 od Intela przy uruchamianiu sieci pojawia się błąd.
/ # ifconfig eth0 10.0.2.14 netmask 255.255.255.240 broadcast 10.0.2.15
slirp_tramp failed - errno = 2
ifconfig: ioctl 0x8914 failed: No such file or directory
/ #Wygląda na to, że jądro nie może połączyć się ze sterownikiem karty sieciowej. Próba skompilowania firmware w jądrze niestety nie pomogła. W momencie publikacji w takiej konfiguracji nie znalazłem jeszcze rozwiązania. W prostszych konfiguracjach (na przykład w Virtualbox) interfejs działa poprawnie.
Zautomatyzujmy przekierowanie za pomocą następującego skryptu powłoki:
#!/bin/sh
# init.sh
mount -t proc proc proc/
mount -t sysfs sys sys/
ifconfig eth0 10.0.2.14 netmask 255.255.255.240 broadcast 10.0.2.15
route add default gw 10.0.2.2
echo "networking set up"
exec /tini /bin/shI oznaczmy go jako wykonywalny:
chmod +x init.shA następnie wprowadźmy zmiany w linii poleceń jądra:
linux
root=\/dev\/root
rootfstype=hostfs
rootflags=$HOME\/prefix\/uml-demo
rw
mem=64M
eth0=slirp,,$HOME\/bin\/slirp
init=\/init.sh
A następnie powtórzmy:
SLiRP Gotowe ...
konfiguracja sieci ustawiona
\/bin\/sh: can't access tty; kontrola zadań wyłączona
nslookup google.com 8.8.8.8
Serwer: 8.8.8.8
Adres 1: 8.8.8.8 dns.google
Nazwa: google.com
Adres 1: 172.217.12.206 lga25s63-in-f14.1e100.net
Adres 2: 2607:f8b0:4004:800::200e iad30s09-in-x0e.1e100.netSieć działa stabilnie!
Plik Docker
Aby ułatwić Ci wszystko to, zebrałam , który automatyzuje większość opisanych kroków i powinien zapewnić Ci działającą konfigurację. Mam też , która zawiera wszystko, co opisano w poście. Ważne jest, aby zrozumieć, że tutaj przedstawiłam tylko minimalną konfigurację.
Mam nadzieję, że ten post pomógł Ci zrozumieć, jak uruchomić gościnne jądro. Wyszło coś dużego, ale publikacja miała być wszechstronnym przewodnikiem na temat budowy, instalacji i konfiguracji Trybu Użytkownika w Linuksie dla nowoczesnych wersji systemów operacyjnych tej rodziny. Kolejne kroki powinny obejmować instalację usług i oprogramowania już wewnątrz gościnnego systemu. Ponieważ obrazy kontenerów Docker to po prostu rozpakowane archiwa tar, powinieneś być w stanie wyodrębnić obraz za pomocą docker export, a następnie określić ścieżkę jego instalacji w katalogu głównym systemu plików gościnnego jądra. Potem wykonaj skrypt powłoki.
Szczególne podziękowania dla Rkeene z #lobsters na Freenode. Bez jego pomocy w debugowaniu Slirp nie doszedłbym tak daleko. Nie mam pojęcia, jak jego system Slackware działa poprawnie z slirp, ale moje systemy Ubuntu i Alpine nie przyjęły slirp i zaproponowany mi przez Rkeene plik binarny. Ale wystarcza mi, że cokolwiek działa.
Źródło: habr.com
