Como começar a usar o modo de usuário no Linux

Introdução do tradutor: Tendo como pano de fundo a entrada massiva de vários tipos de contêineres em nossas vidas, pode ser bastante interessante e útil descobrir com quais tecnologias tudo começou. Alguns deles podem ser usados ​​​​de forma útil até hoje, mas nem todos se lembram de tais métodos (ou sabem se não foram detectados durante seu rápido desenvolvimento). Uma dessas tecnologias é o User Mode Linux. O autor do original pesquisou bastante, descobrindo quais dos desenvolvimentos antigos ainda funcionam e quais não, e reuniu algo como uma instrução passo a passo sobre como obter uma UML homebrew em 2k19. E sim, convidamos o autor do post original para Habr Cadey, então se você tiver alguma dúvida - pergunte em inglês nos comentários.

Como começar a usar o modo de usuário no Linux

O modo de usuário no Linux é, na verdade, uma porta do kernel do Linux para si mesmo. Este modo permite executar um kernel Linux completo como um processo de usuário e é comumente usado por desenvolvedores para testar drivers. Mas este modo também é útil como ferramenta de isolamento geral, cujo princípio é semelhante ao funcionamento de máquinas virtuais. Este modo fornece mais isolamento que o Docker, mas menos que uma máquina virtual completa como KVM ou Virtual Box.

Em geral, o Modo Usuário pode parecer uma ferramenta estranha e difícil de usar, mas ainda tem sua utilidade. Afinal, este é um kernel Linux completo executado por um usuário sem privilégios. Este recurso permite que códigos potencialmente não confiáveis ​​sejam executados sem qualquer ameaça à máquina host. E como este é um kernel completo, seus processos são isolados da máquina host, ou seja processos em execução dentro do modo de usuário não serão visíveis para o host. Não é como um contêiner Docker tradicional, no qual a máquina host sempre vê os processos dentro do armazenamento. Veja este pedaço de pstree de um dos meus servidores:

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

E compare isso com o pstree do kernel Linux no modo de usuário:

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

Ao trabalhar com contêineres Docker, posso ver no host os nomes dos processos que estão em execução no convidado. Com o modo de usuário Linux, isso não é possível. O que isso significa? Isso significa que as ferramentas de monitoramento executadas no subsistema de auditoria do Linux nao vejo processos em execução no sistema convidado. Mas em algumas situações, esse recurso pode se tornar uma faca de dois gumes.

Em geral, todo o post abaixo é uma coleção de pesquisas e tentativas aproximadas de alcançar o resultado desejado. Para fazer isso, tive que usar várias ferramentas antigas, ler as fontes do kernel, fazer depuração intensiva de código escrito na época em que eu ainda estava no ensino fundamental e também mexer nas compilações do Heroku usando um binário especial para encontrar as ferramentas que precisava. . Todo esse trabalho levou o pessoal do meu IRC a me chamar de mágico. Espero que este post sirva como documentação confiável para alguém tentar a mesma coisa com kernels e versões de sistema operacional mais recentes.

Fixação

A configuração do modo de usuário Linux é feita em várias etapas:

  • instalar dependências no host;
  • baixando o kernel do Linux;
  • configuração de compilação do kernel;
  • montagem do kernel;
  • instalação binária;
  • configurar o sistema de arquivos convidado;
  • seleção de parâmetros de inicialização do kernel;
  • configurar uma rede de convidados;
  • iniciando o kernel convidado.

Presumo que se você decidir fazer isso sozinho, provavelmente fará tudo o que está descrito em algum sistema Ubuntu ou semelhante ao Debian. Tentei implementar todos os itens acima em minha distribuição favorita - Alpine, mas não deu em nada, aparentemente devido ao fato de o kernel Linux ter glibc-ismos de ligação rígida para drivers no modo de usuário. Pretendo relatar isso ao upstream depois de finalmente entender o problema.

Instalando dependências no host

O Ubuntu requer pelo menos os seguintes pacotes para construir o kernel Linux (assumindo uma instalação limpa):

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

Você pode instalá-los com o seguinte comando (como root ou com sudo):

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

Observe que a execução do programa de configuração do menu do kernel Linux exigirá a instalação do libncurses-dev. Certifique-se de que esteja instalado usando o seguinte comando (como root ou usando sudo):

apt-get -y install libncurses-dev

Baixando o kernel

Decida onde fazer o download e então construa o kernel. Para esta operação, você precisará alocar cerca de 1,3 GB de espaço no disco rígido, portanto, certifique-se de tê-lo.

Então vá para kernel.org e obtenha o URL para baixar o kernel estável mais recente. No momento em que escrevo isso é: https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.1.16.tar.xz

Baixe este arquivo usando 'wget':

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

E extraia com 'tar':

tar xJf linux-5.1.16.tar.xz

Agora entramos no diretório criado ao descompactar o tarball:

cd linux-5.1.16

Configurando uma compilação de kernel

O sistema de compilação do kernel é um conjunto Makefiles с muitos ferramentas e scripts personalizados para automatizar o processo. Primeiro, abra o programa de configuração interativo:

make ARCH=um menuconfig

Ele irá construir e exibir parcialmente uma caixa de diálogo para você. Quando '[Select]', você poderá configurar usando as teclas Espaço ou Enter. Navegue pela janela, como de costume, com as setas do teclado “para cima” e “para baixo”, e selecione os elementos – “esquerda” ou “direita”.

O ponteiro de visualização —> significa que você está em um submenu, acessado pela tecla Enter. A saída é obviamente através de '[Exit]'.

Inclua as seguintes opções em '[Select]' e certifique-se de que eles tenham um '[*]' próximo a eles:

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

É isso, você pode sair desta janela selecionando sucessivamente '[Exit]'. Apenas certifique-se de que você seja solicitado a salvar a configuração no final e selecione '[Yes]'.

Eu recomendo que você experimente as opções de construção do kernel depois de ler este post. Com esses experimentos, você pode aprender muito sobre como a mecânica do kernel de baixo nível funciona e como diferentes sinalizadores afetam a forma como o kernel é construído.

Construindo o kernel

O kernel do Linux é um grande programa que faz muitas coisas. Mesmo com uma configuração tão mínima em hardware antigo, a construção pode demorar um pouco. Então construa o kernel com o seguinte comando:

make ARCH=um -j$(nproc)

Para que? Este comando dirá ao nosso construtor para usar todos os núcleos e threads de CPU disponíveis no processo de construção. Equipe $(nproc) no final do Build substitui a saída do comando nproc, que faz parte coreutils em uma versão padrão do Ubuntu.

Depois de algum tempo, nosso kernel será compilado em um arquivo executável ./linux.

Instalando o binário

Como o User Mode no Linux cria um binário regular, você pode instalá-lo como qualquer outro utilitário. Veja como eu fiz isso:

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

Também vale a pena ter certeza de que ~/bin está em seu $PATH:

export PATH=$PATH:$HOME/bin

Configurando o sistema de arquivos convidado

Crie um diretório para o sistema de arquivos convidado:

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

Abra alpinelinux.org e em seção de download encontre o link de download real MINI ROOT FILESYSTEM. No momento em que este artigo foi escrito, isso era:

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

Baixe este tarball usando 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

Agora entre no diretório do sistema de arquivos convidado e extraia o arquivo:

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

As ações descritas criarão um pequeno modelo de sistema de arquivos. Devido à natureza do sistema, será extremamente difícil instalar pacotes através do gerenciador apk Alpine. Mas este FS será suficiente para avaliar a ideia geral.

Também precisamos de uma ferramenta tini para reduzir o consumo de memória processos zumbis nosso kernel convidado.

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

Criando uma linha de comando do kernel

O kernel do Linux, como a maioria dos outros programas, possui argumentos de linha de comando que podem ser acessados ​​especificando a chave --help.

Ele mesmo - ajuda

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.

Este painel destaca os principais parâmetros do lançamento. Vamos executar o kernel com o conjunto mínimo de opções necessário:

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

As linhas acima dizem ao nosso kernel o seguinte:

  • Suponha que o sistema de arquivos raiz seja um pseudodispositivo /dev/root.
  • Escolher hostfs como um driver do sistema de arquivos raiz.
  • Monte o sistema de arquivos convidado que criamos no dispositivo raiz.
  • E sim, no modo leitura-gravação.
  • Use apenas 64 megabytes de RAM (você pode usar muito menos dependendo do que planeja fazer, mas 64 MB parece ser a quantidade ideal).
  • O kernel inicia automaticamente /bin/sh como init-processo.

Execute este comando e você deverá obter algo como o seguinte:

Outra folha

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

As manipulações acima nos darão sistema convidado no mínimo, sem coisas como /proc ou nome de host atribuído. Por exemplo, tente os seguintes comandos:

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

Para sair do convidado, digite exit ou pressione control-d. Isso matará o shell seguido por um kernel panic:

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

Tivemos esse kernel panic porque o kernel do Linux pensa que o processo de inicialização está sempre em execução. Sem ele, o sistema não funciona mais e trava. Mas como este é um processo em modo de usuário, a saída resultante é enviada para SIGABRT, o que resulta em uma saída.

Configurando uma rede de convidados

E é aqui que as coisas começam a dar errado. Rede no Modo Usuário O Linux é onde todo o conceito de "modo usuário" limitado começa a desmoronar. Afinal, geralmente no nível do sistema, a rede é limitada privilegiado modos de execução por razões compreensíveis para todos nós.

Observação. per.: você pode ler mais sobre as diferentes opções para trabalhar com uma rede em UML aqui.

Jornada para Slirp

No entanto, existe uma ferramenta antiga e quase sem suporte chamada Slip, com o qual o User Mode Linux pode interagir com a rede. Ele funciona como uma pilha TCP/IP em nível de usuário e não requer nenhuma permissão do sistema para ser executado. Esta ferramenta foi lançado em 1995, e a atualização mais recente está datada Ano 2006. Slirp é muito antigo. Durante o tempo sem suporte e atualizações, os compiladores foram tão longe que agora esta ferramenta só pode ser descrita como podridão de código.

Então vamos baixar o Slirp dos repositórios do Ubuntu e tentar executá-lo:

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)

Ah, deuses. Vamos instalar o depurador do Slirp e ver se conseguimos descobrir o que está acontecendo aqui:

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.

O erro bate em nós está linha. Vejamos o stacktrace, talvez algo nos ajude nisso:

(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.

Aqui vemos que o travamento ocorre durante a inicialização do loop principal, quando o slirp tenta verificar os tempos limite. Neste ponto, tive que desistir de tentar depurar. Mas vamos ver se o Slirp construído a partir de classificações funciona. Baixei novamente o arquivo diretamente do site Sourceforge, porque arrastar algo de lá pela linha de comando é uma dor:

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

Aqui vemos alertas sobre funções integradas indefinidas, ou seja, sobre a incapacidade de vincular o arquivo binário resultante. Parece que entre 2006 e este ponto, o gcc parou de produzir símbolos usados ​​em funções integradas de arquivos intermediários compilados. Vamos tentar substituir a palavra-chave inline em um comentário vazio e veja o resultado:

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

Não. Isso também não funciona. Ainda não consigo encontrar símbolos para essas funções.

Nesse ponto, desisti e comecei a procurar no Github Pacotes de compilação Heroku. Minha teoria era que algum pacote de compilação do Heroku conteria os binários que eu precisava. No final, a busca me levou aqui. baixei e descompactei uml.tar.gz e encontrei o seguinte:

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*

Este é o binário slirp! Ele trabalha?

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

Não trava - então deve funcionar! Vamos plantar esse binário em ~/bin/slirp:

cp slirp ~/bin/slirp

Caso o criador do pacote o remova, eu fiz um espelho.

Configuração de rede

Agora vamos configurar a rede em nosso kernel convidado. Atualizar opções de inicialização:

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

Agora vamos ligar a rede:

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

Os dois primeiros comandos de configuração /proc и /sys necessário para o trabalho ifconfig, que configura a interface de rede para se comunicar com o Slirp. Equipe route define a tabela de roteamento do kernel para forçar todo o tráfego a ser enviado através do túnel Slirp. Vamos verificar isso com uma consulta 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

Работает!

Nota por.: Aparentemente, a postagem original foi escrita em um desktop com placa de rede com fio ou alguma outra configuração que não exigia drivers adicionais. Em um laptop com WiFi 8265 da Intel, ocorre um erro ao aumentar a rede

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

Aparentemente, o kernel não consegue se comunicar com o driver da placa de rede. Uma tentativa de compilar o firmware no kernel, infelizmente, não resolveu a situação. No momento da publicação não foi possível encontrar solução nesta configuração. Em configurações mais simples (por exemplo, no Virtualbox), a interface sobe corretamente.

Vamos automatizar o redirecionamento com o seguinte script de 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

E marque-o como executável:

chmod +x init.sh

E então faremos alterações na linha de comando do kernel:

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

E vamos repetir:

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

A rede está estável!

arquivo docker

Para facilitar a verificação de tudo isso, coletei dockerfile, que automatiza a maioria das etapas descritas e deve fornecer uma configuração funcional. eu também tenho kernel pré-configurado, que tem tudo o que está descrito no post. Mas é importante compreender que aqui descrevi apenas a configuração mínima.

Espero que este post tenha ajudado você a entender como criar um kernel convidado. Acabou sendo uma espécie de monstro, mas a publicação foi concebida como um guia completo sobre construção, instalação e configuração do Modo Usuário no Linux para versões modernas de sistemas operacionais desta família. As próximas etapas devem incluir a instalação de serviços e outros softwares já dentro do sistema convidado. Como as imagens de contêiner do Docker são apenas tarballs divulgados, você poderá extrair a imagem via docker exporte, em seguida, determine seu caminho de instalação na raiz do sistema de arquivos do kernel convidado. Bem, então execute o script de shell.

Agradecimentos especiais a Rkeene do #lobsters no Freenode. Sem a ajuda dele para depurar o Slirp, eu não teria chegado tão longe. Não tenho ideia de como o sistema Slackware dele funciona corretamente com o slirp, mas meus sistemas Ubuntu e Alpine não aceitavam o slirp e o binário Rkeene me sugeriu. Mas é suficiente para mim que pelo menos algo funcione para mim.

Fonte: habr.com

Adicionar um comentário