User Mode Linux (UML)

UML (User Mode Linux) is a nice way for Linux virtualization. Somewhere between containers and full or paravirtualization. But without actually using a visualizer / hypervisor.

Unfortunately UML not so well documented. So I dumped some of my notes here.

ATTENTION: These are just my notes!

I didn’t spend time to check if they can be easily used or MAY EVEN BE DANGEROUS OR HARMFUL TO YOUR COMPUTER!

 

POSSIBLE SECURITY PROBLEM:
https://web.archive.org/web/20210222080616/https://www.securityfocus.com/bid/3973/discuss
UML does not correctly protect kernel address space from user
programs within the UML environment. It may be possible to execute
arbitrary code within the kernel and gain root access. Additionally,
it may be possible to use this vulnerability to escape the UML
environment, leading to local access on the hosting system.
boot message:
This architecture does not have kernel memory protection.

 

Debian 12
ROOT:
  # Alternatively to using root you may have a look at this:
  #   https://libguestfs.org/virt-builder.1.html
  #   https://linux.die.net/man/1/guestfish
  #   https://wiki.debian.org/FakeRoot
  #   https://wiki.debian.org/fakechroot
  apt install user-mode-linux debootstrap slirp
  truncate --size=1G disk.ext4
  mkfs.ext4 disk.ext4
  mount -o loop disk.ext4 /mnt/
  debootstrap --include=user-mode-linux bookworm /mnt/
  umount /mnt/
  chown USER: disk.img
USER SHELL 1:
  # MAYBE ADDITIONAL (HOST:ubd0 -> GUEST:ubda): root=/dev/ubda
  linux.uml mem=1G ubd0=disk.ext4 init=/bin/ash# WITH INITRD:
  #   init= is interpreted by the init script, which usually loads /dev/ first!
  #   rdinit= directly sets another init script in the kernel. (rdinit = ram-disk-init)
  # WITHOUT INITRD:
  #   init= sets the init script directly in the kernel.
  linux.uml mem=1G ubd0=disk.ext4 rdinit=/bin/ash
  # /dev/ populating (root=98:0 console=tty0 -> else this gets automatically attached at the end)
  linux.uml mem=1G ubd0=disk.ext4 root=98:0 console=tty0 rdinit=/bin/ash -- -c \
    '"mount /run; mkdir -p /run/lock; mount /proc; mount /sys; /lib/debian-installer/start-udev; exec ash"'
  # With open shell by systemd
  linux.uml mem=1G ubd0=disk.ext4 rw con9=fd:0,fd:1 con=null console=tty9 systemd.debug-shell=1
  # Alternatively if no login password is set:
  linux.uml mem=1G ubd0=disk.ext4 rw con2=fd:0,fd:1 con=null console=tty2
USER SHELL 2:
  ss -xlnp | grep linux
  uml_mconsole ~/.uml/*/mconsole
    config ubd1=/path/to/disk2.raw

RUNNING UML AS ROOT WITH HOSTFS:
  linux.uml mem=1G rootfstype=hostfs hostfs=/mnt/ ro con9=fd:0,fd:1 con=null console=tty9 systemd.debug-shell=1
# Replacing "ro" with "rw" may be DANGEROUS!!!
# IT MAY CHANGE YOUR HOSTS FILESYSTEM IN UNWANTED WAYS!!!

MINIMAL SYSTEM:
  wget https://deb.debian.org/debian/dists/bullseye/main/installer-amd64/current/images/hd-media/initrd.gz
  linux.uml mem=1G initrd=initrd.gz init=/bin/sh hostfs=/
  mount -t hostfs hostfs /mnt
  mount --bind /mnt/usr/lib/uml/modules/ /lib/modules/
  halt -f

MINIMAL WITH READONLY HOSTFS (Debian-12)
  #### Minimal without network (hostfs=/ is default):
  # linux.uml mem=1G rootfstype=hostfs ro init=/bin/bash
  #### With network via "slirp":
  linux.uml mem=1G rootfstype=hostfs ro init=/bin/bash \
    eth0='slirp,,slirp,host_addr_10.0.2.2,redir_50022_10.0.2.15:22'
  # mount overlay
  mount -t tmpfs -o size=256M tmpfs /mnt
  mkdir /mnt/up /mnt/work /mnt/merged
  insmod /usr/lib/uml/modules/"$(uname -r)"/kernel/fs/overlayfs/overlay.ko
  mount -t overlay -o lowerdir=/,upperdir=/mnt/up,workdir=/mnt/work overlay /mnt/merged
  mount -t devtmpfs devtmpfs /mnt/merged/dev
  mount -t proc proc /mnt/merged/proc
  mount -t sysfs sysfs /mnt/merged/sys
  chroot /mnt/merged /bin/bash
  ip link set eth0 up
  ip -4 addr add 10.0.2.15/24 dev eth0
  ip -4 r add default via 10.0.2.2
  echo 'nameserver 10.0.2.3' >> /etc/resolv.conf



COMPILING A USER MODE LINUX KERNEL:
  # Download and extract kernel sources from https://kernel.org/
  make defconfig ARCH=um
  make menuconfig ARCH=um
  # interesting options (Linux-6.1)
    CONFIG_BLK_DEV_INITRD=y
    CONFIG_VIRTIO_UML=y
    CONFIG_PM_AUTOSLEEP=y
    CONFIG_BLK_WBT=y
    CONFIG_PARTITION_ADVANCED=y
    CONFIG_CMDLINE_PARTITION=y
    CONFIG_BLK_MQ_PCI=y
    CONFIG_BLK_MQ_VIRTIO=y
    CONFIG_BLK_DEV_LOOP=y
    CONFIG_VIRTIO=y
    CONFIG_EXT4_FS_POSIX_ACL=y
    CONFIG_EXT4_FS_SECURITY=y
    CONFIG_BTRFS_FS=y
    CONFIG_BTRFS_FS_POSIX_ACL=y
    CONFIG_FS_POSIX_ACL=y
    CONFIG_FS_ENCRYPTION=y
    CONFIG_FS_ENCRYPTION_ALGS=y
    CONFIG_NETFS_SUPPORT=y
    CONFIG_ISO9660_FS=y
    CONFIG_FAT_FS=y
    CONFIG_MSDOS_FS=y
    CONFIG_VFAT_FS=y
    CONFIG_EXFAT_FS=y
    CONFIG_TMPFS_POSIX_ACL=y
    CONFIG_TMPFS_XATTR=y
    CONFIG_CRAMFS=y
    CONFIG_SQUASHFS=y
    CONFIG_SQUASHFS_XATTR=y
    CONFIG_SQUASHFS_ZLIB=y
    CONFIG_SQUASHFS_LZ4=y
    CONFIG_SQUASHFS_LZO=y
    CONFIG_SQUASHFS_XZ=y
    CONFIG_SQUASHFS_ZSTD=y
    CONFIG_NFS_FS=y
    CONFIG_NFS_V2=y
    CONFIG_NFS_V3=y
    CONFIG_NFSD=m
    CONFIG_NFSD_V2_ACL=y
    CONFIG_NFSD_V3_ACL=y
    CONFIG_NFSD_V4=y
    CONFIG_NFS_ACL_SUPPORT=m
    CONFIG_CIFS=y
    CONFIG_AFS_FS=y
  # Build
  mkdir tmp
  TMPDIR="$(pwd)"/tmp LANGUAGE='' LANG=C.UTF-8 nice ionice -c3 eatmydata \
    sh -c 'make ARCH=um linux modules modules_install INSTALL_MOD_PATH="${HOME}"/opt/user-mode-linux-uml/default'
  # Output (you may rename it to "linux.uml")
    linux

SLIRP (NAT network without root permissions on host)
COMPILING SLIRP:
  mkdir ~/opt/slirp/{bin,build,man/man1}
  cd ~/opt/slirp/build
  wget 'https://deb.debian.org/debian/pool/main/s/slirp/slirp_1.0.17.orig.tar.gz'
  tar xf slirp_1.0.17.orig.tar.gz
  wget 'https://deb.debian.org/debian/pool/main/s/slirp/slirp_1.0.17-11.debian.tar.xz'
  tar xf slirp_1.0.17-11.debian.tar.xz
  cd slirp-1.0.17
  for i in ../slirp_1.0.17-11.debian/patches/0*; do patch -p1 < "${i}"; done cd src/ ./configure --prefix="${HOME}"/opt/slirp CFLAGS='-I. -DUSE_PPP -DUSE_MS_DNS -fno-strict-aliasing -Wno-unused -std=gnu89'; \ make "${CFLAGS} -DFULL_BOLT" PPPCFLAGS="${CFLAGS}" install linux.uml mem=1G initrd=initrd.gz init=/bin/sh \ eth0=slirp,,"${HOME}"/opt/user-mode-linux-uml/slirp/bin/slirp" ip link set eth0 up ip -4 addr add 10.0.2.15/24 dev eth0 ip -4 r add default via 10.0.2.2 echo 'nameserver 10.0.2.3' >> /etc/resolv.conf
SLIRP WITH PORT-FORWARDING:
  linux.uml mem=1G initrd=initrd.gz init=/bin/sh \
    eth0='slirp,,slirp,host_addr_10.0.2.2,redir_50022_10.0.2.15:22'
  linux.uml mem=1G initrd=initrd.gz \
    eth0='slirp,,slirp,host_addr_10.0.2.2,redir_50022_10.0.2.15:22' \
    console=tty0 rdinit=/bin/ash -- -c \
    "\"mount /run; mkdir -p /run/lock; mount /proc; mount /sys; /lib/debian-installer/start-udev; ip link set eth0 up; ip -4 addr add 10.0.2.15/24 dev eth0; ip -4 r add default via 10.0.2.2; echo 'nameserver 10.0.2.3' >> /etc/resolv.conf; exec ash\""
  sh -c 'sleep 10; kill "$(pidof nc)"' & nc -l -p 22
  ##########
  mount -t hostfs -o ro hostfs /mnt
  mount --bind /dev /mnt/dev; mount --bind /proc /mnt/proc; mount --bind /sys /mnt/sys
  mount -t tmpfs -o size=32M,mode=0700 tmpfs /mnt/run
  chroot /mnt
  # Without mode=0700 sshd refuses to read AuthorizedKeysFile
  ##########
  echo 'PUBKEY' >> /run/a_pubk
  ssh-keygen -t rsa -b 4096 -f /run/ssh_rsa -P ''
  /usr/sbin/sshd -f /dev/null -h /run/ssh_rsa -o AuthorizedKeysFile=/run/a_pubk

 

See also:
https://www.kernel.org/doc/html/v6.1/virt/uml/user_mode_linux_howto_v2.html
https://www.kernel.org/doc/html/v5.9/virt/uml/user_mode_linux.html
https://en.wikipedia.org/wiki/User-mode_Linux