Cum să începeți să utilizați Modul utilizator în Linux

Introducere de la traducător: Pe fundalul intrării masive în viața noastră a diferitelor tipuri de containere, poate fi destul de interesant și util să aflăm cu ce tehnologii au început totul o dată. Unele dintre ele pot fi folosite util până astăzi, dar nu toată lumea își amintește astfel de metode (sau știe dacă nu au fost prinse în timpul dezvoltării lor rapide). O astfel de tehnologie este User Mode Linux. Autorul originalului a făcut multe săpături, dându-și seama care dintre vechile dezvoltări încă funcționează și care nu și a pregătit ceva de genul unei instrucțiuni pas cu pas despre cum să obții un UML homebrew în 2k19. Și da, l-am invitat pe autorul postării originale la Habr Cadey, așa că dacă aveți întrebări - întrebați în engleză în comentarii.

Cum să începeți să utilizați Modul utilizator în Linux

Modul utilizator în Linux este, de fapt, un port al nucleului Linux pentru sine. Acest mod vă permite să rulați un nucleu Linux complet ca proces de utilizator și este folosit în mod obișnuit de dezvoltatori pentru a testa driverele. Dar acest mod este util și ca instrument general de izolare, al cărui principiu este similar cu funcționarea mașinilor virtuale. Acest mod oferă mai multă izolare decât Docker, dar mai puțin decât o mașină virtuală cu drepturi depline, cum ar fi KVM sau Virtual Box.

În general, Modul utilizator poate părea un instrument ciudat și dificil de utilizat, dar încă își are utilizările sale. La urma urmei, acesta este un nucleu Linux cu drepturi depline care rulează de la un utilizator neprivilegiat. Această caracteristică permite rularea unui cod potențial neîncrezător fără nicio amenințare la adresa mașinii gazdă. Și deoarece acesta este un nucleu cu drepturi depline, procesele sale sunt izolate de mașina gazdă, adică procesele care rulează în modul utilizator nu vor fi vizibile pentru gazdă. Acesta nu este ca containerul obișnuit Docker, caz în care mașina gazdă vede întotdeauna procesele din interiorul depozitului. Uită-te la această bucată de pstree de pe unul dintre serverele mele:

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}]

Și comparați acest lucru cu pstree-ul nucleului Linux în modul utilizator:

linux─┬─5*[linux]
      └─slirp

Când lucrez cu containere Docker, pot vedea de la gazdă numele proceselor care rulează în invitat. Cu modul utilizator Linux, acest lucru nu este posibil. Ce înseamnă? Aceasta înseamnă că instrumentele de monitorizare care rulează prin subsistemul de audit al Linux nu vad procesele care rulează în sistemul oaspete. Dar, în unele situații, această caracteristică poate deveni o sabie cu două tăișuri.

În general, întreaga postare de mai jos este o colecție de cercetări și încercări brute de a obține rezultatul dorit. Pentru a face acest lucru, a trebuit să folosesc diverse instrumente antice, să citesc sursele nucleului, să fac o depanare intensivă a codului scris în zilele în care eram încă în școala elementară și, de asemenea, să mă ocup de build-urile Heroku folosind un binar special pentru a găsi instrumentele de care aveam nevoie. . Toată această muncă i-a determinat pe băieții de pe IRC-ul meu să mă numească magie. Sper că această postare să servească drept documentație de încredere pentru ca cineva să încerce același lucru cu nuclee și versiuni de SO mai noi.

ajustare

Configurarea modului utilizator Linux se face în mai mulți pași:

  • instalarea de dependențe pe gazdă;
  • descărcarea nucleului Linux;
  • configurarea construirii nucleului;
  • asamblarea miezului;
  • instalare binară;
  • configurarea sistemului de fișiere invitat;
  • selectarea parametrilor de lansare a nucleului;
  • crearea unei rețele de invitați;
  • pornind nucleul invitat.

Presupun că, dacă decideți să o faceți singur, cel mai probabil veți face tot ce este descris într-un sistem de tip Ubuntu sau Debian. Am încercat să implementez toate cele de mai sus în distribuția mea preferată - Alpine, dar nu a ieșit nimic, aparent datorită faptului că kernel-ul Linux are un glibc-ism de legare pentru drivere în modul utilizator. Plănuiesc să raportez acest lucru în amonte după ce în sfârșit înțeleg problema.

Instalarea dependențelor pe gazdă

Ubuntu necesită cel puțin următoarele pachete pentru a construi nucleul Linux (presupunând o instalare curată):

- 'build-essential'
- 'flex'
- 'bison'
- 'xz-utils'
- 'wget'
- 'ca-certificates'
- 'bc'
- 'linux-headers'

Le puteți instala cu următoarea comandă (ca root sau cu sudo):

apt-get -y install build-essential flex bison xz-utils wget ca-certificates bc 
                   linux-headers-$(uname -r)

Rețineți că rularea programului de configurare a meniului nucleului Linux va necesita instalarea libncurses-dev. Vă rugăm să vă asigurați că este instalat cu următoarea comandă (ca root sau cu sudo):

apt-get -y install libncurses-dev

Descărcare kernel

Decideți unde să descărcați și apoi construiți nucleul. Pentru această operațiune, va trebui să alocați aproximativ 1,3 GB de spațiu pe hard disk, așa că asigurați-vă că îl aveți.

După du-te la kernel.org și obțineți adresa URL pentru a descărca cel mai recent kernel stabil. La momentul scrierii, acesta este: https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.1.16.tar.xz

Descărcați acest fișier folosind 'wget':

wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.1.16.tar.xz

Și extrage-l cu 'tar':

tar xJf linux-5.1.16.tar.xz

Acum intrăm în directorul creat la despachetarea tarball-ului:

cd linux-5.1.16

Configurare build kernel

Sistemul de construire a nucleului este un set Makefiles с mulți instrumente și scripturi personalizate pentru a automatiza procesul. Mai întâi, deschideți programul de configurare interactiv:

make ARCH=um menuconfig

Acesta va construi parțial și va afișa o casetă de dialog pentru dvs. Când '[Select]', veți putea configura folosind tastele Space sau Enter. Navigați în fereastra, ca de obicei, cu săgețile de la tastatură „sus” și „jos” și selectați elemente - „stânga” sau „dreapta”.

Indicatorul de vizualizare —> înseamnă că vă aflați într-un submeniu, accesat prin tasta Enter. Calea de ieșire din ea este, evident, prin "[Exit]".

Includeți următoarele opțiuni în „[Select]' și asigurați-vă că au un „[*]” lângă ei:

UML-specific Options:
  - Host filesystem
Networking support (enable this to get the submenu to show up):
  - Networking options:
    - TCP/IP Networking
UML Network devices:
  - Virtual network device
  - SLiRP transport

Gata, poți ieși din această fereastră selectând succesiv '[Exit]'. Doar asigurați-vă că vi se solicită să salvați configurația la sfârșit și selectați „[Yes]".

Vă recomand să vă jucați cu opțiunile de construire a nucleului după ce citiți această postare. Prin aceste experimente, puteți învăța multe în ceea ce privește înțelegerea muncii mecanicilor nucleului de nivel scăzut și impactul diferitelor steaguri asupra asamblarii acestuia.

Construirea nucleului

Nucleul Linux este un program mare care face o mulțime de lucruri. Chiar și cu o configurație atât de minimă pe hardware vechi, construirea poate dura destul de mult. Deci, construiți nucleul cu următoarea comandă:

make ARCH=um -j$(nproc)

Pentru ce? Această comandă va spune constructorului nostru să folosească toate nucleele CPU și firele de execuție disponibile în procesul de construire. Echipă $(nproc) la sfârșitul Build înlocuiește rezultatul comenzii nproc, care face parte din coreutils într-o versiune standard Ubuntu.

După ceva timp, nucleul nostru va fi compilat într-un fișier executabil ./linux.

Instalarea binarului

Deoarece User Mode în Linux creează un binar obișnuit, îl puteți instala ca orice alt utilitar. Iată cum am făcut-o:

mkdir -p ~/bin
cp linux ~/bin/linux

De asemenea, merită să vă asigurați că ~/bin este în tine $PATH:

export PATH=$PATH:$HOME/bin

Configurarea sistemului de fișiere pentru invitați

Creați un director pentru sistemul de fișiere invitat:

mkdir -p $HOME/prefix/uml-demo
cd $HOME/prefix

Deschideți alpinelinux.org și în secțiunea de descărcare găsiți linkul de descărcare real MINI ROOT FILESYSTEM. La momentul scrierii, acesta era:

http://dl-cdn.alpinelinux.org/alpine/v3.10/releases/x86_64/alpine-minirootfs-3.10.0-x86_64.tar.gz

Descărcați acest tarball folosind 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.gz

Acum intrați în directorul sistemului de fișiere invitat și extrageți arhiva:

cd uml-demo
tar xf ../alpine-rootfs.tgz

Acțiunile descrise vor crea un șablon mic de sistem de fișiere. Datorită naturii sistemului, va fi extrem de dificil să instalați pachete prin intermediul managerului de apk Alpine. Dar acest FS va fi suficient pentru a evalua ideea generală.

Avem nevoie și de un instrument tini pentru a reduce consumul de memorie procese zombie nucleul nostru invitat.

wget -O tini https://github.com/krallin/tini/releases/download/v0.18.0/tini-static
chmod +x tini

Crearea unei linii de comandă a nucleului

Nucleul Linux, ca majoritatea celorlalte programe, are argumente de linie de comandă care pot fi accesate prin specificarea cheii --help.

El însuși — ajutor

linux --help
User Mode Linux v5.1.16
        available at http://user-mode-linux.sourceforge.net/

--showconfig
    Prints the config file that this UML binary was generated from.

iomem=<name>,<file>
    Configure <file> as an IO memory region named <name>.

mem=<Amount of desired ram>
    This controls how much "physical" memory the kernel allocates
    for the system. The size is specified as a number followed by
    one of 'k', 'K', 'm', 'M', which have the obvious meanings.
    This is not related to the amount of memory in the host.  It can
    be more, and the excess, if it's ever used, will just be swapped out.
        Example: mem=64M

--help
    Prints this message.

debug
    this flag is not needed to run gdb on UML in skas mode

root=<file containing the root fs>
    This is actually used by the generic kernel in exactly the same
    way as in any other kernel. If you configure a number of block
    devices and want to boot off something other than ubd0, you
    would use something like:
        root=/dev/ubd5

--version
    Prints the version number of the kernel.

umid=<name>
    This is used to assign a unique identity to this UML machine and
    is used for naming the pid file and management console socket.

con[0-9]*=<channel description>
    Attach a console or serial line to a host channel.  See
    http://user-mode-linux.sourceforge.net/old/input.html for a complete
    description of this switch.

eth[0-9]+=<transport>,<options>
    Configure a network device.
    
aio=2.4
    This is used to force UML to use 2.4-style AIO even when 2.6 AIO is
    available.  2.4 AIO is a single thread that handles one request at a
    time, synchronously.  2.6 AIO is a thread which uses the 2.6 AIO
    interface to handle an arbitrary number of pending requests.  2.6 AIO
    is not available in tt mode, on 2.4 hosts, or when UML is built with
    /usr/include/linux/aio_abi.h not available.  Many distributions don't
    include aio_abi.h, so you will need to copy it from a kernel tree to
    your /usr/include/linux in order to build an AIO-capable UML

nosysemu
    Turns off syscall emulation patch for ptrace (SYSEMU).
    SYSEMU is a performance-patch introduced by Laurent Vivier. It changes
    behaviour of ptrace() and helps reduce host context switch rates.
    To make it work, you need a kernel patch for your host, too.
    See http://perso.wanadoo.fr/laurent.vivier/UML/ for further
    information.

uml_dir=<directory>
    The location to place the pid and umid files.

quiet
    Turns off information messages during boot.

hostfs=<root dir>,<flags>,...
    This is used to set hostfs parameters.  The root directory argument
    is used to confine all hostfs mounts to within the specified directory
    tree on the host.  If this isn't specified, then a user inside UML can
    mount anything on the host that's accessible to the user that's running
    it.
    The only flag currently supported is 'append', which specifies that all
    files opened by hostfs will be opened in append mode.

Acest panou evidențiază principalii parametri ai lansării. Să rulăm nucleul cu setul minim necesar de opțiuni:

linux 
  root=/dev/root 
  rootfstype=hostfs 
  rootflags=$HOME/prefix/uml-demo 
  rw 
  mem=64M 
  init=/bin/sh

Rândurile de mai sus spun nucleului nostru următoarele:

  • Să presupunem că sistemul de fișiere rădăcină este un pseudo-dispozitiv /dev/root.
  • Alege hostfs ca driver de sistem de fișiere rădăcină.
  • Montați sistemul de fișiere invitat pe care l-am creat pe dispozitivul rădăcină.
  • Și da, în modul citire-scriere.
  • Folosiți doar 64 MB de RAM (puteți folosi mult mai puțin în funcție de ceea ce intenționați să faceți, dar 64 MB pare a fi cantitatea optimă).
  • Nucleul pornește automat /bin/sh ca init-proces.

Rulați această comandă și ar trebui să obțineți ceva de genul următor:

Încă o foaie

Core dump limits :
        soft - 0
        hard - NONE
Checking that ptrace can change system call numbers...OK
Checking syscall emulation patch for ptrace...OK
Checking advanced syscall emulation patch for ptrace...OK
Checking environment variables for a tempdir...none found
Checking if /dev/shm is on tmpfs...OK
Checking PROT_EXEC mmap in /dev/shm...OK
Adding 32137216 bytes to physical memory to account for exec-shield gap
Linux version 5.1.16 (cadey@kahless) (gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1)) #30 Sun Jul 7 18:57:19 UTC 2019
Built 1 zonelists, mobility grouping on.  Total pages: 23898
Kernel command line: root=/dev/root rootflags=/home/cadey/dl/uml/alpine rootfstype=hostfs rw mem=64M init=/bin/sh
Dentry cache hash table entries: 16384 (order: 5, 131072 bytes)
Inode-cache hash table entries: 8192 (order: 4, 65536 bytes)
Memory: 59584K/96920K available (2692K kernel code, 708K rwdata, 588K rodata, 104K init, 244K bss, 37336K reserved, 0K cma-reserved)
SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
NR_IRQS: 15
clocksource: timer: mask: 0xffffffffffffffff max_cycles: 0x1cd42e205, max_idle_ns: 881590404426 ns
Calibrating delay loop... 7479.29 BogoMIPS (lpj=37396480)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 512 (order: 0, 4096 bytes)
Mountpoint-cache hash table entries: 512 (order: 0, 4096 bytes)
Checking that host ptys support output SIGIO...Yes
Checking that host ptys support SIGIO on close...No, enabling workaround
devtmpfs: initialized
random: get_random_bytes called from setup_net+0x48/0x1e0 with crng_init=0
Using 2.6 host AIO
clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
futex hash table entries: 256 (order: 0, 6144 bytes)
NET: Registered protocol family 16
clocksource: Switched to clocksource timer
NET: Registered protocol family 2
tcp_listen_portaddr_hash hash table entries: 256 (order: 0, 4096 bytes)
TCP established hash table entries: 1024 (order: 1, 8192 bytes)
TCP bind hash table entries: 1024 (order: 1, 8192 bytes)
TCP: Hash tables configured (established 1024 bind 1024)
UDP hash table entries: 256 (order: 1, 8192 bytes)
UDP-Lite hash table entries: 256 (order: 1, 8192 bytes)
NET: Registered protocol family 1
console [stderr0] disabled
mconsole (version 2) initialized on /home/cadey/.uml/tEwIjm/mconsole
Checking host MADV_REMOVE support...OK
workingset: timestamp_bits=62 max_order=14 bucket_order=0
Block layer SCSI generic (bsg) driver version 0.4 loaded (major 254)
io scheduler noop registered (default)
io scheduler bfq registered
loop: module loaded
NET: Registered protocol family 17
Initialized stdio console driver
Using a channel type which is configured out of UML
setup_one_line failed for device 1 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 2 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 3 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 4 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 5 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 6 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 7 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 8 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 9 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 10 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 11 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 12 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 13 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 14 : Configuration failed
Using a channel type which is configured out of UML
setup_one_line failed for device 15 : Configuration failed
Console initialized on /dev/tty0
console [tty0] enabled
console [mc-1] enabled
Failed to initialize ubd device 0 :Couldn't determine size of device's file
VFS: Mounted root (hostfs filesystem) on device 0:11.
devtmpfs: mounted
This architecture does not have kernel memory protection.
Run /bin/sh as init process
/bin/sh: can't access tty; job control turned off
random: fast init done
/ # 

Manipulările de mai sus ne vor oferi sistem invitat la minimum, fără lucruri de genul /proc sau nume de gazdă atribuit. De exemplu, încercați următoarele comenzi:

- uname -av
- cat /proc/self/pid
- hostname

Pentru a vă deconecta de la invitat, tastați exit sau apăsați control-d. Acest lucru va ucide shell-ul urmat de o panică a nucleului:

/ # exit
Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000000
fish: “./linux root=/dev/root rootflag…” terminated by signal SIGABRT (Abort)

Am primit această panică a nucleului deoarece nucleul Linux crede că procesul de inițializare rulează întotdeauna. Fără el, sistemul nu mai poate funcționa și se blochează. Dar, deoarece acesta este un proces în modul utilizator, rezultatul rezultat se trimite singur către SIGABRT, care are ca rezultat o ieșire.

Configurarea rețelei pentru oaspeți

Și aici lucrurile încep să meargă prost. Rețeaua în modul utilizator Linux este locul în care întregul concept al „modului utilizator” limitat începe să se destrame. La urma urmei, de obicei la nivel de sistem, rețeaua este limitată privilegiat moduri de execuție din motive de înțeles tuturor.

Notă. per .: puteți citi mai multe despre diferitele opțiuni de lucru cu o rețea în UML aici.

Călătorie la Slirp

Cu toate acestea, există un instrument străvechi și aproape neacceptat numit Slirp, cu care User Mode Linux poate interacționa cu rețeaua. Funcționează la fel ca o stivă TCP/IP la nivel de utilizator și nu necesită permisiuni de sistem pentru a rula. Acest instrument a fost lansat in 1995, iar cea mai recentă actualizare este datată Anul 2006. Slirp este foarte vechi. În timpul fără suport și actualizări, compilatorii au mers atât de departe încât acum acest instrument poate fi descris doar ca putregaiul codului.

Deci, să descarcăm Slirp din depozitele Ubuntu și să încercăm să-l rulăm:

sudo apt-get install slirp
/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 ...
fish: “/usr/bin/slirp” terminated by signal SIGSEGV (Address boundary error)

O, Doamne. Să instalăm depanatorul lui Slirp și să vedem dacă ne putem da seama ce se întâmplă aici:

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 <http://gnu.org/licenses/gpl.html>
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:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
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.

Eroarea bate în noi această linie. Să ne uităm la stacktrace, poate că ceva ne va ajuta acolo:

(gdb) bt full
#0  ip_slowtimo () at ip_input.c:457
        fp = 0x55784a40
#1  0x000055555556a57c in main_loop () at ./main.c:980
        so = <optimized out>
        so_next = <optimized out>
        timeout = {tv_sec = 0, tv_usec = 0}
        ret = 0
        nfds = 0
        ttyp = <optimized out>
        ttyp2 = <optimized out>
        best_time = <optimized out>
        tmp_time = <optimized out>
#2  0x000055555555b116 in main (argc=1, argv=0x7fffffffdc58) at ./main.c:95
No locals.

Aici vedem că prăbușirea are loc în timpul pornirii buclei principale, când slirp încearcă să verifice timeout-uri. În acest moment, a trebuit să renunț să mai încerc să depanez. Dar să vedem dacă Slirp construit din feluri funcționează. Am re-descărcat arhiva direct de pe site SourceForge, deoarece a trage ceva de acolo prin linia de comandă este o durere:

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

Aici vedem alerte despre funcțiile încorporate nedefinite, adică despre incapacitatea de a lega fișierul binar rezultat. Se pare că între 2006 și acest moment, gcc a încetat să mai producă simboluri utilizate în funcțiile încorporate ale fișierelor compilate intermediare. Să încercăm să înlocuim cuvântul cheie inline pe un comentariu gol și uită-te la rezultat:

vi slirp.h
:6
a
<enter>
#define inline /**/
<escape>
:wq
make

Nu. Nici asta nu merge. Încă nu pot găsi simboluri pentru aceste funcții.

În acest moment, am renunțat și am început să caut pe Github Heroku construiește pachete. Teoria mea a fost că un pachet de compilare Heroku ar conține binarele de care aveam nevoie. Până la urmă, căutarea m-a condus aici. Am descărcat și despachetat uml.tar.gz si a gasit urmatoarele:

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*

Acesta este binarul slirp! Lucreaza?

./slirp
Slirp v1.0.17 (BETA) FULL_BOLT

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]

SLiRP Ready ...

Nu se blochează - așa că ar trebui să funcționeze! Să plantăm acest binar în ~/bin/slirp:

cp slirp ~/bin/slirp

În cazul în care creatorul pachetului îl elimină, I a făcut o oglindă.

Configurare rețea

Acum să configuram rețeaua pe kernel-ul nostru invitat. Actualizați opțiunile de lansare:

linux 
  root=/dev/root 
  rootfstype=hostfs 
  rootflags=$HOME/prefix/uml-demo 
  rw 
  mem=64M 
  eth0=slirp,,$HOME/bin/slirp 
  init=/bin/sh

Acum să pornim rețeaua:

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

Primele două comenzi de configurare /proc и /sys necesare muncii ifconfig, care setează interfața de rețea să comunice cu Slirp. Echipă route setează tabela de rutare a nucleului pentru a forța tot traficul să fie trimis prin tunelul Slirp. Să verificăm asta cu o interogare DNS:

nslookup google.com 8.8.8.8
Server:    8.8.8.8
Address 1: 8.8.8.8 dns.google

Name:      google.com
Address 1: 172.217.12.206 lga25s63-in-f14.1e100.net
Address 2: 2607:f8b0:4006:81b::200e lga25s63-in-x0e.1e100.net

Funcționează!

Notă pentru .: Aparent, postarea inițială a fost scrisă pe un desktop cu o placă de rețea cu fir sau altă configurație care nu necesita drivere suplimentare. Pe un laptop cu WiFi 8265 de la Intel, apare o eroare la ridicarea rețelei

/ # 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
/ #

Aparent, nucleul nu poate comunica cu driverul plăcii de rețea. O încercare de a compila firmware-ul în kernel, din păcate, nu a remediat situația. La momentul publicării, nu a fost posibilă găsirea unei soluții în această configurație. La configurații mai simple (de exemplu, în Virtualbox), interfața crește corect.

Să automatizăm redirecționarea cu următorul script shell:

#!/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/sh

Și marcați-l ca executabil:

chmod +x init.sh

Și apoi vom face modificări în linia de comandă a nucleului:

linux 
  root=/dev/root 
  rootfstype=hostfs 
  rootflags=$HOME/prefix/uml-demo 
  rw 
  mem=64M 
  eth0=slirp,,$HOME/bin/slirp 
  init=/init.sh

Și să repetăm:

SLiRP Ready ...
networking set up
/bin/sh: can't access tty; job control turned off

nslookup google.com 8.8.8.8
Server:    8.8.8.8
Address 1: 8.8.8.8 dns.google

Name:      google.com
Address 1: 172.217.12.206 lga25s63-in-f14.1e100.net
Address 2: 2607:f8b0:4004:800::200e iad30s09-in-x0e.1e100.net

Rețeaua este stabilă!

fișier docker

Pentru a vă fi mai ușor să verificați toate acestea, am adunat Dockerfile, care automatizează majoritatea pașilor descriși și ar trebui să vă ofere o configurație funcțională. De asemenea, am nucleu preconfigurat, care are tot ce este descris în postare. Dar este important de înțeles că aici am conturat doar setarea minimă.

Sper că această postare v-a ajutat să înțelegeți cum să ridicați un nucleu invitat. S-a dovedit a fi un fel de monstru, dar publicația a fost concepută ca un ghid cuprinzător pentru construirea, instalarea și configurarea modului utilizator în Linux sub versiunile moderne ale sistemelor de operare ale acestei familii. Următorii pași ar trebui să includă instalarea serviciilor și a altor software-uri deja în interiorul sistemului oaspete. Deoarece imaginile containerului Docker sunt doar tarball-uri mediatizate, ar trebui să puteți extrage imaginea prin intermediul docker export, și apoi determinați calea de instalare a acesteia în rădăcina sistemului de fișiere al nucleului invitat. Ei bine, atunci executați scriptul shell.

Mulțumiri speciale lui Rkeene de la #lobsters pe Freenode. Fără ajutorul lui la depanarea lui Slirp, nu aș fi ajuns atât de departe. Nu am idee cum funcționează corect sistemul său Slackware cu slirp, dar sistemele mele Ubuntu și Alpine nu au acceptat slirp și mi-a sugerat binarul Rkeene. Dar îmi este suficient că măcar ceva să-mi funcționeze.

Sursa: www.habr.com

Adauga un comentariu