Mounts and Chroot¶
The simplest container is the chroot container. Combined with adjusting the mounts, quite a bit can be done with it. This is used as part of most container ecosystems. We will start with doing it with root for simplicity. In the next lesson, you will learn about namespaces and how to do it rootless.
Warning
Be very careful when doing operations with root, whether directory or via sudo, so as to not break you system.
A careless bind mount can cause serious problems.
Objectives
Basic understanding of the Unix/Linux Virtual Filesystem (VFS)
Learn how to do bind and tmpfs mounts
Learn how to do simple rootful containers with
chrootandpivot_root
Instructor note
XX min teaching
XX min exercises
Unix/Linux Virtual Filesystem¶
Unix-like OSes including Linux use a hierarchical virtual filesystem to which other filesystems, directories, files, etc. are mounted to to make a single tree.
Unlike DOS/Windows, this is a single unified virtual filesystem tree.
There are no “drives” like C:\.
Each location in the tree translates to a location on some device, interface, pseudo-filesystem, etc.
What can be mounted:
Disk partitions
Remote filesystems
Virtual devices like kernel interfaces (e.g.
proc)RAM disks/filesystems (e.g.
tmpfsanddevtmpfs)Bind Mounts
Mount file/directory in VFS tree to another location in the VFS tree
Can even change mount options (e.g. mount it read-only)
A common Linux layout is
/: Root of the VFS (usually a disk or atmpfs)/boot: Mount location of boot partition (sometimes)/dev:devtmpfsfilesystem for device files/dev/shm:tmpfsfor shared memory files/etc: Directory for system-wide configuration/home: User home directories (sometimes another partition/filesystem)/media/USER/MOUNTID: Common mount location of removable media/mnt/MOUNT: Common place to mount extra disks, remote filesystems, etc./proc:procfilesystem from kernel for info/usr: Location for most software, configuration, data, etc. (sometimes another partition/filesystem)/sys:sysfsfilesystem from kernel for info and control
Seeing The Current Mounts¶
There are several ways to see the current mounts in the VFS with different things that are easy to see and others that are hard.
More Readable List: findmnt¶
While /proc/PID/mountinfo has all the information, it is a bit unwieldy for a person to use and reason about.
It is more suited for consumption by programs than for a person.
Luckily, there is a convenient tool, findmnt, that renders most of the information in a fashion that is easier to understand.
Type-Along
To see the information in a more human readable format, run
findmnt
What you will get will depend on the linux system you are on and any mounts you have setup. An example could be
[foo@bar ~]$ findmnt
TARGET SOURCE FSTYPE OPTIONS
/ /dev/vda1 ext4 rw,relatime,seclabel
├─/proc proc proc rw,nosuid,nodev,noexec,relatime
│ └─/proc/sys/fs/binfmt_misc systemd-1 autofs rw,relatime,fd=29,pgrp=1,timeout=0,minproto=5,maxproto=5
│ └─/proc/sys/fs/binfmt_misc binfmt_misc binfmt_mis rw,nosuid,nodev,noexec,relatime
├─/sys sysfs sysfs rw,nosuid,nodev,noexec,relatime,seclabel
│ ├─/sys/kernel/security securityfs securityfs rw,nosuid,nodev,noexec,relatime
│ ├─/sys/fs/cgroup cgroup2 cgroup2 rw,nosuid,nodev,noexec,relatime,seclabel,nsdelegate,memo
│ ├─/sys/fs/pstore pstore pstore rw,nosuid,nodev,noexec,relatime,seclabel
│ ├─/sys/fs/bpf bpf bpf rw,nosuid,nodev,noexec,relatime,mode=700
│ ├─/sys/fs/selinux selinuxfs selinuxfs rw,nosuid,noexec,relatime
│ ├─/sys/kernel/debug debugfs debugfs rw,nosuid,nodev,noexec,relatime,seclabel
│ ├─/sys/kernel/tracing tracefs tracefs rw,nosuid,nodev,noexec,relatime,seclabel
│ ├─/sys/kernel/config configfs configfs rw,nosuid,nodev,noexec,relatime
│ └─/sys/fs/fuse/connections fusectl fusectl rw,nosuid,nodev,noexec,relatime
├─/dev devtmpfs devtmpfs rw,nosuid,seclabel,size=4096k,nr_inodes=2008483,mode=755
│ ├─/dev/shm tmpfs tmpfs rw,nosuid,nodev,seclabel,inode64
│ ├─/dev/pts devpts devpts rw,nosuid,noexec,relatime,seclabel,gid=5,mode=620,ptmxmo
│ ├─/dev/mqueue mqueue mqueue rw,nosuid,nodev,noexec,relatime,seclabel
│ └─/dev/hugepages hugetlbfs hugetlbfs rw,relatime,seclabel,pagesize=2M
├─/run tmpfs tmpfs rw,nosuid,nodev,seclabel,size=3221940k,nr_inodes=819200,
│ ├─/run/credentials/systemd-sysctl.service
│ │ none ramfs ro,nosuid,nodev,noexec,relatime,seclabel,mode=700
│ ├─/run/credentials/systemd-tmpfiles-setup-dev.service
│ │ none ramfs ro,nosuid,nodev,noexec,relatime,seclabel,mode=700
│ ├─/run/credentials/systemd-tmpfiles-setup.service
│ │ none ramfs ro,nosuid,nodev,noexec,relatime,seclabel,mode=700
│ └─/run/user/1002 tmpfs tmpfs rw,nosuid,nodev,relatime,seclabel,size=1610968k,nr_inode
└─/var/lib/nfs/rpc_pipefs sunrpc rpc_pipefs rw,relatime
findmnt arranges the mounts into a tree and aligns some of the columns you saw before to make them more readalbe.
Exercise
In the example output above, onto which mountpoint is /dev/shm mounted to?
Solution
It is mounted onto the filesystem that is mounted at /dev as seen by how findmnt draws a branch out from /dev to /dev/shm.
A Shorter More Minimal List: /proc/PID/mounts¶
Every process also has a more minimal list that has all the mounts but in a more simplified format.
It is easier to read, but is incomplete.
The incompleteness is most glaring for bind mounts, but it is still useful for quick checks with other mounts, particularly to quickly read the mount options.
The more minimal list for a process is at /proc/PID/mounts, which can also be accessed by the convenient short cut path /proc/mounts.
Type-Along
To see the more minimal information, run
cat /proc/self/mountinfo
What you will get will depend on the linux system you are on and any mounts you have setup. An example could be
[foo@bar ~]$ cat /proc/mounts
proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
sysfs /sys sysfs rw,seclabel,nosuid,nodev,noexec,relatime 0 0
devtmpfs /dev devtmpfs rw,seclabel,nosuid,size=4096k,nr_inodes=2008483,mode=755,inode64 0 0
securityfs /sys/kernel/security securityfs rw,nosuid,nodev,noexec,relatime 0 0
tmpfs /dev/shm tmpfs rw,seclabel,nosuid,nodev,inode64 0 0
devpts /dev/pts devpts rw,seclabel,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0
tmpfs /run tmpfs rw,seclabel,nosuid,nodev,size=3221940k,nr_inodes=819200,mode=755,inode64 0 0
cgroup2 /sys/fs/cgroup cgroup2 rw,seclabel,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot 0 0
pstore /sys/fs/pstore pstore rw,seclabel,nosuid,nodev,noexec,relatime 0 0
bpf /sys/fs/bpf bpf rw,nosuid,nodev,noexec,relatime,mode=700 0 0
/dev/vda1 / ext4 rw,seclabel,relatime 0 0
selinuxfs /sys/fs/selinux selinuxfs rw,nosuid,noexec,relatime 0 0
systemd-1 /proc/sys/fs/binfmt_misc autofs rw,relatime,fd=29,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=1207 0 0
mqueue /dev/mqueue mqueue rw,seclabel,nosuid,nodev,noexec,relatime 0 0
hugetlbfs /dev/hugepages hugetlbfs rw,seclabel,relatime,pagesize=2M 0 0
debugfs /sys/kernel/debug debugfs rw,seclabel,nosuid,nodev,noexec,relatime 0 0
tracefs /sys/kernel/tracing tracefs rw,seclabel,nosuid,nodev,noexec,relatime 0 0
none /run/credentials/systemd-sysctl.service ramfs ro,seclabel,nosuid,nodev,noexec,relatime,mode=700 0 0
configfs /sys/kernel/config configfs rw,nosuid,nodev,noexec,relatime 0 0
none /run/credentials/systemd-tmpfiles-setup-dev.service ramfs ro,seclabel,nosuid,nodev,noexec,relatime,mode=700 0 0
fusectl /sys/fs/fuse/connections fusectl rw,nosuid,nodev,noexec,relatime 0 0
none /run/credentials/systemd-tmpfiles-setup.service ramfs ro,seclabel,nosuid,nodev,noexec,relatime,mode=700 0 0
sunrpc /var/lib/nfs/rpc_pipefs rpc_pipefs rw,relatime 0 0
binfmt_misc /proc/sys/fs/binfmt_misc binfmt_misc rw,nosuid,nodev,noexec,relatime 0 0
tmpfs /run/user/1002 tmpfs rw,seclabel,nosuid,nodev,relatime,size=1610968k,nr_inodes=402742,mode=700,uid=1002,gid=1002,inode64 0 0
Now the columns are what is mounted (disk, special filesystem, etc.), the mount point, the filesystem, the mount options, the dump option, and the filesystem check order. Note that bind mounts show up exactly like the disk, special filesystem, etc. they are mounted from but instead have a different mount point; which is why bind mounts are rather incomplete in this format.
Seeing the Utilization of Each Mountpoint: df¶
The program df is meant to see the capacity and utilization of the mounted filesystems that have capacities (not all do).
While not intended for this purpose, it can be used as a quick way to see a subset of the filesystems you have mounted and where they are mounted.
Type-Along
Run the following to see the filesystems that have capacities and how much is used.
df
What you will get will depend on the linux system you are on and any mounts you have setup. An example could be
[foo@bar ~]$ df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 4,0M 0 4,0M 0% /dev
tmpfs 7,7G 84K 7,7G 1% /dev/shm
tmpfs 3,1G 17M 3,1G 1% /run
/dev/vda1 158G 2,4G 149G 2% /
tmpfs 1,6G 0 1,6G 0% /run/user/1002
Notice how few of the mounts are shown. Most of the mounts don’t have a capacity per-se.
tmpfs Mounts¶
tmpfs filesystems are special RAM disks whose free space does not actually consume RAM and which can go to swap.
They have almost entirely replaced traditional ramdisks that were locked in RAM and consumed the same amount whether empty or full.
To mount a tmpfs filesystem, you would run
mount -t tmpfs [OPTIONS] tmpfs TARGET
where TARGET is where you want to mount it to (must be a directory that exists)
You will notice that the “device” for a tmpfs is always tmpfs.
In addition to the standard mount options with -o, there are additional ones to control how large the tmpfs can be (see man tmpfs for all extra options).
They are unmounted just like any other mountpoint with umount TARGET.
Since you aren’t in suitably prepared namespaces (see next lesson), you will have to do the mount and umount commands as root.
Warning
When a tmpfs is no longer mounted anywhere, all its contents are lost forever.
So, backup anything you want to keep in them somewhere else.
Also, never assume the space in a tmpfs is reclaimed securely.
It is just freed, not overwritten first.
Exercise
Do the following:
Create a directory
Create a file in that directory
Mount a
tmpfsto the directory that has a capacity of 4 GiB.Look at the mount with
findmnt.Create a file in the mount
Run
lsto look at the files in the mountUnmount it
Run
lsto look at the files in the directory again
Solution
If you read man tmpf, you will see that the option to control its size is size=SIZE.
So, it would be mounted like
> mkdir foo
> touch foo/bar
> sudo mount -t tmpfs -o size=4g tmpfs foo
> findmnt
...
│ └─/home/foo/foo tmpfs tmpfs rw,relatime,seclabel,size=4194304k,inode64
> touch foo/baz
> ls foo
baz
> sudo umount foo
> ls foo
bar
Notice how the tmpfs hid the file in the directory while it was mounted.
Bind Mounts¶
Bind mounts let you attach one part of the VFS tree to another location.
It then exists at both, so if you edit under one part it will show up on the the other too.
This can mean that more than one path can refer to the same file or directory just like symlinks and hardlinks.
With bind mounts, you bind a file or directory to one in another part of the tree.
Note that the target must exist and must be of the same kind (file or directory) as the source.
Since you aren’t in suitably prepared namespaces (see next lesson), you will have to do the mount and umount commands as root.
Bind mounts are created using mount in the following way:
mount --bind [OPTIONS] SOURCE TARGET
where SOURCE and TARGET are the paths to the source object and the target where you want to bind it to.
A common option to add is -o ro to make the binding be read-only at the target even if it is read-write at the source.
You can then unmount the bind mount just like you would any other mountpoint with umount TARGET
Exercise
Do the following:
Create two directories
Create a file with a different name in each directory
Bind mount the first directory to the second read-only
Run
lsto look at the files in each directoryTry to add text to the file you see in the second directory
Edit the file you see in the first directory and save some content
Check the content of the file you see in the second directory
Unmount the bind mount
Run
lsto look at the files in both directories againCheck the content of the file you see in the first directory
Check the content of the file you see in the second directory
Solution
Doing these steps would look like
> mkdir foo1 foo2
> touch foo1/bar1 foo2/bar2
> ls foo1 foo2
foo1:
bar1
foo2:
bar2
> sudo mount --bind -o ro foo1 foo2
> ls foo1 foo2
foo1:
bar1
foo2:
bar1
> echo this > foo2/bar1
bash: foo2/bar1: Read-only file system
> echo that > foo1/bar1
> cat foo2/bar1
that
> sudo umount foo2
> ls foo1 foo2
foo1:
bar1
foo2:
bar2
> cat foo1/bar1
that
> cat foo2/bar2
>
Notice how the bind mount masked the contents under the target directory. The same masking happens if you bind mount files as well.
Change Root¶
The chroot command (and syscall) is used to change where in the VFS / is for a process and its children.
Its syntax is
chroot [OPTIONS] NEWROOT [COMMAND [ARG ...]]
and runs the specified COMMAND and arguments with the new /.
If you don’t specify COMMAND, it will be the username’s default shell.
If you do this on the terminal, the command runs in that environment until it terminates and then you are back at the terminal where you previously were.
Since you aren’t in suitably prepared namespaces (see next lesson), you will have to do the chroot command as root.
Make a Fresh Container Image¶
You will be using the image you made in the previous lesson in Section Making A Simple Container Image and Image. It is time to make a fresh container image from it.
Type-Along
Make a directory owned by root, mount a small tmpfs to it, and unpack the tarball image into it.
~/containers> sudo mkdir con
~/containers> sudo mount -t tmpfs -o size=1g tmpfs con
~/containers> sudo tar -C con -xvzf img.tgz
./
./bin
./dev/
./dev/null
./etc/
./proc/
./root/
./usr/
./usr/bin/
./usr/bin/ash
./usr/bin/busybox
./usr/bin/cd
./usr/bin/cp
./usr/bin/ln
./usr/bin/ls
./usr/bin/mkdir
./usr/bin/mount
./usr/bin/mv
./usr/bin/ps
./usr/bin/rm
./usr/bin/sh
./usr/bin/touch
./usr/bin/umount
Now check that it unpacked OK.
~/containers> ls -lh con/usr/bin
total 1.5M
lrwxrwxrwx. 1 root root 7 Mar 31 16:34 ash -> busybox
-rwxr-xr-x. 1 root root 1.4M Mar 31 16:33 busybox
lrwxrwxrwx. 1 root root 7 Mar 31 16:34 cd -> busybox
lrwxrwxrwx. 1 root root 7 Mar 31 16:36 cp -> busybox
lrwxrwxrwx. 1 root root 7 Mar 31 16:34 ln -> busybox
lrwxrwxrwx. 1 root root 7 Mar 31 16:34 ls -> busybox
lrwxrwxrwx. 1 root root 7 Mar 31 16:34 mkdir -> busybox
lrwxrwxrwx. 1 root root 7 Mar 31 16:34 mount -> busybox
lrwxrwxrwx. 1 root root 7 Mar 31 16:36 mv -> busybox
lrwxrwxrwx. 1 root root 7 Mar 31 16:36 ps -> busybox
lrwxrwxrwx. 1 root root 7 Mar 31 16:34 rm -> busybox
lrwxrwxrwx. 1 root root 7 Mar 31 16:34 sh -> busybox
lrwxrwxrwx. 1 root root 7 Mar 31 16:34 touch -> busybox
lrwxrwxrwx. 1 root root 7 Mar 31 16:35 umount -> busybox
You now have a fresh container image in its own tmpfs that can be cleaned up merely by unmounting the tmpfs.
chroot Time¶
With the container image prepared, you can chroot into it to have a running container.
Type-Along
Chroot into it using ash as your shell and see what files you see.
~/containers> sudo chroot con /usr/bin/ash
/ # ls -lh
total 0
lrwxrwxrwx 1 0 0 7 Mar 31 14:32 bin -> usr/bin
drwxr-xr-x 2 0 0 60 Mar 31 14:33 dev
drwxr-xr-x 2 0 0 40 Mar 31 14:32 etc
drwxr-xr-x 2 0 0 40 Mar 31 14:32 proc
drwxr-xr-x 2 0 0 60 Mar 31 15:25 root
drwxr-xr-x 3 0 0 60 Mar 31 14:32 usr
Exercise
Try some of the shell commands you made available to yourself for the next few minutes.
If you realize you are missing something useful, you can just run it with busybox COMMAND or make a new symlink with ln -s busybox /usr/bin/COMMAND.
You can exit the chroot with Ctrl+D, and re-enter if you want.
Note that because this tmpfs you unpacked the image into, any changes you did in the exercise do not change the actual image.
If you wanted to persist your changes, you would need to make a new image from it.
Congratulation, you have made and run your first container made from scratch!