Introduction from the translator: In light of the widespread introduction of various types of containers into our lives, it can be quite interesting and useful to know what technologies it all started with. Some of them can still be usefully applied today, but not everyone remembers (or knows, if they missed the time of their rapid development) such methods. One such technology is User Mode Linux. The author of the original material has done significant research to determine which old solutions still work and which do not, and has compiled something like a step-by-step guide on how to set up a homemade UML in 2019. And yes, we invited the author of the original post to write for Habrahabr. , so if you have any questions, please ask them in English in the comments.

User Mode in Linux is essentially a port of the Linux kernel to itself. This mode allows you to run a full-fledged Linux kernel as a user process and is typically used by developers for testing drivers. But this mode is also useful as a general isolation tool, the principle of which is similar to the workings of virtual machines. This mode provides greater isolation than Docker, but less than a full virtual machine like KVM or VirtualBox.
Overall, User Mode may seem like a strange and complex tool to use, but it does have its areas of application. After all, it’s a complete Linux kernel running under a non-privileged user. This feature allows you to run potentially untrustworthy code without any threats to the host machine. And since it’s a complete kernel, its processes are isolated from the host machine, meaning the processes running inside User Mode will not be visible to the host. This is unlike a typical Docker container, where the host machine always sees the processes inside the container. Take a look at this snippet of pstree from one of my servers:
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}]And compare this with the Linux kernel pstree in User Mode:
linux─┬─5*[linux]
└─slirpWhen working with Docker containers, I can see from the host the names of processes running in the guest system. This is not possible with Linux User Mode. What does this mean? It means that monitoring tools that operate through the Linux auditing subsystem cannot see the processes executing in the guest system. However, in some situations, this feature can become a double-edged sword.
Overall, the post below is a collection of research and rough attempts to achieve the desired result. For this, I had to use various ancient tools, read kernel sources, engage in intensive code debugging written back when I was still in elementary school, and also dig into Heroku builds with a special binary in search of the tools I needed. All this work led my IRC friends to start calling me a wizard (magic). I hope this post will serve as reliable documentation for someone to accomplish the same, but with newer kernels and OS versions.
Settings
Setting up Linux User Mode is done in several stages:
- installing dependencies on the host;
- downloading the Linux kernel;
- configuring the kernel build;
- building the kernel;
- installing the binary;
- configuring the guest file system;
- choosing kernel boot parameters;
- setting up the guest network;
- starting the guest kernel.
I assume that if you decide to do this yourself, you will likely be working in some Ubuntu or Debian-like system. I tried to implement all of the above in my favorite distribution — Alpine, but nothing worked, apparently due to the fact that the Linux kernel has a strict binding to glibc-isms for User Mode drivers. I plan to report this upstream after I fully understand the issue.
Installing dependencies on the host
Ubuntu requires at least the following packages to build the Linux kernel (assuming a clean installation):
- 'build-essential'
- 'flex'
- 'bison'
- 'xz-utils'
- 'wget'
- 'ca-certificates'
- 'bc'
- 'linux-headers'
You can install them using the following command (with root privileges or using sudo):
apt-get -y install build-essential flex bison xz-utils wget ca-certificates bc
linux-headers-$(uname -r) Note that running the configuration menu program for the Linux kernel will require the installation of libncurses-dev. Please make sure it is installed using the following command (with root privileges or using sudo):
apt-get -y install libncurses-devDownloading the kernel
Determine a location for downloading and subsequently building the kernel. For this operation, you will need to allocate about 1.3 GB of space on your hard drive, so make sure you have it.
Then go to and get the URL to download the latest stable version of the kernel. As of the writing of this post, it is:
Download this file using 'wget':
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.1.16.tar.xz And extract it using 'tar':
tar xJf linux-5.1.16.tar.xzNow navigate to the directory created by extracting the tarball:
cd linux-5.1.16Configuring the kernel build
The kernel build system is a set of with along with various custom tools and scripts to automate the process. To begin, open the interactive configuration program:
make ARCH=um menuconfig It will partially lead the build and display a dialog box. When '[Select]' appears at the bottom of the window, you will be able to configure it using the Space or Enter keys. Navigation within the window is done using the up and down arrow keys, and selecting items is done with the left or right arrows.
A pointer like —> indicates that you are in a submenu, which can be entered by pressing Enter. Exiting it is obviously through '[Exit]‘.
Enable the following options in '[Select]' and ensure that they have a symbol '[*]' next to them:
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 From this window, you can exit by sequentially choosing ‘[Exit]‘. Just make sure that it prompts you to save the configuration at the end and you choose ‘[Yes]‘.
I recommend you experiment with the kernel build parameters after reading this post. Through these experiments, you can gain a lot of insight into how low-level kernel mechanics work and the impact of various flags on its build.
Kernel Build
The Linux kernel is a large program that handles many tasks. Even with such a minimal configuration on old hardware, building it can take a significant amount of time. Therefore, build the kernel using the following command:
make ARCH=um -j$(nproc) Why? This command tells our builder to use all available cores and threads of the CPU during the build process. The command $(nproc) at the end of the build inserts the output of the command nproc, which is part of coreutils in the standard Ubuntu build.
After some time, our kernel will be compiled into an executable file ./linux.
Installing the Binary
Since User Mode in Linux creates a regular binary, you can install it just like any other utility. Here’s how I did it:
mkdir -p ~/bin
cp linux ~/bin/linux You should also ensure that ~/bin is in your $PATH:
export PATH=$PATH:$HOME/binSetting Up the Guest Filesystem
Create a directory for the guest filesystem:
mkdir -p $HOME/prefix/uml-demo
cd $HOME/prefix
Open alpinelinux.org and in the find the current download link for MINI ROOT FILESYSTEM. At the time of writing this publication, it was:
http://dl-cdn.alpinelinux.org/alpine/v3.10/releases/x86_64/alpine-minirootfs-3.10.0-x86_64.tar.gzDownload this tarball using 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.gzNow enter the guest filesystem directory and unpack the archive:
cd uml-demo
tar xf ../alpine-rootfs.tgzThe described actions will create a small template of the filesystem. Due to the peculiarities of the system, installing packages via the Alpine apk manager will be extremely difficult. But this FS will be enough to gauge the general idea.
We will also need the tool to prevent memory consumption in our guest kernel.
wget -O tini https://github.com/krallin/tini/releases/download/v0.18.0/tini-static
chmod +x tiniCreating the Kernel Command Line
In the Linux kernel, as in most other programs, there are command line arguments that can be reviewed by specifying the key --help.
self —help
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=,
Configure as an IO memory region named .
mem=
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=
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=
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]*=
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]+=,
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=
The location to place the pid and umid files.
quiet
Turns off information messages during boot.
hostfs=,,...
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.This banner highlights the main launch parameters. Let's run the kernel with the minimum required options:
linux
root=/dev/root
rootfstype=hostfs
rootflags=$HOME/prefix/uml-demo
rw
mem=64M
init=/bin/shThe lines above tell our kernel the following:
- Assume that the root filesystem is a pseudo-device
/dev/root. - Select as the driver for the root filesystem.
- Mount the guest filesystem we created in the root device.
- And yes, in read-write mode.
- Use only 64 megabytes of RAM (you can use much less depending on what you plan to do, but 64 MB seems to be an optimal amount).
- The kernel automatically starts
/bin/shasinit-process.
Run this command, and you should get something like the following:
Another sheet
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
/ # The manipulations above will give us a minimal guest system, without things like /proc or an assigned hostname. For example, try executing the following commands:
- uname -av
- cat /proc/self/pid
- hostname
To exit the guest system, enter sigreturn or press control-d. This will terminate the shell with a subsequent kernel panic:
/ # exit
Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000000
fish: “./linux root=/dev/root rootflag…” terminated by signal SIGABRT (Abort) We encountered this kernel panic because the Linux kernel considers that the initialization process is always running. Without it, the system can no longer function and shuts down. However, since this is a user-mode process, the result sends itself to SIGABRT, which leads to an exit.
Setting up the guest network
Now things start to go off track. The network in User Mode Linux is where the whole concept of limited 'user mode' begins to fall apart. Typically, at the system level, the network is restricted to privileged execution modes for reasons we all understand.
Note: more about different ways to work with networking in UML can be read .
A journey into slirp
However, there exists an ancient and practically unsupported tool called , which allows User Mode Linux to interact with the network. It works somewhat like a TCP/IP stack at the user level and doesn't require any system permissions to run. This tool was released in 1995, and the last update dates back to 2006.Slirp is very old. During its time without support and updates, compilers have evolved so much that this tool can only be described as .
So, let's install Slirp from the Ubuntu repositories and try to run it:
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)Oh goodness. Let's set up a debugger for Slirp and see if we can figure out what's going on:
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.An error occurred in . Let's take a look at the stack trace; maybe it can help us:
(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.Here we see that the crash occurs during the main loop execution when Slirp tries to check the timeouts. At that moment, I should have given up on debugging. But let's see if Slirp built from source works. I re-downloaded the archive directly from the , because pulling something from there via the command line is a hassle:
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 Here we see alerts about undefined inline functions, meaning it's impossible to link the resulting binary file. Apparently, between 2006 and now, gcc stopped creating symbols used in the inline functions of intermediate compiled files. Let's try replacing the keyword inline with an empty comment and see what happens:
vi slirp.h
:6
a
#define inline /**/
:wq
makeNope. This still doesn't work. Still unable to find the symbols for these functions.
At this point, I gave up and started looking on Github . My theory was based on the idea that some Heroku buildpack would contain the binaries I needed. Ultimately, my searches led me . I downloaded and unpacked uml.tar.gz and found the following:
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*This is the binary file slirp! But does it work?
./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 ...
It's not crashing — so it should work! Let's place this binary in ~/bin/slirp:
cp slirp ~/bin/slirpIn case the package creator removes it, I .
Network setup
Now let's set up the network on our guest kernel. :
linux
root=/dev/root
rootfstype=hostfs
rootflags=$HOME/prefix/uml-demo
rw
mem=64M
eth0=slirp,$HOME/bin/slirp
init=/bin/shNow let's enable the network:
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 The first two setup commands /proc and /sys are required for operation ifconfig, which sets up the network interface for communication with Slirp. The command route sets the kernel's routing table to force all traffic through the Slirp tunnel. Let's check this with a DNS query:
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.netIt works!
Note: Apparently, the original post was written on a desktop with a wired network card, or some other configuration that does not require additional drivers. However, on a laptop with an Intel WiFi 8265, there is an error when bringing up the network.
/ # 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
/ #It seems that the kernel cannot connect to the network driver. Unfortunately, attempting to compile the firmware into the kernel did not resolve the issue. As of the time of publication, a solution in this configuration has not yet been found. In simpler configurations (for example, in VirtualBox), the interface comes up correctly.
Let's automate the redirection using the following shell script:
#!/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/shAnd let's make it executable:
chmod +x init.shAnd then we’ll make changes to the kernel command line:
linux
root=/dev/root
rootfstype=hostfs
rootflags=$HOME/prefix/uml-demo
rw
mem=64M
eth0=slirp,, $HOME/bin/slirp
init=/init.sh
And repeat:
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.netThe network is working stably!
Docker file
To make it easier for you to check everything, I have assembled , which automates most of the described steps and should provide you with a working configuration. I also have a , which includes everything described in the post. But it's important to understand that here I have outlined only the minimal setup.
I hope this post has helped you understand how to bring up a guest kernel. It turned out to be quite a monster, but this publication was intended as a comprehensive guide on building, installing, and configuring User Mode in Linux for modern versions of operating systems in this family. Subsequent actions should include installing services and other software within the guest system. Since Docker container images are just unpacked tar archives, you should be able to extract the image via docker export, and then determine the installation path in the root file system of the guest kernel. Then execute the shell script.
Special thanks to Rkeene from #lobsters on Freenode. Without his help in debugging Slirp, I wouldn't have come this far. I have no idea how his Slackware system works correctly with slirp, but my Ubuntu and Alpine systems did not accept slirp and the binary provided to me by Rkeene. But I’m happy with the fact that something is working.
Source: habr.com
