unshare(1) is part of util-linux that is pre-installed on literally every linux distros and can be utilized to provide "installation-free" lightweight containers. This article includes some notes and code snippets about this usage.
This software and the namespace functionalities in the kernel are still under rapid development. Linux kernel 4.17+ and util-linux 2.39+ are required to run the snippets.
mkdir arch && cd arch
pacstrap -KNP . pacman archlinux-keyring
rm var/cache/pacman/pkg/* # optional, save space
The
-N
option causespacstrap
to run inside a user namespace. If it reports errors likeunshare: no line matching user "xxx" in /etc/subuid
, run the following commands in the host system as root:echo "$USER:65536:65536" >> /etc/subuid echo "$USER:65536:65536" >> /etc/subgid
The
$USER
variable should be manually replaced with the user that are intended to be used to create the container because root privilege is required to edit these files and$USER
will be subsequently becomeroot
.
Next, use the following command to edit pacman.conf
to comment out the "CheckSpace" option, or pacman
will complain that /
is not mounted.
awk -i inplace '/^CheckSpace/{print "#"$0; next}{print}' etc/pacman.conf
Then add a simple script start.sh
for starting the container:
> start.sh <<"EOF"
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
unshare -rm --map-auto --propagation slave bash -c "\
mount -R /proc $SCRIPT_DIR/proc;\
mount -R /dev $SCRIPT_DIR/dev;\
mount --bind /etc/resolv.conf $SCRIPT_DIR/etc/resolv.conf;\
mount -o nodev,nosuid,size=16G -t tmpfs tmpfs $SCRIPT_DIR/tmp;\
mount -o nodev,nosuid,size=16G -t tmpfs tmpfs $SCRIPT_DIR/run;\
export HOME=/root;\
export SHELL=/usr/bin/bash;\
export PATH=/usr/bin;\
chroot $SCRIPT_DIR $*"
EOF
chmod +x start.sh
The first command actually starts with
>
. Some may add a uselesscat
in front of it to feel better. The double quotes aroundEOF
prevents shell from expanding the variables before writing to the file.
Now we can enter the container with by running start.sh
.
Inside container:
pacman -S openssh
ssh-keygen -A
awk -i inplace '/^#Port 22/{print "Port 3922"; next}{print}' /etc/ssh/sshd_config
Inside host system in the container folder:
mkdir root/.ssh
cp ~/.ssh/id_rsa.pub root/.ssh/authorized_keys
./start.sh /usr/bin/sshd
The
--map-auto
option ofunshare
instart.sh
is important becausesshd
usessetgid
and will fail due to insufficient privilege if the user namespace is not mapped.
Now the container can be accessed by:
ssh root@localhost -p 3922
Run in the host system inside the container folder. The image file is saved adjacent to the container folder.
export XZ_OPT="-e -T 0"
tar -cJvf ../image.tar.xz .
sha256sum ../image.tar.xz
mkdir container
tar xf image.tar.xz -C container
The latest download link can be found at https://alpinelinux.org/downloads.
mkdir alpine && cd alpine
wget 'https://dl-cdn.alpinelinux.org/alpine/v3.18/releases/x86_64/alpine-minirootfs-3.18.0-x86_64.tar.gz'
tar xf *.tar.gz && rm *.tar.gz
> start.sh <<"EOF"
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
unshare -rm --map-auto --propagation slave bash -c "\
mount -R /proc $SCRIPT_DIR/proc;\
mount -R /dev $SCRIPT_DIR/dev;\
mount --bind /etc/resolv.conf $SCRIPT_DIR/etc/resolv.conf;\
mount -o nodev,nosuid,size=16G -t tmpfs tmpfs $SCRIPT_DIR/tmp;\
mount -o nodev,nosuid,size=16G -t tmpfs tmpfs $SCRIPT_DIR/run;\
export HOME=/root;\
export SHELL=/bin/sh;\
export PATH=/usr/bin:/usr/sbin:/bin:/sbin;\
chroot $SCRIPT_DIR $*"
EOF
chmod +x start.sh
touch etc/resolv.conf
wget 'https://r2.ylxdzsw.com/arch.230601.tar.xz' # 116M sha256sum 24ef32def517576a5f22b8f2fdc4577450fa31ab120448820ff0b636bdc6d6e4
wget 'https://r2.ylxdzsw.com/alpine.230601.tar.xz' # 2.6M sha256sum 39f31ede4d550c388c24b2eacb37d55f862d65b9ec106b480b6545ba1a841c0e
wget 'https://r2.ylxdzsw.com/pluto.230601.tar.xz' # 132M sha256sum 6183b4b5fa71cc29f7269e5e700ebeec5dbd427003217ead2723d55f833c2e5d